changeset 10832:0eb46bb0d2fc

Branch merge
author tide@sparcv490
date Fri, 05 Jun 2009 10:28:40 -0400
parents f3b3a1cde932 (current diff) 4a9784073e11 (diff)
children 67cf6bad92e2
files usr/src/Makefile.lint usr/src/Targetdirs usr/src/cmd/Makefile usr/src/cmd/Makefile.cmd usr/src/cmd/fdisk/fdisk.c usr/src/cmd/filebench/common/flowop_library.c usr/src/cmd/ntfsprogs/THIRDPARTYLICENSE.readme usr/src/cmd/parted/THIRDPARTYLICENSE.readme usr/src/lib/Makefile usr/src/lib/libntfs/THIRDPARTYLICENSE.readme usr/src/lib/libparted/THIRDPARTYLICENSE.readme usr/src/lib/libscf/common/lowlevel.c usr/src/uts/common/Makefile.files usr/src/uts/sun4v/cpu/rock_atomic.s
diffstat 432 files changed, 102427 insertions(+), 2850 deletions(-) [+]
line wrap: on
line diff
--- a/usr/src/Makefile.lint	Fri Jun 05 10:27:16 2009 -0400
+++ b/usr/src/Makefile.lint	Fri Jun 05 10:28:40 2009 -0400
@@ -373,6 +373,7 @@
 	lib/libinetcfg \
 	lib/libinetsvc \
 	lib/libinetutil \
+	lib/libinstzones \
 	lib/libipmi \
 	lib/libipmp \
 	lib/libipp \
--- a/usr/src/Targetdirs	Fri Jun 05 10:27:16 2009 -0400
+++ b/usr/src/Targetdirs	Fri Jun 05 10:28:40 2009 -0400
@@ -161,6 +161,8 @@
 	/var/log \
 	/var/log/pool \
 	/var/sadm \
+	/var/sadm/pkg \
+	/var/sadm/security \
 	/var/opt \
 	/var/run \
 	/var/smb \
@@ -317,6 +319,7 @@
 	/usr/proc/bin \
 	/usr/sadm \
 	/usr/sadm/install \
+	/usr/sadm/install/bin \
 	/usr/sadm/install/scripts \
 	/usr/sadm/sysadm \
 	/usr/sadm/sysadm/add-ons \
@@ -344,7 +347,11 @@
 	/var/news \
 	/var/preserve \
 	/var/saf \
-	/var/spool
+	/var/sadm/install \
+	/var/sadm/install/admin \
+	/var/sadm/install/logs \
+	/var/spool \
+	/var/spool/pkg
 
 sparcv9_ROOT.BIN64= \
 	/platform/sun4u/lib \
@@ -593,6 +600,7 @@
 $(ROOT)/usr/mail \
 $(ROOT)/var/mail  :=	GROUP= mail
 
+
 $(ROOT)/etc/dladm :=		OWNER= dladm
 $(ROOT)/etc/dladm :=		GROUP= sys
 
@@ -615,6 +623,15 @@
 
 
 #
+# These permissions must match those set
+# in pkgdefs.
+#
+$(ROOT)/var/sadm/pkg \
+$(ROOT)/var/sadm/security \
+$(ROOT)/var/sadm/install/logs :=	DIRMODE= 555
+
+
+#
 # These permissions must match the ones set
 # internally by fdfs and autofs.
 #
--- a/usr/src/cmd/Makefile	Fri Jun 05 10:27:16 2009 -0400
+++ b/usr/src/cmd/Makefile	Fri Jun 05 10:28:40 2009 -0400
@@ -386,6 +386,7 @@
 	sulogin		\
 	sunpc		\
 	svc		\
+	svr4pkg		\
 	swap		\
 	sync		\
 	sysdef		\
@@ -709,6 +710,7 @@
 	strings		\
 	su		\
 	svc		\
+	svr4pkg		\
 	swap		\
 	syseventadm	\
 	syseventd	\
--- a/usr/src/cmd/Makefile.cmd	Fri Jun 05 10:27:16 2009 -0400
+++ b/usr/src/cmd/Makefile.cmd	Fri Jun 05 10:28:40 2009 -0400
@@ -22,7 +22,7 @@
 # Use is subject to license terms.
 #
 #
-# Copyright 2008 Sun Microsystems, Inc.  All rights reserved.
+# Copyright 2009 Sun Microsystems, Inc.  All rights reserved.
 # Use is subject to license terms.
 #
 # Definitions common to command source.
@@ -59,6 +59,10 @@
 ROOTLIBZONES=		$(ROOT)/lib/zones
 
 ROOTSHLIB=	$(ROOT)/usr/share/lib
+ROOTPKGBIN=	$(ROOT)/usr/sadm/install/bin
+ROOTCLASS_SCR_DIR= $(ROOT)/usr/sadm/install/scripts
+ROOTADMIN_SRC_DIR= $(ROOT)/var/sadm/install/admin
+
 ROOTSHLIBCCS=	$(ROOTSHLIB)/ccs
 ROOTSBIN=	$(ROOT)/sbin
 ROOTUSRSBIN=	$(ROOT)/usr/sbin
@@ -97,8 +101,8 @@
 ROOTMAN3=	$(ROOTMAN)/man3
 ROOTVARSMB=	$(ROOT)/var/smb
 
+
 #
-
 # Like ROOTLIBDIR in $(SRC)/Makefile.lib, any lower-level Makefiles that
 # put their binaries in a non-standard location should reset this and use
 # $(ROOTCMD) in their `install' target. By default we set this to a bogus
@@ -139,6 +143,8 @@
 ROOTLIBSHFILES= $(SHFILES:%=$(ROOTLIB)/%)
 ROOTSHLIBPROG=	$(PROG:%=$(ROOTSHLIB)/%)
 ROOTSBINPROG=	$(PROG:%=$(ROOTSBIN)/%)
+ROOTPKGBINPROG= $(PROG:%=$(ROOTPKGBIN)/%)
+ROOTCLASS_SCR_FILES= $(SCRIPTS:%=$(ROOTCLASS_SCR_DIR)/%)
 ROOTUSRSBINPROG=$(PROG:%=$(ROOTUSRSBIN)/%)
 ROOTUSRSBINSCRIPT=$(SCRIPT:%=$(ROOTUSRSBIN)/%)
 ROOTETCPROG=	$(PROG:%=$(ROOTETC)/%)
@@ -202,6 +208,8 @@
 ROOTLIBZONESFILES=	$(LIBZONESFILES:%=$(ROOTLIBZONES)/%)
 $(ROOTLIBZONESFILES) :=	FILEMODE = 0555
 
+ROOTADMIN_SRC_FILE= $(ADMINFILE:%=$(ROOTADMIN_SRC_DIR)/%)
+$(ROOTADMIN_SRC_FILE) := FILEMODE = 0444
 
 #
 # Directories for smf(5) service manifests and profiles.
@@ -307,6 +315,15 @@
 $(ROOTSHLIB)/%: %
 	$(INS.file)
 
+$(ROOTPKGBIN)/%: %
+	$(INS.file)
+
+$(ROOTCLASS_SCR_DIR)/%: %
+	$(INS.file)
+
+$(ROOTADMIN_SRC_DIR)/%: %
+	$(INS.file)
+
 $(ROOTSBIN)/%: %
 	$(INS.file)
 
--- a/usr/src/cmd/cmd-inet/usr.lib/in.dhcpd/generic.c	Fri Jun 05 10:27:16 2009 -0400
+++ b/usr/src/cmd/cmd-inet/usr.lib/in.dhcpd/generic.c	Fri Jun 05 10:28:40 2009 -0400
@@ -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,10 +18,9 @@
  *
  * CDDL HEADER END
  */
-#pragma ident	"%Z%%M%	%I%	%E% SMI"
 
 /*
- * Copyright 2005 Sun Microsystems, Inc.  All rights reserved.
+ * Copyright 2009 Sun Microsystems, Inc.  All rights reserved.
  * Use is subject to license terms.
  */
 
@@ -179,7 +177,7 @@
 nm_cmp(const void *n1, const void *n2)
 {
 	void *v1 = (void *) (((NNODE *)n1)->net.s_addr &
-				((NNODE *)n2)->mask.s_addr);
+	    ((NNODE *)n2)->mask.s_addr);
 	void *v2 = (void *) ((NNODE *)n2)->net.s_addr;
 
 	return (memcmp(&v1, &v2, sizeof (struct in_addr)));
@@ -273,8 +271,8 @@
 	ushort_t	code;
 	uint_t		vend_len;
 	uchar_t		len, *vp, *vdata, *data, *endp, *main_optp, *opt_endp;
-	uchar_t		overload = DHCP_OVRLD_CLR,
-			    using_overload = DHCP_OVRLD_CLR;
+	uchar_t		overload = DHCP_OVRLD_CLR;
+	uchar_t		using_overload = DHCP_OVRLD_CLR;
 	boolean_t	srv_using_file = B_FALSE, clnt_ovrld_file = B_FALSE;
 	boolean_t	echo_clnt_file;
 
@@ -488,7 +486,9 @@
 					using_overload |= DHCP_OVRLD_FILE;
 					break;
 				}
-			} else {
+			}
+			/* Skip the option if it's too long to fit */
+			if (len < (endp - optp - 1)) {
 				/* Load options. */
 				*optp++ = (uchar_t)code;
 				*optp++ = len;
@@ -587,7 +587,7 @@
 		} else {
 			dhcpmsg(LOG_ERR,
 			    "Unrecognized option with code: %d %d\n", cat,
-				code);
+			    code);
 		}
 	}
 
--- a/usr/src/cmd/cmd-inet/usr.lib/inetd/config.c	Fri Jun 05 10:27:16 2009 -0400
+++ b/usr/src/cmd/cmd-inet/usr.lib/inetd/config.c	Fri Jun 05 10:28:40 2009 -0400
@@ -19,12 +19,10 @@
  * CDDL HEADER END
  */
 /*
- * Copyright 2008 Sun Microsystems, Inc.  All rights reserved.
+ * Copyright 2009 Sun Microsystems, Inc.  All rights reserved.
  * Use is subject to license terms.
  */
 
-#pragma ident	"%Z%%M%	%I%	%E% SMI"
-
 /*
  * Routines used by inetd to read inetd's configuration from the repository,
  * to validate it and setup inetd's data structures appropriately based on
@@ -630,14 +628,16 @@
  * 'errstr' is set to point at an appropriate error string.
  */
 struct method_context *
-read_method_context(const char *inst_fmri, const char *method, const char *path,
-    const char **errstr)
+read_method_context(const char *inst_fmri, const char *method, const char *path)
 {
 	scf_instance_t			*scf_inst = NULL;
 	struct method_context		*ret;
 	uint_t				retries;
-	const char			*tmpstr;
+	mc_error_t			*tmperr;
+	char				*fail;
 
+	fail = gettext("Failed to retrieve method context for the %s method of "
+	    "instance %s : %s");
 	for (retries = 0; retries <= REP_OP_RETRIES; retries++) {
 		if (make_handle_bound(rep_handle) == -1)
 			goto inst_failure;
@@ -659,11 +659,12 @@
 	if (retries > REP_OP_RETRIES)
 		goto inst_failure;
 
-	if ((tmpstr = restarter_get_method_context(
+	if ((tmperr = restarter_get_method_context(
 	    RESTARTER_METHOD_CONTEXT_VERSION, scf_inst, NULL, method, path,
 	    &ret)) != NULL) {
 		ret = NULL;
-		*errstr = tmpstr;
+		error_msg(fail, method, inst_fmri, tmperr->msg);
+		restarter_mc_error_destroy(tmperr);
 	}
 
 	scf_instance_destroy(scf_inst);
@@ -675,7 +676,8 @@
 	 * since we don't call bind_textdomain_codeset() or
 	 * setlocale(3C) after initialization.
 	 */
-	*errstr = gettext("failed to get instance from repository");
+	error_msg(fail, method, inst_fmri,
+	    gettext("failed to get instance from repository"));
 	return (NULL);
 }
 
--- a/usr/src/cmd/cmd-inet/usr.lib/inetd/inetd.c	Fri Jun 05 10:27:16 2009 -0400
+++ b/usr/src/cmd/cmd-inet/usr.lib/inetd/inetd.c	Fri Jun 05 10:28:40 2009 -0400
@@ -2975,7 +2975,6 @@
 	pid_t			child_pid;
 	method_info_t		*mi;
 	struct method_context	*mthd_ctxt = NULL;
-	const char		*errstr;
 	int			sig = 0;
 	int			ret;
 	instance_cfg_t		*cfg = instance->config;
@@ -3047,12 +3046,8 @@
 	 * modify the instances state if things go wrong.
 	 */
 	if ((mthd_ctxt = read_method_context(instance->fmri,
-	    methods[method].name, mi->exec_path, &errstr)) == NULL) {
-		error_msg(gettext("Failed to retrieve method context for the "
-		    "%s method of instance %s: %s"), methods[method].name,
-		    instance->fmri, errstr);
+	    methods[method].name, mi->exec_path)) == NULL)
 		goto prefork_failure;
-	}
 
 	/*
 	 * Perform some basic checks before we fork to limit the possibility
--- a/usr/src/cmd/cmd-inet/usr.lib/inetd/inetd_impl.h	Fri Jun 05 10:27:16 2009 -0400
+++ b/usr/src/cmd/cmd-inet/usr.lib/inetd/inetd_impl.h	Fri Jun 05 10:28:40 2009 -0400
@@ -300,7 +300,7 @@
 extern boolean_t method_info_equal(const method_info_t *,
     const method_info_t *);
 extern struct method_context *read_method_context(const char *, const char *,
-    const char *, const char **);
+    const char *);
 extern void destroy_instance_cfg(instance_cfg_t *);
 extern instance_cfg_t *read_instance_cfg(const char *);
 extern boolean_t bind_config_equal(const basic_cfg_t *, const basic_cfg_t *);
--- a/usr/src/cmd/cmd-inet/usr.sbin/ifconfig/ifconfig.c	Fri Jun 05 10:27:16 2009 -0400
+++ b/usr/src/cmd/cmd-inet/usr.sbin/ifconfig/ifconfig.c	Fri Jun 05 10:28:40 2009 -0400
@@ -4476,21 +4476,26 @@
 	if (ioctl(fd, SIOCGLIFFLAGS, &lifrl) == -1)
 		return (_B_FALSE);
 
-	if (up) {
+	if (up)
 		lifrl.lifr_flags |= IFF_UP;
-	} else {
-		/*
-		 * If we've been asked to bring down an IFF_DUPLICATE address,
-		 * then get the address and set it.  This will cause IP to
-		 * clear IFF_DUPLICATE and stop the automatic recovery timer.
-		 */
-		if (lifrl.lifr_flags & IFF_DUPLICATE) {
-			return (ioctl(fd, SIOCGLIFADDR, &lifrl) != -1 &&
-			    ioctl(fd, SIOCSLIFADDR, &lifrl) != -1);
+	else
+		lifrl.lifr_flags &= ~IFF_UP;
+
+	if (ioctl(fd, SIOCSLIFFLAGS, &lifrl) == -1)
+		return (_B_FALSE);
+
+	/*
+	 * If we're trying to bring the address down, ensure that DAD activity
+	 * (observable by IFF_DUPLICATE) has also been stopped.
+	 */
+	if (!up && ioctl(fd, SIOCGLIFFLAGS, &lifrl) != -1 &&
+	    lifrl.lifr_flags & IFF_DUPLICATE) {
+		if (ioctl(fd, SIOCGLIFADDR, &lifrl) == -1 ||
+		    ioctl(fd, SIOCSLIFADDR, &lifrl) == -1) {
+			return (_B_FALSE);
 		}
-		lifrl.lifr_flags &= ~IFF_UP;
-	}
-	return (ioctl(fd, SIOCSLIFFLAGS, &lifrl) == 0);
+	}
+	return (_B_TRUE);
 }
 
 static boolean_t
--- a/usr/src/cmd/cmd-inet/usr.sbin/in.ftpd/popen.c	Fri Jun 05 10:27:16 2009 -0400
+++ b/usr/src/cmd/cmd-inet/usr.sbin/in.ftpd/popen.c	Fri Jun 05 10:28:40 2009 -0400
@@ -1,10 +1,8 @@
 /*
- * Copyright 2004 Sun Microsystems, Inc.  All rights reserved.
+ * Copyright 2009 Sun Microsystems, Inc.  All rights reserved.
  * Use is subject to license terms.
  */
 
-#pragma ident	"%Z%%M%	%I%	%E% SMI"
-
 /****************************************************************************    
   Copyright (c) 1999,2000 WU-FTPD Development Group.  
   All rights reserved.
@@ -198,11 +196,10 @@
 	/* begin CERT suggested fixes */
 	close(0);
 	i = geteuid();
-	delay_signaling();	/* we can't allow any signals while euid==0: kinch */
-	seteuid(0);
+	setid_priv_on(0);
 	setgid(getegid());
 	setuid(i);
-	enable_signaling();	/* we can allow signals once again: kinch */
+	setid_priv_off(i);
 	/* end CERT suggested fixes */
 	execv(gargv[0], gargv);
 	perror(gargv[0]);
--- a/usr/src/cmd/cmd-inet/usr.sbin/in.ftpd/privs.c	Fri Jun 05 10:27:16 2009 -0400
+++ b/usr/src/cmd/cmd-inet/usr.sbin/in.ftpd/privs.c	Fri Jun 05 10:28:40 2009 -0400
@@ -1,10 +1,8 @@
 /*
- * Copyright 2004 Sun Microsystems, Inc.  All rights reserved.
+ * Copyright 2009 Sun Microsystems, Inc.  All rights reserved.
  * Use is subject to license terms.
  */
 
-#pragma ident	"%Z%%M%	%I%	%E% SMI"
-
 /*
  * Least privilege support functions.
  */
@@ -24,17 +22,8 @@
 #include "proto.h"
 
 #ifdef SOLARIS_PRIVS
-/*
- * Before becoming privilege aware in init_privs(), no explicit privilege
- * manipulation using priv_on()/priv_off() is necessary as seteuid(0) sets
- * the effective privilege set to the limit set. Thus these are all
- * initialized to TRUE.
- */
-static boolean_t got_setid_priv = B_TRUE;
-static boolean_t got_privaddr_priv = B_TRUE;
-static boolean_t got_read_priv = B_TRUE;
-static boolean_t got_search_priv = B_TRUE;
-static boolean_t got_chown_priv = B_TRUE;
+/* When ununitialized, this indicates we still have all privs */
+static priv_set_t *uprivs;
 #endif /* SOLARIS_PRIVS */
 
 #ifdef SOLARIS_PRIVS
@@ -55,20 +44,20 @@
 }
 #endif /* PRIVS_DEBUG */
 
-static void priv_on(const char *priv, boolean_t already_have)
+static void priv_on(const char *priv)
 {
     /* no need to add the privilege if already have it */
-    if (already_have)
+    if (uprivs == NULL || priv_ismember(uprivs, priv))
 	return;
 
     if (priv_set(PRIV_ON, PRIV_EFFECTIVE, priv, NULL) == -1)
 	syslog(LOG_ERR, "priv_set: error adding privilege %s: %m", priv);
 }
 
-static void priv_off(const char *priv, boolean_t already_had)
+static void priv_off(const char *priv)
 {
     /* don't remove the privilege if already had it */
-    if (already_had)
+    if (uprivs == NULL || priv_ismember(uprivs, priv))
 	return;
 
     if (priv_set(PRIV_OFF, PRIV_EFFECTIVE, priv, NULL) == -1)
@@ -85,52 +74,64 @@
 {
 #ifdef SOLARIS_PRIVS
     uid_t euid = geteuid();
-    priv_set_t *privset;
+    priv_set_t *pset1, *pset2;
 
     /*
-     * The FTP server runs with "basic" inheritable privileges, which are
-     * reset in pam_setcred() for non anonymous users. The seteuid() call in
-     * pass() sets the effective privileges to the inheritable privileges.
+     * The FTP server runs with the inheritable set and the limit set
+     * filled in through user_attr (or with default values of basic and all).
+     * The privileges available to the user at login, is an intersection
+     * of both those sets.  The only way to limit the root user is by
+     * changing the limit set, not by changing the I set.
      */
-    if ((privset = priv_allocset()) == NULL) {
-	syslog(LOG_ERR, "priv_allocset failed: %m");
+    if ((pset1 = priv_allocset()) == NULL ||
+	(uprivs = priv_allocset()) == NULL ||
+	(pset2 = priv_allocset()) == NULL) {
+	    syslog(LOG_ERR, "priv_allocset failed: %m");
+	    dologout(1);
+    }
+    if (getppriv(PRIV_LIMIT, pset1) == -1) {
+	syslog(LOG_ERR, "getppriv(limit) failed: %m");
 	dologout(1);
     }
-    if (getppriv(PRIV_EFFECTIVE, privset) == -1) {
-	syslog(LOG_ERR, "getppriv(effective) failed: %m");
+    if (getppriv(euid == 0 ? PRIV_PERMITTED : PRIV_INHERITABLE, pset2) == -1) {
+	syslog(LOG_ERR, "getppriv() failed: %m");
 	dologout(1);
     }
 
+    /* Compute the permitted set after login. */
+    priv_intersect(pset2, pset1);
+
     /*
-     * Set the permitted privilege set to the effective privileges plus
+     * Set the permitted privilege set to the allowable privileges plus
      * those required after init_privs() is called. Keep note of which
-     * effective privileges we already had so we don't turn them off.
+     * effective privileges we already had in uprivs so we don't turn
+     * them off.
      */
-    if (!priv_ismember(privset, PRIV_PROC_SETID)) {
-	got_setid_priv = B_FALSE;
-	(void) priv_addset(privset, PRIV_PROC_SETID);
-    }
-    if (!priv_ismember(privset, PRIV_NET_PRIVADDR)) {
-	got_privaddr_priv = B_FALSE;
-	(void) priv_addset(privset, PRIV_NET_PRIVADDR);
+    priv_emptyset(pset2);
+    (void) priv_addset(pset2, PRIV_PROC_SETID);
+    (void) priv_addset(pset2, PRIV_NET_PRIVADDR);
+    (void) priv_addset(pset2, PRIV_FILE_DAC_READ);
+    (void) priv_addset(pset2, PRIV_FILE_DAC_SEARCH);
+    (void) priv_addset(pset2, PRIV_FILE_CHOWN);
+
+    priv_copyset(pset2, uprivs);
+    priv_intersect(pset1, uprivs);
+
+    /* Now, set the effective privileges. */
+    if (setppriv(PRIV_SET, PRIV_EFFECTIVE, pset1) == -1) {
+	syslog(LOG_ERR,
+	    "unable to set privileges for %s: setppriv(effective): %m",
+	    username);
+	dologout(1);
     }
-    if (!priv_ismember(privset, PRIV_FILE_DAC_READ)) {
-	got_read_priv = B_FALSE;
-	(void) priv_addset(privset, PRIV_FILE_DAC_READ);
-    }
-    if (!priv_ismember(privset, PRIV_FILE_DAC_SEARCH)) {
-	got_search_priv = B_FALSE;
-	(void) priv_addset(privset, PRIV_FILE_DAC_SEARCH);
-    }
-    if (!priv_ismember(privset, PRIV_FILE_CHOWN)) {
-	got_chown_priv = B_FALSE;
-	(void) priv_addset(privset, PRIV_FILE_CHOWN);
-    }
+
 #if defined(SOLARIS_BSM_AUDIT) && !defined(SOLARIS_NO_AUDIT_FTPD_LOGOUT)
     /* needed for audit_ftpd_logout() */
-    (void) priv_addset(privset, PRIV_PROC_AUDIT);
+    (void) priv_addset(pset1, PRIV_PROC_AUDIT);
 #endif
-    if (setppriv(PRIV_SET, PRIV_PERMITTED, privset) == -1) {
+    /* And set the permitted, adding ftpd's required privileges in the mix. */
+    priv_union(pset2, pset1);
+    if (setppriv(PRIV_SET, PRIV_PERMITTED, pset1) == -1) {
 	syslog(LOG_ERR,
 	    "unable to set privileges for %s: setppriv(permitted): %m",
 	    username);
@@ -140,8 +141,8 @@
      * setppriv() has made us privilege aware, so the effective privileges
      * are no longer modified by user ID changes.
      */
-
-    priv_freeset(privset);
+    priv_freeset(pset1);
+    priv_freeset(pset2);
 
     /* set the real, effective and saved group ID's */
     setid_priv_on(0);
@@ -181,7 +182,7 @@
 {
     delay_signaling();
 #ifdef SOLARIS_PRIVS
-    priv_on(PRIV_NET_PRIVADDR, got_privaddr_priv);
+    priv_on(PRIV_NET_PRIVADDR);
 #else
     (void) seteuid(uid);
 #endif
@@ -191,7 +192,7 @@
 void port_priv_off(uid_t uid)
 {
 #ifdef SOLARIS_PRIVS
-    priv_off(PRIV_NET_PRIVADDR, got_privaddr_priv);
+    priv_off(PRIV_NET_PRIVADDR);
 #else
     (void) seteuid(uid);
 #endif
@@ -203,8 +204,8 @@
 {
     delay_signaling();
 #ifdef SOLARIS_PRIVS
-    priv_on(PRIV_FILE_DAC_READ, got_read_priv);
-    priv_on(PRIV_FILE_DAC_SEARCH, got_search_priv);
+    priv_on(PRIV_FILE_DAC_READ);
+    priv_on(PRIV_FILE_DAC_SEARCH);
 #endif
     /* necessary on Solaris for access over NFS */
     (void) seteuid(uid);
@@ -213,8 +214,8 @@
 void access_priv_off(uid_t uid)
 {
 #ifdef SOLARIS_PRIVS
-    priv_off(PRIV_FILE_DAC_READ, got_read_priv);
-    priv_off(PRIV_FILE_DAC_SEARCH, got_search_priv);
+    priv_off(PRIV_FILE_DAC_READ);
+    priv_off(PRIV_FILE_DAC_SEARCH);
 #endif
     (void) seteuid(uid);
     enable_signaling();
@@ -226,7 +227,7 @@
 {
     delay_signaling();
 #ifdef SOLARIS_PRIVS
-    priv_on(PRIV_PROC_SETID, got_setid_priv);
+    priv_on(PRIV_PROC_SETID);
 #else
     (void) seteuid(uid);
 #endif
@@ -236,7 +237,7 @@
 void setid_priv_off(uid_t uid)
 {
 #ifdef SOLARIS_PRIVS
-    priv_off(PRIV_PROC_SETID, got_setid_priv);
+    priv_off(PRIV_PROC_SETID);
 #else
     (void) seteuid(uid);
 #endif
@@ -248,7 +249,7 @@
 {
     delay_signaling();
 #ifdef SOLARIS_PRIVS
-    priv_on(PRIV_FILE_CHOWN, got_chown_priv);
+    priv_on(PRIV_FILE_CHOWN);
 #endif
     /* necessary on Solaris for chown over NFS */
     (void) seteuid(uid);
@@ -257,7 +258,7 @@
 void chown_priv_off(uid_t uid)
 {
 #ifdef SOLARIS_PRIVS
-    priv_off(PRIV_FILE_CHOWN, got_chown_priv);
+    priv_off(PRIV_FILE_CHOWN);
 #endif
     (void) seteuid(uid);
     enable_signaling();
--- a/usr/src/cmd/fdisk/fdisk.c	Fri Jun 05 10:27:16 2009 -0400
+++ b/usr/src/cmd/fdisk/fdisk.c	Fri Jun 05 10:28:40 2009 -0400
@@ -38,7 +38,6 @@
  * operations from a supplied menu or from the command line. Diagnostic
  * options are also available.
  */
- 
 #include <stdio.h>
 #include <stdlib.h>
 #include <string.h>
@@ -871,7 +870,7 @@
 		if (io_ifdisk) {
 			(void) fprintf(stderr, "\n");
 			(void) fprintf(stderr, "Press Enter to continue.\n");
-			(void) gets(s);
+			(void) fgets(s, sizeof (s), stdin);
 		}
 	}
 
@@ -1000,7 +999,7 @@
 		print_Table();
 		if (io_ifdisk) {
 			(void) fprintf(stderr, "Press Enter to continue.\n");
-			(void) gets(s);
+			(void) fgets(s, sizeof (s), stdin);
 		}
 	}
 
@@ -1971,15 +1970,16 @@
 	for (;;) {
 		(void) printf(Q_LINE);
 		(void) printf("Enter Selection: ");
-		(void) gets(s);
+		(void) fgets(s, sizeof (s), stdin);
 		rm_blanks(s);
-		while (!((s[0] > '0') && (s[0] < '7') && (s[1] == 0))) {
+		while (!((s[0] > '0') && (s[0] < '7') &&
+		    ((s[1] == '\0') || (s[1] == '\n')))) {
 			(void) printf(E_LINE); /* Clear any previous error */
 			(void) printf(
 			    "Enter a one-digit number between 1 and 6.");
 			(void) printf(Q_LINE);
 			(void) printf("Enter Selection: ");
-			(void) gets(s);
+			(void) fgets(s, sizeof (s), stdin);
 			rm_blanks(s);
 		}
 		(void) printf(E_LINE);
@@ -2096,9 +2096,9 @@
 		    "   5=DOS12     6=DOS16       7=DOSEXT     8=DOSBIG\n"
 		    "   9=DOS16LBA  A=x86 Boot    B=Diagnostic C=FAT32\n"
 		    "   D=FAT32LBA  E=DOSEXTLBA   F=EFI        0=Exit? ");
-		(void) gets(s);
+		(void) fgets(s, sizeof (s), stdin);
 		rm_blanks(s);
-		if (s[1] != 0) {
+		if ((s[1] != '\0') && (s[1] != '\n')) {
 			(void) printf(E_LINE);
 			(void) printf("Invalid selection, try again.");
 			continue;
@@ -2305,11 +2305,11 @@
 	(void) printf(
 	    "Specify the percentage of disk to use for this partition\n"
 	    "(or type \"c\" to specify the size in cylinders). ");
-	(void) gets(s);
+	(void) fgets(s, sizeof (s), stdin);
 	rm_blanks(s);
 	if (s[0] != 'c') {	/* Specify size in percentage of disk */
 		i = 0;
-		while (s[i] != '\0') {
+		while ((s[i] != '\0') && (s[i] != '\n')) {
 			if (s[i] < '0' || s[i] > '9') {
 				(void) printf(E_LINE);
 				(void) printf("Invalid percentage value "
@@ -2569,9 +2569,10 @@
 			    "Specify the partition number to boot from"
 			    " (or specify 0 for none): ");
 			}
-		(void) gets(s);
+		(void) fgets(s, sizeof (s), stdin);
 		rm_blanks(s);
-		if ((s[1] != 0) || (s[0] < '0') || (s[0] > '4')) {
+		if (((s[1] != '\0') && (s[1] != '\n')) ||
+		    (s[0] < '0') || (s[0] > '4')) {
 			(void) printf(E_LINE);
 			(void) printf(
 			    "Invalid response, please specify a number"
@@ -2687,14 +2688,15 @@
 DEL1:	(void) printf(Q_LINE);
 	(void) printf("Specify the partition number to delete"
 	    " (or enter 0 to exit): ");
-	(void) gets(s);
+	(void) fgets(s, sizeof (s), stdin);
 	rm_blanks(s);
 	if ((s[0] == '0')) {	/* exit delete command */
 		(void) printf(E_LINE);	/* clear error message */
 		return (1);
 	}
 	/* Accept only a single digit between 1 and 4 */
-	if (s[1] != 0 || (i = atoi(s)) < 1 || i > FD_NUMPART) {
+	if (((s[1] != '\0') && (s[1] != '\n')) ||
+	    (i = atoi(s)) < 1 || i > FD_NUMPART) {
 		(void) printf(E_LINE);
 		(void) printf("Invalid response, retry the operation.\n");
 		goto DEL1;
@@ -2777,9 +2779,11 @@
 {
 int slen, i, j;
 unsigned int cyl;
-	(void) gets(s);
+	(void) fgets(s, sizeof (s), stdin);
 	rm_blanks(s);
 	slen = strlen(s);
+	if (s[slen - 1] == '\n')
+		slen--;
 	j = 1;
 	cyl = 0;
 	for (i = slen - 1; i >= 0; i--) {
@@ -3494,9 +3498,10 @@
 	char	s[80];
 
 	for (;;) {
-		(void) gets(s);
+		(void) fgets(s, sizeof (s), stdin);
 		rm_blanks(s);
-		if ((s[1] != 0) || ((s[0] != 'y') && (s[0] != 'n'))) {
+		if (((s[1] != '\0') && (s[1] != '\n')) ||
+		    ((s[0] != 'y') && (s[0] != 'n'))) {
 			(void) printf(E_LINE);
 			(void) printf("Please answer with \"y\" or \"n\": ");
 			continue;
--- a/usr/src/cmd/filebench/common/eventgen.c	Fri Jun 05 10:27:16 2009 -0400
+++ b/usr/src/cmd/filebench/common/eventgen.c	Fri Jun 05 10:28:40 2009 -0400
@@ -19,7 +19,7 @@
  * CDDL HEADER END
  */
 /*
- * Copyright 2008 Sun Microsystems, Inc.  All rights reserved.
+ * Copyright 2009 Sun Microsystems, Inc.  All rights reserved.
  * Use is subject to license terms.
  *
  * Portions Copyright 2008 Denis Cheng
@@ -79,6 +79,7 @@
 	hrtime_t last;
 
 	last = gethrtime();
+	filebench_shm->shm_eventgen_enabled = FALSE;
 
 	/* CONSTCOND */
 	while (1) {
@@ -91,6 +92,11 @@
 			continue;
 		} else {
 			rate = avd_get_int(filebench_shm->shm_eventgen_hz);
+			if (rate > 0) {
+				filebench_shm->shm_eventgen_enabled = TRUE;
+			} else {
+				continue;
+			}
 		}
 
 		/* Sleep for 10xperiod */
--- a/usr/src/cmd/filebench/common/filebench.h	Fri Jun 05 10:27:16 2009 -0400
+++ b/usr/src/cmd/filebench/common/filebench.h	Fri Jun 05 10:28:40 2009 -0400
@@ -123,7 +123,7 @@
 #define	MIN(x, y) ((x) < (y) ? (x) : (y))
 #endif
 
-#define	FILEBENCH_VERSION	"1.4.7"
+#define	FILEBENCH_VERSION	"1.4.8"
 #define	FILEBENCHDIR	"/usr/benchmarks/filebench"
 #define	FILEBENCH_PROMPT	"filebench> "
 #define	MAX_LINE_LEN	1024
--- a/usr/src/cmd/filebench/common/flowop_library.c	Fri Jun 05 10:27:16 2009 -0400
+++ b/usr/src/cmd/filebench/common/flowop_library.c	Fri Jun 05 10:28:40 2009 -0400
@@ -762,7 +762,7 @@
 flowoplib_eventlimit(threadflow_t *threadflow, flowop_t *flowop)
 {
 	/* Immediately bail if not set/enabled */
-	if (filebench_shm->shm_eventgen_hz == NULL)
+	if (!filebench_shm->shm_eventgen_enabled)
 		return (FILEBENCH_OK);
 
 	if (flowop->fo_initted == 0) {
@@ -772,7 +772,7 @@
 	}
 
 	flowop_beginop(threadflow, flowop);
-	while (filebench_shm->shm_eventgen_hz != NULL) {
+	while (filebench_shm->shm_eventgen_enabled) {
 		(void) ipc_mutex_lock(&filebench_shm->shm_eventgen_lock);
 		if (filebench_shm->shm_eventgen_q > 0) {
 			filebench_shm->shm_eventgen_q--;
@@ -825,7 +825,7 @@
 	uint64_t events;
 
 	/* Immediately bail if not set/enabled */
-	if (filebench_shm->shm_eventgen_hz == NULL)
+	if (!filebench_shm->shm_eventgen_enabled)
 		return (FILEBENCH_OK);
 
 	if (flowop->fo_initted == 0) {
@@ -880,7 +880,7 @@
 	events = iops;
 
 	flowop_beginop(threadflow, flowop);
-	while (filebench_shm->shm_eventgen_hz != NULL) {
+	while (filebench_shm->shm_eventgen_enabled) {
 
 		(void) ipc_mutex_lock(&filebench_shm->shm_eventgen_lock);
 		if (filebench_shm->shm_eventgen_q >= events) {
@@ -913,7 +913,7 @@
 	uint64_t events;
 
 	/* Immediately bail if not set/enabled */
-	if (filebench_shm->shm_eventgen_hz == NULL)
+	if (!filebench_shm->shm_eventgen_enabled)
 		return (FILEBENCH_OK);
 
 	if (flowop->fo_initted == 0) {
@@ -954,7 +954,7 @@
 	events = ops;
 
 	flowop_beginop(threadflow, flowop);
-	while (filebench_shm->shm_eventgen_hz != NULL) {
+	while (filebench_shm->shm_eventgen_enabled) {
 		(void) ipc_mutex_lock(&filebench_shm->shm_eventgen_lock);
 		if (filebench_shm->shm_eventgen_q >= events) {
 			filebench_shm->shm_eventgen_q -= events;
@@ -988,7 +988,7 @@
 	uint64_t events;
 
 	/* Immediately bail if not set/enabled */
-	if (filebench_shm->shm_eventgen_hz == NULL)
+	if (!filebench_shm->shm_eventgen_enabled)
 		return (FILEBENCH_OK);
 
 	if (flowop->fo_initted == 0) {
@@ -1047,7 +1047,7 @@
 	    (u_longlong_t)bytes, (u_longlong_t)events);
 
 	flowop_beginop(threadflow, flowop);
-	while (filebench_shm->shm_eventgen_hz != NULL) {
+	while (filebench_shm->shm_eventgen_enabled) {
 		(void) ipc_mutex_lock(&filebench_shm->shm_eventgen_lock);
 		if (filebench_shm->shm_eventgen_q >= events) {
 			filebench_shm->shm_eventgen_q -= events;
--- a/usr/src/cmd/filebench/common/ipc.h	Fri Jun 05 10:27:16 2009 -0400
+++ b/usr/src/cmd/filebench/common/ipc.h	Fri Jun 05 10:28:40 2009 -0400
@@ -152,6 +152,7 @@
 	/*
 	 * Event generator state
 	 */
+	int		shm_eventgen_enabled; /* event gen in operation */
 	avd_t		shm_eventgen_hz;   /* number of events per sec. */
 	uint64_t	shm_eventgen_q;    /* count of unclaimed events */
 	pthread_mutex_t	shm_eventgen_lock; /* lock protecting count */
--- a/usr/src/cmd/filebench/common/misc.c	Fri Jun 05 10:27:16 2009 -0400
+++ b/usr/src/cmd/filebench/common/misc.c	Fri Jun 05 10:28:40 2009 -0400
@@ -335,7 +335,7 @@
 filebench_shutdown(int error) {
 
 	if (error) {
-		filebench_log(LOG_DEBUG_IMPL, "Shutdown on error");
+		filebench_log(LOG_DEBUG_IMPL, "Shutdown on error %d", error);
 		(void) ipc_mutex_lock(&filebench_shm->shm_procflow_lock);
 		if (filebench_shm->shm_f_abort == FILEBENCH_ABORT_FINI) {
 			(void) ipc_mutex_unlock(
--- a/usr/src/cmd/filebench/common/parser_gram.y	Fri Jun 05 10:27:16 2009 -0400
+++ b/usr/src/cmd/filebench/common/parser_gram.y	Fri Jun 05 10:28:40 2009 -0400
@@ -119,8 +119,11 @@
 static void parser_fileset_create(cmd_t *);
 
 /* set commands */
-static void parser_set_integer(char *, fbint_t);
-static void parser_set_var(char *, char *);
+static void parser_set_integer(cmd_t *cmd);
+static void parser_set_var(cmd_t *cmd);
+static void parser_set_var_op_int(cmd_t *cmd);
+static void parser_set_int_op_var(cmd_t *cmd);
+static void parser_set_var_op_var(cmd_t *cmd);
 
 /* Shutdown Commands */
 static void parser_proc_shutdown(cmd_t *);
@@ -185,7 +188,7 @@
 %token FSE_DIRECTORY FSE_COMMAND FSE_FILESET FSE_XMLDUMP FSE_RAND FSE_MODE
 %token FSE_MULTI FSE_MULTIDUMP
 %token FSK_SEPLST FSK_OPENLST FSK_CLOSELST FSK_ASSIGN FSK_IN FSK_QUOTE
-%token FSK_DIRSEPLST
+%token FSK_DIRSEPLST FSK_PLUS FSK_MINUS FSK_MULTIPLY FSK_DIVIDE
 %token FSA_SIZE FSA_PREALLOC FSA_PARALLOC FSA_PATH FSA_REUSE
 %token FSA_PROCESS FSA_MEMSIZE FSA_RATE FSA_CACHED FSA_READONLY FSA_TRUSTTREE
 %token FSA_IOSIZE FSA_FILE FSA_WSS FSA_NAME FSA_RANDOM FSA_INSTANCES
@@ -226,6 +229,7 @@
 %type <cmd> thread echo_command usage_command help_command vars_command
 %type <cmd> version_command enable_command multisync_command
 %type <cmd> warmup_command fscheck_command fsflush_command
+%type <cmd> set_integer_command set_other_command
 
 %type <attr> files_attr_op files_attr_ops pt_attr_op pt_attr_ops
 %type <attr> fo_attr_op fo_attr_ops ev_attr_op ev_attr_ops
@@ -242,7 +246,7 @@
 %type <ival> randvar_attr_name FSA_TYPE randtype_name randvar_attr_param
 %type <ival> randsrc_name FSA_RANDSRC randvar_attr_tsp em_attr_name
 %type <ival> FSS_TYPE FSS_SEED FSS_GAMMA FSS_MEAN FSS_MIN FSS_SRC
-%type <ival> fscheck_attr_name FSA_FSTYPE
+%type <ival> fscheck_attr_name FSA_FSTYPE binary_op
 
 %type <rndtb>  probtabentry_list probtabentry
 %type <avd> var_int_val
@@ -790,18 +794,68 @@
 		yydebug = 1;
 };
 
-set_command: FSC_SET FSV_VARIABLE FSK_ASSIGN FSV_VAL_INT
+set_command:
+   set_integer_command
+ | set_other_command;
+
+set_integer_command: FSC_SET FSV_VARIABLE FSK_ASSIGN FSV_VAL_INT
+{
+	if (($$ = alloc_cmd()) == NULL)
+		YYERROR;
+	$$->cmd_tgt1 = $2;
+	$$->cmd_qty = $4;
+	if (parentscript) {
+		parser_vars($$);
+	}
+	$$->cmd = parser_set_integer;
+}| FSC_SET FSV_VARIABLE FSK_ASSIGN FSV_VARIABLE
 {
 	if (($$ = alloc_cmd()) == NULL)
 		YYERROR;
-	var_assign_integer($2, $4);
+	var_assign_var($2, $4);
+	$$->cmd_tgt1 = $2;
+	$$->cmd_tgt2 = $4;
 	if (parentscript) {
-		$$->cmd_tgt1 = $2;
 		parser_vars($$);
 	}
-	$$->cmd = NULL;
+	$$->cmd = parser_set_var;
 }
-| FSC_SET FSV_VARIABLE FSK_ASSIGN FSV_VAL_BOOLEAN
+| set_integer_command binary_op FSV_VAL_INT
+{
+	if ($1->cmd == parser_set_integer) {
+		switch ($2) {
+		case FSK_PLUS:
+			var_assign_integer($1->cmd_tgt1, $1->cmd_qty + $3);
+			break;
+		case FSK_MINUS:
+			var_assign_integer($1->cmd_tgt1, $1->cmd_qty - $3);
+			break;
+		case FSK_MULTIPLY:
+			var_assign_integer($1->cmd_tgt1, $1->cmd_qty * $3);
+			break;
+		case FSK_DIVIDE:
+			var_assign_integer($1->cmd_tgt1, $1->cmd_qty / $3);
+			break;
+		}
+		$$->cmd = NULL;
+	} else {
+		$1->cmd_qty = $3;
+		$1->cmd_subtype = $2;
+		$1->cmd = parser_set_var_op_int;
+	}
+}
+| set_integer_command binary_op FSV_VARIABLE
+{
+	$1->cmd_tgt3 = $3;
+	$1->cmd_subtype = $2;
+	if ($1->cmd == parser_set_integer) {
+		$$->cmd = parser_set_int_op_var;
+	} else {
+		$1->cmd = parser_set_var_op_var;
+	}
+};
+
+set_other_command: FSC_SET FSV_VARIABLE FSK_ASSIGN FSV_VAL_BOOLEAN
 {
 	if (($$ = alloc_cmd()) == NULL)
 		YYERROR;
@@ -832,16 +886,6 @@
 		parser_vars($$);
 	}
 	$$->cmd = NULL;
-}| FSC_SET FSV_VARIABLE FSK_ASSIGN FSV_VARIABLE
-{
-	if (($$ = alloc_cmd()) == NULL)
-		YYERROR;
-	var_assign_var($2, $4);
-	if (parentscript) {
-		$$->cmd_tgt1 = $2;
-		parser_vars($$);
-	}
-	$$->cmd = NULL;
 } | FSC_SET FSE_MODE FSC_QUIT FSA_TIMEOUT
 {
 	filebench_shm->shm_rmode = FILEBENCH_MODE_TIMEOUT;
@@ -1480,6 +1524,12 @@
 	$$->attr_name = $1;
 };
 
+binary_op:
+   FSK_PLUS {$$ = FSK_PLUS;}
+ | FSK_MINUS {$$ = FSK_MINUS;}
+ | FSK_MULTIPLY {$$ = FSK_MULTIPLY;}
+ | FSK_DIVIDE {$$ = FSK_DIVIDE;};
+
 files_attr_name: attrs_define_file
 |attrs_define_fileset;
 
@@ -3236,9 +3286,9 @@
  * variable, or the appropriate field of a random variable
  */
 static void
-parser_set_integer(char *name, fbint_t integer)
+parser_set_integer(cmd_t *cmd)
 {
-	var_assign_integer(name, integer);
+	var_assign_integer(cmd->cmd_tgt1, cmd->cmd_qty);
 }
 
 /*
@@ -3247,9 +3297,100 @@
  * random variable from another variable
  */
 static void
-parser_set_var(char *dst_name, char *src_name)
+parser_set_var(cmd_t *cmd)
+{
+	var_assign_var(cmd->cmd_tgt1, cmd->cmd_tgt2);
+}
+
+/*
+ * Used by the set command to set up for a binary operation of a
+ * variable from a var, with an integer
+ */
+static void
+parser_set_var_op_int(cmd_t *cmd)
+{
+	printf("parser_set_var_op_int: Called\n");
+	switch (cmd->cmd_subtype) {
+	case FSK_PLUS:
+		var_assign_op_var_int(cmd->cmd_tgt1, VAR_IND_INT_SUM_IV,
+		    cmd->cmd_tgt2, cmd->cmd_qty);
+		break;
+
+	case FSK_MINUS:
+		var_assign_op_var_int(cmd->cmd_tgt1, VAR_IND_IV_DIF_INT,
+		    cmd->cmd_tgt2, cmd->cmd_qty);
+		break;
+
+	case FSK_MULTIPLY:
+		var_assign_op_var_int(cmd->cmd_tgt1, VAR_IND_INT_MUL_IV,
+		    cmd->cmd_tgt2, cmd->cmd_qty);
+		break;
+
+	case FSK_DIVIDE:
+		var_assign_op_var_int(cmd->cmd_tgt1, VAR_IND_IV_DIV_INT,
+		    cmd->cmd_tgt2, cmd->cmd_qty);
+		break;
+	}
+}
+
+/*
+ * Used by the set command to set up for a binary operation of an
+ * integer with a variable from a var
+ */
+static void
+parser_set_int_op_var(cmd_t *cmd)
 {
-	var_assign_var(dst_name, src_name);
+	switch (cmd->cmd_subtype) {
+	case FSK_PLUS:
+		var_assign_op_var_int(cmd->cmd_tgt1, VAR_IND_INT_SUM_IV,
+		    cmd->cmd_tgt3, cmd->cmd_qty);
+		break;
+
+	case FSK_MINUS:
+		var_assign_op_var_int(cmd->cmd_tgt1, VAR_IND_INT_DIF_IV,
+		    cmd->cmd_tgt3, cmd->cmd_qty);
+		break;
+
+	case FSK_MULTIPLY:
+		var_assign_op_var_int(cmd->cmd_tgt1, VAR_IND_INT_MUL_IV,
+		    cmd->cmd_tgt3, cmd->cmd_qty);
+		break;
+
+	case FSK_DIVIDE:
+		var_assign_op_var_int(cmd->cmd_tgt1, VAR_IND_INT_DIV_IV,
+		    cmd->cmd_tgt3, cmd->cmd_qty);
+		break;
+	}
+}
+
+/*
+ * Used by the set command to set up for a binary operation of two
+ * variables from other vars.
+ */
+static void
+parser_set_var_op_var(cmd_t *cmd)
+{
+	switch (cmd->cmd_subtype) {
+	case FSK_PLUS:
+		var_assign_op_var_var(cmd->cmd_tgt1, VAR_IND_IV_SUM_IV,
+		    cmd->cmd_tgt2, cmd->cmd_tgt3);
+		break;
+
+	case FSK_MINUS:
+		var_assign_op_var_var(cmd->cmd_tgt1, VAR_IND_IV_DIF_IV,
+		    cmd->cmd_tgt2, cmd->cmd_tgt3);
+		break;
+
+	case FSK_MULTIPLY:
+		var_assign_op_var_var(cmd->cmd_tgt1, VAR_IND_IV_MUL_IV,
+		    cmd->cmd_tgt2, cmd->cmd_tgt3);
+		break;
+
+	case FSK_DIVIDE:
+		var_assign_op_var_var(cmd->cmd_tgt1, VAR_IND_IV_DIV_IV,
+		    cmd->cmd_tgt2, cmd->cmd_tgt3);
+		break;
+	}
 }
 
 
--- a/usr/src/cmd/filebench/common/parser_lex.l	Fri Jun 05 10:27:16 2009 -0400
+++ b/usr/src/cmd/filebench/common/parser_lex.l	Fri Jun 05 10:28:40 2009 -0400
@@ -220,6 +220,10 @@
 <INITIAL>=			{ return FSK_ASSIGN; }
 <INITIAL>\,			{ return FSK_SEPLST; }
 <INITIAL>in                     { return FSK_IN; }
+<INITIAL>\+                     { return FSK_PLUS; }
+<INITIAL>\-                     { return FSK_MINUS; }
+<INITIAL>\*                     { return FSK_MULTIPLY; }
+<INITIAL>\/                     { return FSK_DIVIDE; }
 
 <INITIAL>[0-9]+	{
                                 errno = 0;
--- a/usr/src/cmd/filebench/common/parsertypes.h	Fri Jun 05 10:27:16 2009 -0400
+++ b/usr/src/cmd/filebench/common/parsertypes.h	Fri Jun 05 10:28:40 2009 -0400
@@ -19,15 +19,13 @@
  * CDDL HEADER END
  */
 /*
- * Copyright 2008 Sun Microsystems, Inc.  All rights reserved.
+ * Copyright 2009 Sun Microsystems, Inc.  All rights reserved.
  * Use is subject to license terms.
  */
 
 #ifndef	_FB_PARSERTYPES_H
 #define	_FB_PARSERTYPES_H
 
-#pragma ident	"%Z%%M%	%I%	%E% SMI"
-
 #include "config.h"
 
 #include "filebench.h"
@@ -68,8 +66,8 @@
 	char		*cmd_tgt1;
 	char		*cmd_tgt2;
 	char		*cmd_tgt3;
-	char		*cmd_tgt4;
 	char		*thread_name;
+	int		cmd_subtype;
 	uint64_t	cmd_qty;
 	struct cmd	*cmd_list;
 	struct cmd	*cmd_next;
--- a/usr/src/cmd/filebench/common/vars.c	Fri Jun 05 10:27:16 2009 -0400
+++ b/usr/src/cmd/filebench/common/vars.c	Fri Jun 05 10:28:40 2009 -0400
@@ -19,14 +19,12 @@
  * CDDL HEADER END
  */
 /*
- * Copyright 2008 Sun Microsystems, Inc.  All rights reserved.
+ * Copyright 2009 Sun Microsystems, Inc.  All rights reserved.
  * Use is subject to license terms.
  *
  * Portions Copyright 2008 Denis Cheng
  */
 
-#pragma ident	"%Z%%M%	%I%	%E% SMI"
-
 #include <stdlib.h>
 #include <stdio.h>
 #include <string.h>
@@ -41,6 +39,9 @@
 #include "fb_random.h"
 
 static var_t *var_find_dynamic(char *name);
+static boolean_t var_get_bool(var_t *var);
+static fbint_t var_get_int(var_t *var);
+static double var_get_dbl(var_t *var);
 
 /*
  * The filebench variables system has attribute value descriptors (avd_t)
@@ -152,7 +153,6 @@
 fbint_t
 avd_get_int(avd_t avd)
 {
-	var_t *ivp;
 	randdist_t *rndp;
 
 	if (avd == NULL)
@@ -169,21 +169,7 @@
 			return (0);
 
 	case AVD_IND_VAR:
-		if ((ivp = avd->avd_val.varptr) == NULL)
-			return (0);
-
-		if (VAR_HAS_INTEGER(ivp))
-			return (ivp->var_val.integer);
-
-		if (VAR_HAS_RANDDIST(ivp)) {
-			if ((rndp = ivp->var_val.randptr) != NULL)
-				return ((fbint_t)rndp->rnd_get(rndp));
-		}
-
-		filebench_log(LOG_ERROR,
-		    "Attempt to get integer from %s var $%s",
-		    var_get_type_string(ivp), ivp->var_name);
-		return (0);
+		return (var_get_int(avd->avd_val.varptr));
 
 	case AVD_IND_RANDVAR:
 		if ((rndp = avd->avd_val.randptr) == NULL)
@@ -207,7 +193,6 @@
 double
 avd_get_dbl(avd_t avd)
 {
-	var_t *ivp;
 	randdist_t *rndp;
 
 	if (avd == NULL)
@@ -233,22 +218,7 @@
 			return (0.0);
 
 	case AVD_IND_VAR:
-		ivp = avd->avd_val.varptr;
-
-		if (ivp && VAR_HAS_INTEGER(ivp))
-			return ((double)ivp->var_val.integer);
-
-		if (ivp && VAR_HAS_DOUBLE(ivp))
-			return (ivp->var_val.dbl_flt);
-
-		if (ivp && VAR_HAS_RANDDIST(ivp)) {
-			if ((rndp = ivp->var_val.randptr) != NULL)
-				return (rndp->rnd_get(rndp));
-		}
-		filebench_log(LOG_ERROR,
-		    "Attempt to get double float from %s var $%s",
-		    var_get_type_string(ivp), ivp->var_name);
-		return (0.0);
+		return (var_get_dbl(avd->avd_val.varptr));
 
 	case AVD_IND_RANDVAR:
 		if ((rndp = avd->avd_val.randptr) == NULL) {
@@ -270,8 +240,6 @@
 boolean_t
 avd_get_bool(avd_t avd)
 {
-	var_t *ivp;
-
 	if (avd == NULL)
 		return (0);
 
@@ -300,23 +268,7 @@
 		return (FALSE);
 
 	case AVD_IND_VAR:
-		if ((ivp = avd->avd_val.varptr) == NULL)
-			return (0);
-
-		if (VAR_HAS_BOOLEAN(ivp))
-			return (ivp->var_val.boolean);
-
-		if (VAR_HAS_INTEGER(ivp)) {
-			if (ivp->var_val.boolean)
-				return (TRUE);
-			else
-				return (FALSE);
-		}
-
-		filebench_log(LOG_ERROR,
-		    "Attempt to get boolean from %s var $%s",
-		    var_get_type_string(ivp), ivp->var_name);
-		return (FALSE);
+		return (var_get_bool(avd->avd_val.varptr));
 
 	default:
 		filebench_log(LOG_ERROR,
@@ -465,7 +417,11 @@
 
 	case VAR_TYPE_INDVAR_SET:
 		avd->avd_type = AVD_IND_VAR;
-		avd->avd_val.varptr = var->var_val.varptr;
+		if ((var->var_type & VAR_INDVAR_MASK) == VAR_IND_ASSIGN)
+			avd->avd_val.varptr = var->var_varptr1;
+		else
+			avd->avd_val.varptr = var;
+
 		break;
 
 	default:
@@ -666,6 +622,29 @@
 }
 
 /*
+ * Searches for the named var and returns it if found. If not
+ * found it allocates a new variable
+ */
+static var_t *
+var_find_alloc(char *name)
+{
+	var_t *var;
+
+	if (name == NULL) {
+		filebench_log(LOG_ERROR,
+		    "var_find_alloc: Var name not supplied");
+		return (NULL);
+	}
+
+	name += 1;
+
+	if ((var = var_find(name)) == NULL) {
+			var = var_alloc(name);
+	}
+	return (var);
+}
+
+/*
  * Searches for the named var, and, if found, sets its
  * var_val.boolean's value to that of the supplied boolean.
  * If not found, the routine allocates a new var and sets
@@ -678,19 +657,7 @@
 {
 	var_t *var;
 
-	if (name == NULL) {
-		filebench_log(LOG_ERROR,
-		    "var_assign_boolean: Name not supplied");
-		return (0);
-	}
-
-	name += 1;
-
-	if ((var = var_find(name)) == NULL) {
-			var = var_alloc(name);
-	}
-
-	if (var == NULL) {
+	if ((var = var_find_alloc(name)) == NULL) {
 		filebench_log(LOG_ERROR, "Cannot assign variable %s",
 		    name);
 		return (-1);
@@ -723,19 +690,7 @@
 {
 	var_t *var;
 
-	if (name == NULL) {
-		filebench_log(LOG_ERROR,
-		    "var_assign_integer: Name not supplied");
-		return (0);
-	}
-
-	name += 1;
-
-	if ((var = var_find(name)) == NULL) {
-			var = var_alloc(name);
-	}
-
-	if (var == NULL) {
+	if ((var = var_find_alloc(name)) == NULL) {
 		filebench_log(LOG_ERROR, "Cannot assign variable %s",
 		    name);
 		return (-1);
@@ -756,6 +711,201 @@
 }
 
 /*
+ * Add, subtract, multiply or divide two integers based on optype
+ * passed from caller.
+ */
+static fbint_t
+var_binary_integer_op(var_t *var)
+{
+	fbint_t result;
+	fbint_t src1, src2;
+
+	if (var == NULL)
+		return (0);
+
+	switch (var->var_type & VAR_INDBINOP_MASK) {
+	case VAR_IND_BINOP_INT:
+		src2 = var->var_val.integer;
+		break;
+
+	case VAR_IND_BINOP_DBL:
+		src2 = (fbint_t)var->var_val.dbl_flt;
+		break;
+
+	case VAR_IND_BINOP_VAR:
+		if (var->var_val.varptr2 != NULL)
+			src2 = var_get_int(var->var_val.varptr2);
+		else
+			src2 = 0;
+		break;
+	}
+
+	if (var->var_varptr1 != NULL)
+		src1 = var_get_int(var->var_varptr1);
+	else
+		src1 = 0;
+
+	switch (var->var_type & VAR_INDVAR_MASK) {
+	case VAR_IND_VAR_SUM_VC:
+		result = src1 + src2;
+		break;
+
+	case VAR_IND_VAR_DIF_VC:
+		result = src1 - src2;
+		break;
+
+	case VAR_IND_C_DIF_VAR:
+		result = src2 - src1;
+		break;
+
+	case VAR_IND_VAR_MUL_VC:
+		result = src1 * src2;
+		break;
+
+	case VAR_IND_VAR_DIV_VC:
+		result = src1 / src2;
+		break;
+
+	case VAR_IND_C_DIV_VAR:
+		result = src2 / src1;
+		break;
+
+	default:
+		filebench_log(LOG_DEBUG_IMPL,
+		    "var_binary_integer_op: Called with unknown IND_TYPE");
+		result = 0;
+		break;
+	}
+	return (result);
+}
+
+/*
+ * Add, subtract, multiply or divide two double precision floating point
+ * numbers based on optype passed from caller.
+ */
+static double
+var_binary_dbl_flt_op(var_t *var)
+{
+	double result;
+	double src1, src2;
+
+	if (var == NULL)
+		return (0.0);
+
+	switch (var->var_type & VAR_INDBINOP_MASK) {
+	case VAR_IND_BINOP_INT:
+		src2 = (double)var->var_val.integer;
+		break;
+
+	case VAR_IND_BINOP_DBL:
+		src2 = var->var_val.dbl_flt;
+		break;
+
+	case VAR_IND_BINOP_VAR:
+		if (var->var_val.varptr2 != NULL)
+			src2 = var_get_dbl(var->var_val.varptr2);
+		else
+			src2 = 0;
+		break;
+	}
+
+	if (var->var_varptr1 != NULL)
+		src1 = var_get_dbl(var->var_varptr1);
+	else
+		src1 = 0;
+
+	switch (var->var_type & VAR_INDVAR_MASK) {
+	case VAR_IND_VAR_SUM_VC:
+		result = src1 + src2;
+		break;
+
+	case VAR_IND_VAR_DIF_VC:
+		result = src1 - src2;
+		break;
+
+	case VAR_IND_C_DIF_VAR:
+		result = src2 - src1;
+		break;
+
+	case VAR_IND_VAR_MUL_VC:
+		result = src1 * src2;
+		break;
+
+	case VAR_IND_C_DIV_VAR:
+		result = src2 / src1;
+		break;
+
+	case VAR_IND_VAR_DIV_VC:
+		result = src1 / src2;
+		break;
+
+	default:
+		filebench_log(LOG_DEBUG_IMPL,
+		    "var_binary_dbl_flt_op: Called with unknown IND_TYPE");
+		result = 0;
+		break;
+	}
+	return (result);
+}
+
+/*
+ * Perform a binary operation on a variable and an integer
+ */
+int
+var_assign_op_var_int(char *name, int optype, char *src1, fbint_t src2)
+{
+	var_t *var;
+	var_t *var_src1;
+
+	if ((var_src1 = var_find(src1+1)) == NULL)
+		return (FILEBENCH_ERROR);
+
+	if ((var = var_find_alloc(name)) == NULL)
+		return (FILEBENCH_ERROR);
+
+	if ((var->var_type & VAR_TYPE_MASK) == VAR_TYPE_RANDOM) {
+		filebench_log(LOG_ERROR,
+		    "Cannot assign integer to random variable %s", name);
+		return (FILEBENCH_ERROR);
+	}
+
+	VAR_SET_BINOP_INDVAR(var, var_src1, optype);
+
+	var->var_val.integer = src2;
+
+	return (FILEBENCH_OK);
+}
+
+int
+var_assign_op_var_var(char *name, int optype, char *src1, char *src2)
+{
+	var_t *var;
+	var_t *var_src1;
+	var_t *var_src2;
+
+	if ((var_src1 = var_find(src1+1)) == NULL)
+		return (FILEBENCH_ERROR);
+
+	if ((var_src2 = var_find(src2+1)) == NULL)
+		return (FILEBENCH_ERROR);
+
+	if ((var = var_find_alloc(name)) == NULL)
+		return (FILEBENCH_ERROR);
+
+	if ((var->var_type & VAR_TYPE_MASK) == VAR_TYPE_RANDOM) {
+		filebench_log(LOG_ERROR,
+		    "Cannot assign integer to random variable %s", name);
+		return (FILEBENCH_ERROR);
+	}
+
+	VAR_SET_BINOP_INDVAR(var, var_src1, optype);
+
+	var->var_val.varptr2 = var_src2;
+
+	return (FILEBENCH_OK);
+}
+
+/*
  * Find a variable, and set it to random type.
  * If it does not have a random extension, allocate one
  */
@@ -853,27 +1003,12 @@
 	return (avd_alloc_var_ptr(var));
 }
 
-
 /*
- * Searches for the named var, and if found copies the var_val.string,
- * if it exists, a decimal number string representation of
- * var_val.integer, the state of var_val.boolean, or the type of random
- * distribution employed, into a malloc'd bit of memory using fb_stralloc().
- * Returns a pointer to the created string, or NULL on failure.
+ * Converts the contents of a var to a string
  */
-char *
-var_to_string(char *name)
+static char *
+var_get_string(var_t *var)
 {
-	var_t *var;
-	char tmp[128];
-
-	name += 1;
-
-	if ((var = var_find(name)) == NULL)
-		var = var_find_dynamic(name);
-
-	if (var == NULL)
-		return (NULL);
 
 	if ((var->var_type & VAR_TYPE_MASK) == VAR_TYPE_RANDOM) {
 		switch (var->var_val.randptr->rnd_type & RAND_TYPE_MASK) {
@@ -899,15 +1034,142 @@
 	}
 
 	if (VAR_HAS_INTEGER(var)) {
+		char tmp[128];
+
 		(void) snprintf(tmp, sizeof (tmp), "%llu",
 		    (u_longlong_t)var->var_val.integer);
 		return (fb_stralloc(tmp));
 	}
 
+	if (VAR_HAS_INDVAR(var)) {
+		var_t *ivp;
+
+		if ((ivp = var->var_varptr1) != NULL) {
+			return (var_get_string(ivp));
+		}
+	}
+
+	if (VAR_HAS_BINOP(var)) {
+		char tmp[128];
+
+		(void) snprintf(tmp, sizeof (tmp), "%llu",
+		    var_binary_integer_op(var));
+		return (fb_stralloc(tmp));
+	}
+
 	return (fb_stralloc("No default"));
 }
 
 /*
+ * Searches for the named var, and if found copies the var_val.string,
+ * if it exists, a decimal number string representation of
+ * var_val.integer, the state of var_val.boolean, or the type of random
+ * distribution employed, into a malloc'd bit of memory using fb_stralloc().
+ * Returns a pointer to the created string, or NULL on failure.
+ */
+char *
+var_to_string(char *name)
+{
+	var_t *var;
+
+	name += 1;
+
+	if ((var = var_find(name)) == NULL)
+		var = var_find_dynamic(name);
+
+	if (var == NULL)
+		return (NULL);
+
+	return (var_get_string(var));
+}
+
+/*
+ * Returns the boolean from the supplied var_t "var".
+ */
+static boolean_t
+var_get_bool(var_t *var)
+{
+	if (var == NULL)
+		return (0);
+
+	if (VAR_HAS_BOOLEAN(var))
+		return (var->var_val.boolean);
+
+	if (VAR_HAS_INTEGER(var)) {
+		if (var->var_val.integer == 0)
+			return (FALSE);
+		else
+			return (TRUE);
+	}
+
+	filebench_log(LOG_ERROR,
+	    "Attempt to get boolean from %s var $%s",
+	    var_get_type_string(var), var->var_name);
+	return (FALSE);
+}
+
+/*
+ * Returns the fbint_t from the supplied var_t "var".
+ */
+static fbint_t
+var_get_int(var_t *var)
+{
+	randdist_t *rndp;
+
+	if (var == NULL)
+		return (0);
+
+	if (VAR_HAS_INTEGER(var))
+		return (var->var_val.integer);
+
+	if (VAR_HAS_RANDDIST(var)) {
+		if ((rndp = var->var_val.randptr) != NULL)
+			return ((fbint_t)rndp->rnd_get(rndp));
+	}
+
+	if (VAR_HAS_BINOP(var))
+		return (var_binary_integer_op(var));
+
+	filebench_log(LOG_ERROR,
+	    "Attempt to get integer from %s var $%s",
+	    var_get_type_string(var), var->var_name);
+	return (0);
+}
+
+/*
+ * Returns the floating point value of a variable pointed to by the
+ * supplied var_t "var". Intended to get the actual (double) value
+ * supplied by the random variable.
+ */
+static double
+var_get_dbl(var_t *var)
+{
+	randdist_t *rndp;
+
+	if (var == NULL)
+		return (0.0);
+
+	if (VAR_HAS_INTEGER(var))
+		return ((double)var->var_val.integer);
+
+	if (VAR_HAS_DOUBLE(var))
+		return (var->var_val.dbl_flt);
+
+	if (VAR_HAS_RANDDIST(var)) {
+		if ((rndp = var->var_val.randptr) != NULL)
+			return (rndp->rnd_get(rndp));
+	}
+
+	if (VAR_HAS_BINOP(var))
+		return (var_binary_dbl_flt_op(var));
+
+	filebench_log(LOG_ERROR,
+	    "Attempt to get double float from %s var $%s",
+	    var_get_type_string(var), var->var_name);
+	return (0.0);
+}
+
+/*
  * Searches for the named var, and if found returns the value,
  * of var_val.boolean. If the var is not found, or a boolean
  * value has not been set, logs an error and returns 0.
@@ -922,13 +1184,13 @@
 	if ((var = var_find(name)) == NULL)
 		var = var_find_dynamic(name);
 
-	if ((var != NULL) && VAR_HAS_BOOLEAN(var))
-		return (var->var_val.boolean);
+	if (var != NULL)
+		return (var_get_bool(var));
 
 	filebench_log(LOG_ERROR,
 	    "Variable %s referenced before set", name);
 
-	return (0);
+	return (FALSE);
 }
 
 /*
@@ -946,8 +1208,8 @@
 	if ((var = var_find(name)) == NULL)
 		var = var_find_dynamic(name);
 
-	if ((var != NULL) && VAR_HAS_INTEGER(var))
-		return (var->var_val.integer);
+	if (var != NULL)
+		return (var_get_int(var));
 
 	filebench_log(LOG_ERROR,
 	    "Variable %s referenced before set", name);
@@ -956,6 +1218,30 @@
 }
 
 /*
+ * Searches for the named var, and if found returns the value,
+ * of var_val.dbl_flt. If the var is not found, or the
+ * floating value has not been set, logs an error and returns 0.0.
+ */
+double
+var_to_double(char *name)
+{
+	var_t *var;
+
+	name += 1;
+
+	if ((var = var_find(name)) == NULL)
+		var = var_find_dynamic(name);
+
+	if (var != NULL)
+		return (var_get_dbl(var));
+
+	filebench_log(LOG_ERROR,
+	    "Variable %s referenced before set", name);
+
+	return (0.0);
+}
+
+/*
  * Searches for the named random var, and if found, converts the
  * requested parameter into a string or a decimal number string
  * representation, into a malloc'd bit of memory using fb_stralloc().
@@ -1074,7 +1360,7 @@
 	}
 
 	if (VAR_HAS_INDVAR(src_var)) {
-		VAR_SET_INDVAR(dst_var, src_var->var_val.varptr);
+		VAR_SET_INDVAR(dst_var, src_var->var_varptr1);
 		filebench_log(LOG_DEBUG_SCRIPT,
 		    "Assign var %s to var %s", dst_var->var_name,
 		    src_var->var_name);
@@ -1586,7 +1872,7 @@
 	if (VAR_HAS_INDVAR(proto_lvar)) {
 		var_t *uplvp;
 
-		uplvp = (var_t *)proto_lvar->var_val.varptr;
+		uplvp = (var_t *)proto_lvar->var_varptr1;
 
 		/* search for more current uplvar on comp master list */
 		if (mstr_lvars) {
@@ -1596,6 +1882,6 @@
 		}
 
 		if (VAR_HAS_INDVAR(uplvp))
-			VAR_SET_INDVAR(newlvar, uplvp->var_val.varptr);
+			VAR_SET_INDVAR(newlvar, uplvp->var_varptr1);
 	}
 }
--- a/usr/src/cmd/filebench/common/vars.h	Fri Jun 05 10:27:16 2009 -0400
+++ b/usr/src/cmd/filebench/common/vars.h	Fri Jun 05 10:28:40 2009 -0400
@@ -19,7 +19,7 @@
  * CDDL HEADER END
  */
 /*
- * Copyright 2008 Sun Microsystems, Inc.  All rights reserved.
+ * Copyright 2009 Sun Microsystems, Inc.  All rights reserved.
  * Use is subject to license terms.
  */
 
@@ -90,22 +90,64 @@
 		double		dbl_flt;
 		char		*string;
 		struct randdist *randptr;
-		struct var	*varptr;
+		struct var	*varptr2;
 	} var_val;
+	struct var	*var_varptr1;
 } var_t;
 
-#define	VAR_TYPE_GLOBAL		0x00	/* global variable */
-#define	VAR_TYPE_DYNAMIC	0x01	/* Dynamic variable */
-#define	VAR_TYPE_RANDOM		0x02	/* random variable */
-#define	VAR_TYPE_LOCAL		0x03	/* Local variable */
-#define	VAR_TYPE_MASK		0x0f
-#define	VAR_TYPE_BOOL_SET	0x10	/* var contains a boolean */
-#define	VAR_TYPE_INT_SET	0x20	/* var contains an integer */
-#define	VAR_TYPE_STR_SET	0x30	/* var contains a string */
-#define	VAR_TYPE_DBL_SET	0x40	/* var contains a double */
-#define	VAR_TYPE_RAND_SET	0x50	/* var contains a randdist pointer */
-#define	VAR_TYPE_INDVAR_SET	0x60    /* var points to another local var */
-#define	VAR_TYPE_SET_MASK	0xf0
+/* basic var types */
+#define	VAR_TYPE_GLOBAL		0x0000	/* global variable */
+#define	VAR_TYPE_DYNAMIC	0x1000	/* Dynamic variable */
+#define	VAR_TYPE_RANDOM		0x2000	/* random variable */
+#define	VAR_TYPE_LOCAL		0x3000	/* Local variable */
+#define	VAR_TYPE_MASK		0xf000
+
+/* various var subtypes that a var can be set to */
+#define	VAR_TYPE_BOOL_SET	0x0100	/* var contains a boolean */
+#define	VAR_TYPE_INT_SET	0x0200	/* var contains an integer */
+#define	VAR_TYPE_STR_SET	0x0300	/* var contains a string */
+#define	VAR_TYPE_DBL_SET	0x0400	/* var contains a double */
+#define	VAR_TYPE_RAND_SET	0x0500	/* var contains a randdist pointer */
+#define	VAR_TYPE_INDVAR_SET	0x0700	/* var points to another variable(s) */
+#define	VAR_TYPE_SET_MASK	0x0f00
+
+/* indirection to another variable or variables with binary op */
+#define	VAR_IND_ASSIGN		0x0000	/* just assignment to another var */
+#define	VAR_IND_BINOP_INT	0x0010	/* binary op with an integer */
+#define	VAR_IND_BINOP_DBL	0x0020	/* binary op with a double float */
+#define	VAR_IND_BINOP_VAR	0x0030	/* binary op with another var */
+#define	VAR_INDBINOP_MASK	0x00f0
+
+
+#define	VAR_IND_VAR_SUM_VC	0x0001	/* var sums var | cnst and *varptr1 */
+#define	VAR_IND_VAR_DIF_VC	0x0002	/* var subs var | cnst and *varptr1 */
+#define	VAR_IND_C_DIF_VAR	0x0003	/* var subs *varptr1 and constant */
+#define	VAR_IND_VAR_MUL_VC	0x0005	/* var muls var | cnst and *varptr1 */
+#define	VAR_IND_VAR_DIV_VC	0x0006	/* var divs var | cnst by *varptr1 */
+#define	VAR_IND_C_DIV_VAR	0x0007	/* var divs *varptr1 by constant */
+#define	VAR_INDVAR_MASK		0x000f
+
+/* Binary ops between an integer and a variable */
+#define	VAR_IND_INT_SUM_IV	(VAR_IND_BINOP_INT | VAR_IND_VAR_SUM_VC)
+#define	VAR_IND_IV_DIF_INT	(VAR_IND_BINOP_INT | VAR_IND_VAR_DIF_VC)
+#define	VAR_IND_INT_DIF_IV	(VAR_IND_BINOP_INT | VAR_IND_C_DIF_VAR)
+#define	VAR_IND_INT_MUL_IV	(VAR_IND_BINOP_INT | VAR_IND_VAR_MUL_VC)
+#define	VAR_IND_IV_DIV_INT	(VAR_IND_BINOP_INT | VAR_IND_VAR_DIV_VC)
+#define	VAR_IND_INT_DIV_IV	(VAR_IND_BINOP_INT | VAR_IND_C_DIV_VAR)
+
+/* Binary ops between a double float and a variable */
+#define	VAR_IND_DBL_SUM_IV	(VAR_IND_BINOP_DBL | VAR_IND_VAR_SUM_VC)
+#define	VAR_IND_IV_DIF_DBL	(VAR_IND_BINOP_DBL | VAR_IND_VAR_DIF_VC)
+#define	VAR_IND_DBL_DIF_IV	(VAR_IND_BINOP_DBL | VAR_IND_C_DIF_VAR)
+#define	VAR_IND_DBL_MUL_IV	(VAR_IND_BINOP_DBL | VAR_IND_VAR_MUL_VC)
+#define	VAR_IND_IV_DIV_DBL	(VAR_IND_BINOP_DBL | VAR_IND_VAR_DIV_VC)
+#define	VAR_IND_DBL_DIV_IV	(VAR_IND_BINOP_DBL | VAR_IND_C_DIV_VAR)
+
+/* Binary ops between two variables: varptr2 op varptr1 */
+#define	VAR_IND_IV_SUM_IV	(VAR_IND_BINOP_VAR | VAR_IND_VAR_SUM_VC)
+#define	VAR_IND_IV_DIF_IV	(VAR_IND_BINOP_VAR | VAR_IND_VAR_DIF_VC)
+#define	VAR_IND_IV_MUL_IV	(VAR_IND_BINOP_VAR | VAR_IND_VAR_MUL_VC)
+#define	VAR_IND_IV_DIV_IV	(VAR_IND_BINOP_VAR | VAR_IND_VAR_DIV_VC)
 
 #define	VAR_HAS_BOOLEAN(vp) \
 	(((vp)->var_type & VAR_TYPE_SET_MASK) == VAR_TYPE_BOOL_SET)
@@ -123,7 +165,12 @@
 	(((vp)->var_type & VAR_TYPE_SET_MASK) == VAR_TYPE_RAND_SET)
 
 #define	VAR_HAS_INDVAR(vp) \
-	(((vp)->var_type & VAR_TYPE_SET_MASK) == VAR_TYPE_INDVAR_SET)
+	((((vp)->var_type & VAR_TYPE_SET_MASK) == VAR_TYPE_INDVAR_SET) && \
+	(((vp)->var_type & VAR_INDBINOP_MASK) == VAR_IND_ASSIGN))
+
+#define	VAR_HAS_BINOP(vp) \
+	((((vp)->var_type & VAR_TYPE_SET_MASK) == VAR_TYPE_INDVAR_SET) && \
+	(((vp)->var_type & VAR_INDBINOP_MASK) != VAR_IND_ASSIGN))
 
 #define	VAR_SET_BOOL(vp, val)	\
 	{			\
@@ -165,12 +212,22 @@
 
 #define	VAR_SET_INDVAR(vp, val)	\
 	{			\
-		(vp)->var_val.varptr = (val); \
+		(vp)->var_varptr1 = (val); \
 		(vp)->var_type = \
-		    (((vp)->var_type & (~VAR_TYPE_SET_MASK)) | \
+		    (((vp)->var_type & (~(VAR_TYPE_SET_MASK | \
+		    VAR_INDVAR_MASK))) | \
 		    VAR_TYPE_INDVAR_SET); \
 	}
 
+#define	VAR_SET_BINOP_INDVAR(vp, val, st)	\
+	{			\
+		(vp)->var_varptr1 = (val); \
+		(vp)->var_type = \
+		    (((vp)->var_type & (~(VAR_TYPE_SET_MASK | \
+		    VAR_INDVAR_MASK))) | \
+		    (VAR_TYPE_INDVAR_SET | st)); \
+	}
+
 avd_t avd_bool_alloc(boolean_t bool);
 avd_t avd_int_alloc(fbint_t integer);
 avd_t avd_str_alloc(char *string);
@@ -185,12 +242,15 @@
 int var_assign_double(char *name, double dbl);
 int var_assign_string(char *name, char *string);
 int var_assign_var(char *name, char *string);
+int var_assign_op_var_int(char *name, int optype, char *src1, fbint_t src2);
+int var_assign_op_var_var(char *name, int optype, char *src1, char *src2);
 void var_update_comp_lvars(var_t *newlvar, var_t *proto_comp_vars,
     var_t *mstr_lvars);
 var_t *var_define_randvar(char *name);
 var_t *var_find_randvar(char *name);
 boolean_t var_to_boolean(char *name);
 fbint_t var_to_integer(char *name);
+double var_to_double(char *name);
 var_t *var_lvar_alloc_local(char *name);
 var_t *var_lvar_assign_boolean(char *name, boolean_t);
 var_t *var_lvar_assign_integer(char *name, fbint_t);
--- a/usr/src/cmd/filebench/config/newfeatures.prof	Fri Jun 05 10:27:16 2009 -0400
+++ b/usr/src/cmd/filebench/config/newfeatures.prof	Fri Jun 05 10:28:40 2009 -0400
@@ -42,6 +42,12 @@
         personality = filemicro_seqwriterandvartab;
 }
 
+CONFIG rate_limit_copy {
+	function = generic;
+	personality = ratelimcopyfiles;
+	eventrate = 20;
+}
+
 CONFIG list_dirs_test {
 	function = generic;
 	personality = listdirs;
@@ -69,7 +75,7 @@
 	numactivevids = 4;
 	numpassivevids = 20;
 	nthreads = 6;
-	srvbwrate = 12;
+	eventrate = 12;
 	repintval = 20;
 	passvidsname = bigfileset;
 	actvidsname = u2fileset;
@@ -82,6 +88,12 @@
 	warmuptime = 120;
 }
 
+CONFIG network_file_system {
+        function = generic;
+        personality = networkfs;
+	warmuptime = 120;
+}
+
 CONFIG composite_flowop_test {
         function = generic;
         personality = compflow_demo;
--- a/usr/src/cmd/filebench/config/videoserver.prof	Fri Jun 05 10:27:16 2009 -0400
+++ b/usr/src/cmd/filebench/config/videoserver.prof	Fri Jun 05 10:28:40 2009 -0400
@@ -19,7 +19,7 @@
 # CDDL HEADER END
 #
 #
-# Copyright 2008 Sun Microsystems, Inc.  All rights reserved.
+# Copyright 2009 Sun Microsystems, Inc.  All rights reserved.
 # Use is subject to license terms.
 #
 
@@ -38,6 +38,6 @@
 	numactivevids = 8;
 	numpassivevids = 40;
 	nthreads = 16;
-	srvbwrate = 32;
+	eventrate = 32;
 	repintval = 60;
 }
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/usr/src/cmd/filebench/workloads/networkfs.f	Fri Jun 05 10:28:40 2009 -0400
@@ -0,0 +1,131 @@
+#
+# 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
+# or http://www.opensolaris.org/os/licensing.
+# 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 2009 Sun Microsystems, Inc.  All rights reserved.
+# Use is subject to license terms.
+#
+# $dir - directory for datafiles
+# $eventrate - event generator rate (0 == free run)
+# $nfiles - number of data files
+# $nthreads - number of worker threads
+
+set $dir=/tmp
+set $cached=false
+set $eventrate=10
+set $meandirwidth=20
+set $nthreads=1
+set $nfiles=10000
+set $sync=false
+set $totalfiles=$nfiles * $eventrate
+
+eventgen rate=$eventrate
+
+define randvar name=$wrtiosize, type=tabular, min=1k, round=1k, randtable =
+{{ 0,   1k,    7k},
+ {50,   9k,   15k},
+ {14,  17k,   23k},
+ {14,  33k,   39k},
+ {12,  65k,   71k},
+ {10, 129k,  135k}
+}
+
+define randvar name=$rdiosize, type=tabular, min=8k, round=1k, randtable =
+{{85,   8k,   8k},
+ { 8,  17k,  23k},
+ { 4,  33k,  39k},
+ { 2,  65k,  71k},
+ { 1, 129k, 135k}
+}
+
+define randvar name=$filesize, type=tabular, min=1k, round=1k, randtable =
+{{33,   1k,    1k},
+ {21,   1k,    3k},
+ {13,   3k,    5k},
+ {10,   5k,   11k},
+ {08,  11k,   21k},
+ {05,  21k,   43k},
+ {04,  43k,   85k},
+ {03,  85k,  171k},
+ {02, 171k,  341k},
+ {01, 341k, 1707k}
+}
+
+define randvar name=$fileidx, type=gamma, min=0, gamma=100
+
+define fileset name=bigfileset,path=$dir,size=$filesize,entries=$totalfiles,dirwidth=$meandirwidth,prealloc=60,cached=$cached
+
+define flowop name=rmw, $filesetrmw
+{
+  flowop openfile name=openfile1,filesetname=$filesetrmw,indexed=$fileidx,fd=1
+  flowop readwholefile name=readfile1,iosize=$rdiosize,fd=1
+  flowop createfile name=newfile2,filesetname=$filesetrmw,indexed=$fileidx,fd=2
+  flowop writewholefile name=writefile2,fd=2,iosize=$wrtiosize,srcfd=1
+  flowop closefile name=closefile1,fd=1
+  flowop closefile name=closefile2,fd=2
+  flowop deletefile name=deletefile1,fd=1
+}
+
+define flowop name=launch, $filesetlch
+{
+  flowop openfile name=openfile3,filesetname=$filesetlch,indexed=$fileidx,fd=3
+  flowop readwholefile name=readfile3,iosize=$rdiosize,fd=3
+  flowop openfile name=openfile4,filesetname=$filesetlch,indexed=$fileidx,fd=4
+  flowop readwholefile name=readfile4,iosize=$rdiosize,fd=4
+  flowop closefile name=closefile3,fd=3
+  flowop openfile name=openfile5,filesetname=$filesetlch,indexed=$fileidx,fd=5
+  flowop readwholefile name=readfile5,iosize=$rdiosize,fd=5
+  flowop closefile name=closefile4,fd=4
+  flowop closefile name=closefile5,fd=5
+}
+
+define flowop name=appnd, $filesetapd
+{
+  flowop openfile name=openfile6,filesetname=$filesetapd,indexed=$fileidx,fd=6
+  flowop appendfilerand name=appendfilerand6,iosize=$wrtiosize,fd=6
+  flowop closefile name=closefile6,fd=6
+}
+
+define process name=netclient,instances=1
+{
+  thread name=fileuser,memsize=10m,instances=$nthreads
+  {
+    flowop launch name=launch1, iters=1, $filesetlch=bigfileset
+    flowop rmw name=rmw1, iters=6, $filesetrmw=bigfileset
+    flowop appnd name=appnd1, iters=3, $filesetapd=bigfileset
+    flowop statfile name=statfile1,filesetname=bigfileset,indexed=$fileidx
+    flowop eventlimit name=ratecontrol
+  }
+}
+
+echo  "NetworkFileServer Version 1.0 personality successfully loaded"
+usage "Usage: set \$dir=<dir>            defaults to $dir"
+usage "       set \$cached=<bool>        defaults to $cached"
+usage "       set \$eventrate=<value>    defaults to $eventrate"
+usage "       set \$wrtiosize.type=<type>   defaults to $wrtiosize.type"
+usage "       set \$wrtiosize.randsrc=<src> defaults to $wrtiosize.randsrc"
+usage "       set \$rdiosize.type=<type>   defaults to $rdiosize.type"
+usage "       set \$rdiosize.randsrc=<src> defaults to $rdiosize.randsrc"
+usage "       set \$filesize.type=<type>   defaults to $filesize.type"
+usage "       set \$filesize.randsrc=<src> defaults to $filesize.randsrc"
+usage "       set \$nfiles=<value>       defaults to $nfiles"
+usage "       set \$nthreads=<value>     defaults to $nthreads"
+usage "       set \$sync=<bool>          defaults to $sync"
+usage " "
+usage "       run runtime (e.g. run 60)"
--- a/usr/src/cmd/filebench/workloads/oltp.f	Fri Jun 05 10:27:16 2009 -0400
+++ b/usr/src/cmd/filebench/workloads/oltp.f	Fri Jun 05 10:28:40 2009 -0400
@@ -19,17 +19,19 @@
 # CDDL HEADER END
 #
 #
-# Copyright 2008 Sun Microsystems, Inc.  All rights reserved.
+# Copyright 2009 Sun Microsystems, Inc.  All rights reserved.
 # Use is subject to license terms.
 #
-# ident	"%Z%%M%	%I%	%E% SMI"
-
+# $dir - directory for datafiles
+# $eventrate - event generator rate (0 == free run)
 # $iosize - iosize for database block access
-# $dir - directory for datafiles
 # $nshadows - number of shadow processes
 # $ndbwriters - number of database writers
-#
+# $nfiles - number of data files
+# $nlogfiles - number of log files
+
 set $dir=/tmp
+set $eventrate=0
 set $runtime=30
 set $iosize=2k
 set $nshadows=200
@@ -44,6 +46,8 @@
 set $nlogfiles=1
 set $directio=0
 
+eventgen rate = $eventrate
+
 # Define a datafile and logfile
 define fileset name=datafiles,path=$dir,size=$filesize,filesizegamma=0,entries=$nfiles,dirwidth=1024,prealloc=100,cached=$cached,reuse
 define fileset name=logfile,path=$dir,size=$logfilesize,filesizegamma=0,entries=$nlogfiles,dirwidth=1024,prealloc=100,cached=$cached,reuse
@@ -86,8 +90,10 @@
   }
 }
 
-echo "OLTP Version 2.2 personality successfully loaded"
-usage "Usage: set \$dir=<dir>"
+echo "OLTP Version 2.3 personality successfully loaded"
+usage "Usage: set \$dir=<dir>         defaults to $dir"
+usage " "
+usage "       set \$eventrate=<value> defaults to $eventrate"
 usage " "
 usage "       set \$filesize=<size>   defaults to $filesize, n.b. there are ten files of this size"
 usage " "
--- a/usr/src/cmd/filebench/workloads/randomfileaccess.f	Fri Jun 05 10:27:16 2009 -0400
+++ b/usr/src/cmd/filebench/workloads/randomfileaccess.f	Fri Jun 05 10:28:40 2009 -0400
@@ -18,17 +18,17 @@
 #
 # CDDL HEADER END
 #
-# Copyright 2008 Sun Microsystems, Inc.  All rights reserved.
+# Copyright 2009 Sun Microsystems, Inc.  All rights reserved.
 # Use is subject to license terms.
 #
-# 
+# Exercises the indexed attribute of the fileset_pick() function. 
 # 
 
 set $dir=/tmp
 set $cached=false
 set $meandirwidth=20
 set $nthreads=5
-set $nfiles=100000
+set $nfiles=10000
 set $sync=false
 
 define randvar name=$wrtiosize, min=512, round=512, type=gamma, mean=16k
@@ -53,7 +53,7 @@
 
 define randvar name=$fileidx, type=gamma, min=0, gamma=100
 
-define fileset name=bigfileset,path=$dir,size=$filesize,entries=$nfiles,dirwidth=$meandirwidth,prealloc=60,cached=$cached
+define fileset name=bigfileset,path=$dir,size=$filesize,entries=$nfiles,dirwidth=$meandirwidth,prealloc=100,cached=$cached
 
 define process name=netclient,instances=1
 {
@@ -67,16 +67,11 @@
     flowop readwholefile name=readfile1,iosize=$rdiosize,fd=2
     flowop readwholefile name=readfile2,iosize=$rdiosize,fd=3
     flowop closefile name=closefile2,fd=2
-    flowop createfile name=newfile1,filesetname=bigfileset,indexed=$fileidx,fd=1
-    flowop writewholefile name=writefile1,iosize=$wrtiosize,srcfd=1,fd=1
     flowop closefile name=closefile3,fd=3
-    flowop deletefile name=deletefile1,filesetname=bigfileset,fd=2
-    flowop statfile name=statfile1,filesetname=bigfileset,fd=1
-    flowop closefile name=closefile4,fd=1
   }
 }
 
-echo  "NetworkServer Version 1.0 personality successfully loaded"
+echo  "NetworkServer Version 1.1 personality successfully loaded"
 usage "Usage: set \$dir=<dir>            defaults to $dir"
 usage "       set \$cached=<bool>        defaults to $cached"
 usage "       set \$wrtiosize.type=<type>   defaults to $wrtiosize.type"
--- a/usr/src/cmd/filebench/workloads/randomread.f	Fri Jun 05 10:27:16 2009 -0400
+++ b/usr/src/cmd/filebench/workloads/randomread.f	Fri Jun 05 10:28:40 2009 -0400
@@ -19,18 +19,25 @@
 # CDDL HEADER END
 #
 #
-# Copyright 2007 Sun Microsystems, Inc.  All rights reserved.
+# Copyright 2009 Sun Microsystems, Inc.  All rights reserved.
 # Use is subject to license terms.
 #
-# ident	"%Z%%M%	%I%	%E% SMI"
+# $dir - directory for datafiles
+# $eventrate - event generator rate (0 == free run)
+# $filesize - size of data file
+# $iosize - size of each read
+# $nthreads - number of worker threads
 
 set $dir=/tmp
-set $nthreads=1
+set $eventrate=0
+set $filesize=1m
 set $iosize=8k
-set $filesize=1m
+set $nthreads=1
 set $workingset=0
 set $directio=0
 
+eventgen rate=$eventrate
+
 define file name=largefile1,path=$dir,size=$filesize,prealloc,reuse,paralloc
 
 define process name=rand-read,instances=1
@@ -42,8 +49,9 @@
   }
 }
 
-echo "Random Read Version 2.0 IO personality successfully loaded"
-usage "Usage: set \$dir=<dir>"
+echo "Random Read Version 2.1 IO personality successfully loaded"
+usage "Usage: set \$dir=<dir>         defaults to $dir"
+usage "       set \$eventrate=<value> defaults to $eventrate"
 usage "       set \$filesize=<size>   defaults to $filesize"
 usage "       set \$iosize=<value>    defaults to $iosize"
 usage "       set \$nthreads=<value>  defaults to $nthreads"
--- a/usr/src/cmd/filebench/workloads/randomrw.f	Fri Jun 05 10:27:16 2009 -0400
+++ b/usr/src/cmd/filebench/workloads/randomrw.f	Fri Jun 05 10:28:40 2009 -0400
@@ -19,18 +19,25 @@
 # CDDL HEADER END
 #
 #
-# Copyright 2007 Sun Microsystems, Inc.  All rights reserved.
+# Copyright 2009 Sun Microsystems, Inc.  All rights reserved.
 # Use is subject to license terms.
 #
-# ident	"%Z%%M%	%I%	%E% SMI"
+# $dir - directory for datafiles
+# $eventrate - event generator rate (0 == free run)
+# $filesize - size of data file
+# $iosize - size of each I/O request
+# $nthreads - number of worker threads
 
 set $dir=/tmp
-set $nthreads=1
+set $eventrate=0
+set $filesize=1m
 set $iosize=8k
-set $filesize=1m
+set $nthreads=1
 set $workingset=0
 set $directio=0
 
+eventgen rate=$eventrate
+
 define file name=largefile1,path=$dir,size=$filesize,prealloc,reuse,paralloc
 
 define process name=rand-rw,instances=1
@@ -38,17 +45,18 @@
   thread name=rand-r-thread,memsize=5m,instances=$nthreads
   {
     flowop read name=rand-read1,filename=largefile1,iosize=$iosize,random,workingset=$workingset,directio=$directio
-    flowop eventlimit name=rand-rate
+    flowop eventlimit name=rand-rdrate
   }
   thread name=rand-w-thread,memsize=5m,instances=$nthreads
   {
     flowop write name=rand-write1,filename=largefile1,iosize=$iosize,random,workingset=$workingset,directio=$directio
-    flowop eventlimit name=rand-rate
+    flowop eventlimit name=rand-wtrate
   }
 }
 
-echo "Random RW Version 2.0 IO personality successfully loaded"
-usage "Usage: set \$dir=<dir>"
+echo "Random RW Version 2.1 IO personality successfully loaded"
+usage "Usage: set \$dir=<dir>         defaults to $dir"
+usage "       set \$eventrate=<value> defaults to $eventrate"
 usage "       set \$filesize=<size>   defaults to $filesize"
 usage "       set \$iosize=<value>    defaults to $iosize"
 usage "       set \$nthreads=<value>  defaults to $nthreads"
--- a/usr/src/cmd/filebench/workloads/randomwrite.f	Fri Jun 05 10:27:16 2009 -0400
+++ b/usr/src/cmd/filebench/workloads/randomwrite.f	Fri Jun 05 10:28:40 2009 -0400
@@ -19,18 +19,25 @@
 # CDDL HEADER END
 #
 #
-# Copyright 2007 Sun Microsystems, Inc.  All rights reserved.
+# Copyright 2009 Sun Microsystems, Inc.  All rights reserved.
 # Use is subject to license terms.
 #
-# ident	"%Z%%M%	%I%	%E% SMI"
+# $dir - directory for datafiles
+# $eventrate - event generator rate (0 == free run)
+# $filesize - size of data file
+# $iosize - size of each I/O request
+# $nthreads - number of worker threads
 
 set $dir=/tmp
-set $nthreads=1
+set $eventrate=0
+set $filesize=1m
 set $iosize=8k
-set $filesize=1m
+set $nthreads=1
 set $workingset=0
 set $directio=0
 
+eventgen rate=$eventrate
+
 define file name=largefile1,path=$dir,size=$filesize,prealloc,reuse,paralloc
 
 define process name=rand-write,instances=1
@@ -42,8 +49,9 @@
   }
 }
 
-echo "Random Write Version 2.0 IO personality successfully loaded"
-usage "Usage: set \$dir=<dir>"
+echo "Random Write Version 2.1 IO personality successfully loaded"
+usage "Usage: set \$dir=<dir>         defaults to $dir"
+usage "       set \$eventrate=<value> defaults to $eventrate"
 usage "       set \$filesize=<size>   defaults to $filesize"
 usage "       set \$iosize=<value>    defaults to $iosize"
 usage "       set \$nthreads=<value>  defaults to $nthreads"
--- a/usr/src/cmd/filebench/workloads/ratelimcopyfiles.f	Fri Jun 05 10:27:16 2009 -0400
+++ b/usr/src/cmd/filebench/workloads/ratelimcopyfiles.f	Fri Jun 05 10:28:40 2009 -0400
@@ -19,29 +19,37 @@
 # CDDL HEADER END
 #
 #
-# Copyright 2008 Sun Microsystems, Inc.  All rights reserved.
+# Copyright 2009 Sun Microsystems, Inc.  All rights reserved.
 # Use is subject to license terms.
 #
-# ident	"%Z%%M%	%I%	%E% SMI"
 
 # RateLimCopyFiles.f uses the iopslimit flowop with the target attribute
 # set to the writewholefile flowop to limit the rate to one writewholefile
 # operation per event. Without the target attribute set, the limit will
 # be one writewholefile OR readwholefile operation per event, so in effect
 # it will run at half the rate. Without the target attribute, this workload
-# is identical to copyfiles.f. Note that you do have to enable the event
-# generator for any of the rate limiting flowops to take effect, for example
-# by typing:
-#     eventget rate=10
-# at the go_filebench prompt to get ten events per second.
+# is identical to copyfiles.f. Set the event generator rate by setting
+# the $eventrate variable, for instance by typing:
+#     set $eventrate=20
+# at the go_filebench prompt to get twenty events per second.
 #
+
+# $dir - directory for datafiles
+# $eventrate - event generator rate (0 == free run)
+# $filesize - size of data file
+# $iosize - size of each I/O request
+# $nfiles - number of files in the fileset
+# $nthreads - number of worker threads
+
 set $dir=/tmp
+set $eventrate=10
 set $dirwidth=20
 set $filesize=16k
 set $iosize=1m
 set $nfiles=1000
 set $nthreads=1
 
+eventgen rate=$eventrate
 set mode quit firstdone
 
 define fileset name=bigfileset,path=$dir,size=$filesize,entries=$nfiles,dirwidth=$dirwidth,prealloc=100
@@ -61,8 +69,9 @@
   }
 }
 
-echo  "RateLimCopyFiles Version 1.0 personality successfully loaded"
-usage "Usage: set \$dir=<dir>"
+echo  "RateLimCopyFiles Version 1.1 personality successfully loaded"
+usage "Usage: set \$dir=<dir>         defaults to $dir"
+usage "       set \$eventrate=<value> defaults to $eventrate"
 usage "       set \$filesize=<size>   defaults to $filesize"
 usage "       set \$nfiles=<value>    defaults to $nfiles"
 usage "       set \$iosize=<size>     defaults to $iosize"
--- a/usr/src/cmd/filebench/workloads/tpcso.f	Fri Jun 05 10:27:16 2009 -0400
+++ b/usr/src/cmd/filebench/workloads/tpcso.f	Fri Jun 05 10:28:40 2009 -0400
@@ -19,25 +19,27 @@
 # CDDL HEADER END
 #
 #
-# Copyright 2007 Sun Microsystems, Inc.  All rights reserved.
+# Copyright 2009 Sun Microsystems, Inc.  All rights reserved.
 # Use is subject to license terms.
 #
-# ident	"%Z%%M%	%I%	%E% SMI"
 
+# $dir - directory for datafiles
+# $eventrate - event generator rate (0 == free run)
 # $iosize - iosize for database block access
-# $dir - directory for datafiles
 # $nshadows - number of shadow processes
 # $ndbwriters - number of database writers
 
 set $dir=/tmp
-set $runtime=30
+set $eventrate=0
 set $iosize=2k
 set $nshadows=200
 set $ndbwriters=10
+set $runtime=30
 set $usermode=20000
 set $memperthread=1m
 
 debug 1
+eventgen rate=$eventrate
 
 # Define a datafile and logfile
 define file name=aux.df,path=$dir,size=251m,reuse,prealloc,paralloc
@@ -251,8 +253,10 @@
   }
 }
 
-echo "Tpcso Version 2.0 personality successfully loaded"
-usage "Usage: set \$dir=<dir>"
+echo "Tpcso Version 2.1 personality successfully loaded"
+usage "Usage: set \$dir=<dir>         defaults to $dir"
+usage " "
+usage "       set \$eventrate=<value> defaults to $eventrate"
 usage " "
 usage "       set \$iosize=<value>    defaults to $iosize, typically 2k or 8k"
 usage " "
--- a/usr/src/cmd/filebench/workloads/videoserver.f	Fri Jun 05 10:27:16 2009 -0400
+++ b/usr/src/cmd/filebench/workloads/videoserver.f	Fri Jun 05 10:28:40 2009 -0400
@@ -19,7 +19,7 @@
 # CDDL HEADER END
 #
 #
-# Copyright 2008 Sun Microsystems, Inc.  All rights reserved.
+# Copyright 2009 Sun Microsystems, Inc.  All rights reserved.
 # Use is subject to license terms.
 #
 # This workloads emulates a video server. It has two filesets, one of videos
@@ -33,6 +33,7 @@
 # 10 seconds. Thus the write bandwidth will be set as $filesize/$repintval.
 
 set $dir=/tmp
+set $eventrate=96
 set $filesize=10g
 set $nthreads=48
 set $numactivevids=32
@@ -45,9 +46,8 @@
 set $actvidsname=activevids
 #
 set $repintval=10
-set $srvbwrate=96
 
-eventgen rate=$srvbwrate
+eventgen rate=$eventrate
 
 define fileset name=$actvidsname,path=$dir,size=$filesize,entries=$numactivevids,dirwidth=4,prealloc,paralloc,reuse=$reuseit
 define fileset name=$passvidsname,path=$dir,size=$filesize,entries=$numpassivevids,dirwidth=20,prealloc=50,paralloc,reuse=$reuseit
@@ -73,15 +73,15 @@
   }
 }
 
-echo  "Video Server Version 1.0 personality successfully loaded"
+echo  "Video Server Version 1.1 personality successfully loaded"
 usage "Usage: set \$dir=<dir>              defaults to $dir"
+usage "       set \$eventrate=<value>      defaults to $eventrate"
 usage "       set \$filesize=<size>        defaults to $filesize"
 usage "       set \$nthreads=<value>       defaults to $nthreads"
 usage "       set \$writeiosize=<value>    defaults to $writeiosize"
 usage "       set \$readiosize=<value>     defaults to $readiosize"
 usage "       set \$numactivevids=<value>  defaults to $numactivevids"
 usage "       set \$numpassivevids=<value> defaults to $numpassivevids"
-usage "       set \$srvbwrate=<value>      defaults to $srvbwrate"
 usage " "
 usage "       run runtime (e.g. run 60)"
 
--- a/usr/src/cmd/hal/hald/solaris/devinfo_usb.c	Fri Jun 05 10:27:16 2009 -0400
+++ b/usr/src/cmd/hal/hald/solaris/devinfo_usb.c	Fri Jun 05 10:28:40 2009 -0400
@@ -39,6 +39,7 @@
 static HalDevice *devinfo_usb_scsa2usb_add(HalDevice *d, di_node_t node);
 static HalDevice *devinfo_usb_printer_add(HalDevice *usbd, di_node_t node);
 static HalDevice *devinfo_usb_input_add(HalDevice *usbd, di_node_t node);
+static HalDevice *devinfo_usb_video4linux_add(HalDevice *usbd, di_node_t node);
 const gchar *devinfo_printer_prnio_get_prober(HalDevice *d, int *timeout);
 const gchar *devinfo_keyboard_get_prober(HalDevice *d, int *timeout);
 static void set_usb_properties(HalDevice *d, di_node_t node, gchar *devfs_path, char *driver_name);
@@ -92,25 +93,26 @@
 }
 
 static char *
-get_hid_devlink(char *devfs_path)
+get_usb_devlink(char *devfs_path, const char *dir_name)
 {
 	char *result = NULL;
 	DIR *dp;
 
-	if ((dp = opendir("/dev/usb")) != NULL) {
+	if ((dp = opendir(dir_name)) != NULL) {
 		struct dirent *ep;
 
 		while ((ep = readdir(dp)) != NULL) {
 			char path[MAXPATHLEN], lpath[MAXPATHLEN];
 
-			snprintf(path, sizeof (path), "/dev/usb/%s",
-			    ep->d_name);
+			strncpy(path, dir_name, strlen(dir_name));
+			strncat(path, ep->d_name, strlen(ep->d_name));
 			memset(lpath, 0, sizeof (lpath));
 			if ((readlink(path, lpath, sizeof (lpath)) > 0) &&
 			    (strstr(lpath, devfs_path) != NULL)) {
 				result = strdup(path);
 				break;
 			}
+			memset(path, 0, sizeof (path));
 		}
 		closedir(dp);
 	}
@@ -225,6 +227,11 @@
 				di_devlink_fini(&hdl);
 			}
 			nd = devinfo_usb_input_add(d, node);
+		} else if (strcmp(driver_name, "usbvc") == 0) {
+			if (hdl = di_devlink_init(devfs_path, DI_MAKE_LINK)) {
+				di_devlink_fini(&hdl);
+			}
+			nd = devinfo_usb_video4linux_add(d, node);
 		}
 	}
 
@@ -321,7 +328,7 @@
 			if (p == NULL)
 				goto out;
 			*p = '\0';
-			
+
 			if ((tmp_node = di_init (devpath, DINFOCPYALL)) == DI_NODE_NIL)
 				goto out;
 
@@ -448,15 +455,16 @@
 		if (strcmp(di_minor_nodetype(minor), nodetype) == 0) {
 			*devlink = get_devlink(devlink_hdl, re, *minor_path);
 			/*
-			 * During hotplugging, devlink could be NULL for hid
+			 * During hotplugging, devlink could be NULL for usb
 			 * devices due to devlink database has not yet been
 			 * updated when hal try to read from it although the
 			 * actually dev link path has been created. In such a
 			 * situation, we will read the devlink name from
 			 * /dev/usb directory.
 			 */
-			if ((*devlink == NULL) && (strstr(re, "hid") != NULL)) {
-				*devlink = get_hid_devlink(*minor_path);
+			if ((*devlink == NULL) &&
+			    ((strstr(re, "hid") != NULL) || (strstr(re, "video") != NULL))) {
+				*devlink = get_usb_devlink(*minor_path, "/dev/usb/");
 			}
 
 			if (*devlink != NULL) {
@@ -472,6 +480,69 @@
 }
 
 static HalDevice *
+devinfo_usb_video4linux_add(HalDevice *usbd, di_node_t node)
+{
+	HalDevice *d = NULL;
+	int	major;
+	di_minor_t minor;
+	dev_t	devt;
+	char	*devlink = NULL;
+	char	*dev_videolink = NULL;
+	char	*minor_path = NULL;
+	char	*minor_name = NULL;
+	char	udi[HAL_PATH_MAX];
+	char	*s;
+
+	get_dev_link_path(node, "usb_video",
+	    "^usb/video+",  &devlink, &minor_path, &minor_name);
+
+	if ((minor_path == NULL) || (devlink == NULL)) {
+
+		goto out;
+	}
+
+	HAL_DEBUG(("devlink %s, minor_name %s", devlink, minor_name));
+	if (strcmp(minor_name, "usbvc") != 0) {
+
+		goto out;
+	}
+
+	d = hal_device_new();
+
+	devinfo_set_default_properties(d, usbd, node, minor_path);
+	hal_device_property_set_string(d, "info.subsystem", "video4linux");
+	hal_device_property_set_string(d, "info.category", "video4linux");
+
+	hal_device_add_capability(d, "video4linux");
+
+	/* Get logic link under /dev (/dev/video+) */
+	dev_videolink = get_usb_devlink(strstr(devlink, "usb"), "/dev/");
+
+	hal_device_property_set_string(d, "video4linux.device", dev_videolink);
+
+	hal_util_compute_udi(hald_get_gdl(), udi, sizeof (udi),
+	    "%s_video4linux", hal_device_get_udi(usbd));
+
+	hal_device_set_udi(d, udi);
+	hal_device_property_set_string(d, "info.udi", udi);
+	PROP_STR(d, node, s, "usb-product-name", "info.product");
+
+	devinfo_add_enqueue(d, minor_path, &devinfo_usb_handler);
+
+
+out:
+	if (devlink) {
+		free(devlink);
+	}
+
+	if (minor_path) {
+		di_devfs_path_free(minor_path);
+	}
+
+	return (d);
+}
+
+static HalDevice *
 devinfo_usb_input_add(HalDevice *usbd, di_node_t node)
 {
 	HalDevice *d = NULL;
@@ -497,7 +568,7 @@
 
 		goto out;
 	}
-	
+
 	d = hal_device_new();
 
 	devinfo_set_default_properties(d, usbd, node, minor_path);
@@ -520,7 +591,7 @@
 
 	hal_util_compute_udi(hald_get_gdl(), udi, sizeof (udi),
 	    "%s_logicaldev_input", hal_device_get_udi(usbd));
-	    
+
 	hal_device_set_udi(d, udi);
 	hal_device_property_set_string(d, "info.udi", udi);
 
--- a/usr/src/cmd/mdb/Makefile.common	Fri Jun 05 10:27:16 2009 -0400
+++ b/usr/src/cmd/mdb/Makefile.common	Fri Jun 05 10:28:40 2009 -0400
@@ -72,6 +72,7 @@
 	logindmux \
 	mac \
 	md \
+	mr_sas \
 	nca \
 	nsctl \
 	nsmb \
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/usr/src/cmd/mdb/common/modules/mr_sas/mr_sas.c	Fri Jun 05 10:28:40 2009 -0400
@@ -0,0 +1,215 @@
+/*
+ * 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
+ * or http://www.opensolaris.org/os/licensing.
+ * 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 2009 Sun Microsystems, Inc.  All rights reserved.
+ * Use is subject to license terms.
+ */
+
+#include <limits.h>
+#include <sys/mdb_modapi.h>
+#include <sys/sysinfo.h>
+#include <sys/sunmdi.h>
+#include <sys/scsi/scsi.h>
+#include "mr_sas.h"
+
+int
+construct_path(uintptr_t addr, char *result)
+{
+	struct	dev_info	d;
+	char	devi_node[PATH_MAX];
+	char	devi_addr[PATH_MAX];
+
+	if (mdb_vread(&d, sizeof (d), addr) == -1) {
+		mdb_warn("couldn't read dev_info");
+		return (DCMD_ERR);
+	}
+
+	if (d.devi_parent) {
+		construct_path((uintptr_t)d.devi_parent, result);
+		mdb_readstr(devi_node, sizeof (devi_node),
+		    (uintptr_t)d.devi_node_name);
+		mdb_readstr(devi_addr, sizeof (devi_addr),
+		    (uintptr_t)d.devi_addr);
+		mdb_snprintf(result+strlen(result),
+		    PATH_MAX-strlen(result),
+		    "/%s%s%s", devi_node, (*devi_addr ? "@" : ""),
+		    devi_addr);
+	}
+	return (DCMD_OK);
+}
+
+void
+display_targets(struct mrsas_instance m, int verbose)
+{
+	int	tgt;
+	struct mrsas_ld *mr_ldp;
+	char	device_path[PATH_MAX];
+
+	if (verbose) {
+		*device_path = 0;
+		if (construct_path((uintptr_t)m.dip, device_path) != DCMD_OK) {
+			strcpy(device_path, "couldn't determine device path");
+		}
+	}
+
+	mdb_printf("\n");
+	if (verbose)
+		mdb_printf("%s\n", device_path);
+	mdb_printf("dev_type target\n");
+	mdb_printf("----------");
+	mdb_printf("\n");
+	for (tgt = 0; tgt < MRDRV_MAX_LD; tgt++) {
+		mr_ldp = (struct mrsas_ld *)&m.mr_ld_list[tgt];
+		if ((mr_ldp != NULL) && (mr_ldp->dip != NULL) &&
+		    (mr_ldp->lun_type == MRSAS_LD_LUN)) {
+			mdb_printf("sd %d", tgt);
+			mdb_printf("\n");
+		}
+	}
+	mdb_printf("\n");
+}
+
+void
+display_deviceinfo(struct mrsas_instance m)
+{
+	uint16_t vid, did, svid, sid;
+
+	vid = m.vendor_id;
+	did = m.device_id;
+	svid = m.subsysvid;
+	sid = m.subsysid;
+
+	mdb_printf("\n");
+	mdb_printf("vendor_id device_id subsysvid subsysid");
+	mdb_printf("\n");
+	mdb_printf("--------------------------------------");
+	mdb_printf("\n");
+	mdb_printf("    0x%x   0x%x    0x%x    0x%x",
+	    vid, did, svid, sid);
+	mdb_printf("\n");
+}
+
+static int
+mr_sas_dcmd(uintptr_t addr, uint_t flags, int argc, const mdb_arg_t *argv)
+{
+	struct mrsas_instance m;
+
+	int	instance;
+	uint16_t ncmds;
+	uint_t	verbose = FALSE;
+	uint_t	device_info = FALSE;
+	uint_t	target_info = FALSE;
+	int	rv = DCMD_OK;
+	void	*mrsas_state;
+
+	if (!(flags & DCMD_ADDRSPEC)) {
+		mrsas_state = NULL;
+		if (mdb_readvar(&mrsas_state, "mrsas_state") == -1) {
+			mdb_warn("can't read mrsas_state");
+			return (DCMD_ERR);
+		}
+		if (mdb_pwalk_dcmd("genunix`softstate", "mr_sas`mr_sas",
+		    argc, argv, (uintptr_t)mrsas_state) == -1) {
+			mdb_warn("mdb_pwalk_dcmd failed");
+			return (DCMD_ERR);
+		}
+		return (DCMD_OK);
+	}
+
+	if (mdb_getopts(argc, argv,
+	    'd', MDB_OPT_SETBITS, TRUE, &device_info,
+	    't', MDB_OPT_SETBITS, TRUE, &target_info,
+	    'v', MDB_OPT_SETBITS, TRUE, &verbose,
+	    NULL) != argc)
+		return (DCMD_USAGE);
+
+	if (mdb_vread(&m, sizeof (m), addr) == -1) {
+		mdb_warn("couldn't read mrsas_instance struct at 0x%p", addr);
+		return (DCMD_ERR);
+	}
+	instance = m.instance;
+
+	/* cmd slot info */
+	ncmds = m.max_fw_cmds;
+
+	/* processing completed */
+	if (((flags & DCMD_ADDRSPEC) && !(flags & DCMD_LOOP)) ||
+	    (flags & DCMD_LOOPFIRST)) {
+		if ((flags & DCMD_LOOP) && !(flags & DCMD_LOOPFIRST))
+			mdb_printf("\n");
+		mdb_printf("         mrsas_t inst max_fw_cmds intr_type");
+		mdb_printf("\n");
+		mdb_printf("===========================================");
+		mdb_printf("\n");
+	}
+
+	mdb_printf("%16p %4d      %4d    ", addr, instance, ncmds);
+	switch (m.intr_type) {
+		case DDI_INTR_TYPE_MSIX:
+			mdb_printf("MSI-X");
+			break;
+		case DDI_INTR_TYPE_MSI:
+			mdb_printf("MSI");
+			break;
+		case DDI_INTR_TYPE_FIXED:
+			mdb_printf("FIXED");
+			break;
+		default:
+			mdb_printf("INVALD");
+	}
+	mdb_printf("\n");
+
+	if (target_info)
+		display_targets(m, verbose);
+
+	if (device_info)
+		display_deviceinfo(m);
+
+	return (rv);
+}
+
+void
+mr_sas_help(void)
+{
+	mdb_printf("Prints summary information about each mr_sas instance, "
+	    "Without the address of a \"struct mrsas_instance\", prints every "
+	    "instance.\n\n"
+	    "Switches:\n"
+	    "  -t   includes information about targets\n"
+	    "  -d   includes information about the hardware\n"
+	    "  -v   displays extra information for some options\n");
+}
+
+static const mdb_dcmd_t dcmds[] = {
+	{ "mr_sas", "?[-tdv]", "print mr_sas information", mr_sas_dcmd,
+	    mr_sas_help },
+	{ NULL }
+};
+
+static const mdb_modinfo_t modinfo = {
+	MDB_API_VERSION, dcmds, NULL
+};
+
+const mdb_modinfo_t *
+_mdb_init(void)
+{
+	return (&modinfo);
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/usr/src/cmd/mdb/intel/amd64/mr_sas/Makefile	Fri Jun 05 10:28:40 2009 -0400
@@ -0,0 +1,42 @@
+#
+# 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
+# or http://www.opensolaris.org/os/licensing.
+# 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 2009 Sun Microsystems, Inc.  All rights reserved.
+# Use is subject to license terms.
+#
+
+MODULE = mr_sas.so
+MDBTGT = kvm
+
+MODSRCS = mr_sas.c
+
+MRSASBASE = ../../../../../uts/common/io/mr_sas
+
+include ../../../../Makefile.cmd
+include ../../../../Makefile.cmd.64
+include ../../Makefile.amd64
+include ../../../Makefile.module
+
+CPPFLAGS += -I$(SRC)/uts/common
+CPPFLAGS += -I$(SRC)/uts/common/sys/scsi
+CPPFLAGS += -I$(SRC)/uts/common/sys/scsi/adapters
+CPPFLAGS += -I$(MRSASBASE)
+CPPFLAGS += -D KMDB_MODULE
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/usr/src/cmd/mdb/intel/ia32/mr_sas/Makefile	Fri Jun 05 10:28:40 2009 -0400
@@ -0,0 +1,41 @@
+#
+# 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
+# or http://www.opensolaris.org/os/licensing.
+# 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 2009 Sun Microsystems, Inc.  All rights reserved.
+# Use is subject to license terms.
+#
+
+MODULE = mr_sas.so
+MDBTGT = kvm
+
+MODSRCS = mr_sas.c
+
+MRSASBASE = ../../../../../uts/common/io/mr_sas
+
+include ../../../../Makefile.cmd
+include ../../Makefile.ia32
+include ../../../Makefile.module
+
+CPPFLAGS += -I$(SRC)/uts/common
+CPPFLAGS += -I$(SRC)/uts/common/sys/scsi
+CPPFLAGS += -I$(SRC)/uts/common/sys/scsi/adapter
+CPPFLAGS += -I$(MRSASBASE)
+CPPFLAGS += -D KMDB_MODULE
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/usr/src/cmd/mdb/sparc/v9/mr_sas/Makefile	Fri Jun 05 10:28:40 2009 -0400
@@ -0,0 +1,42 @@
+#
+# 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
+# or http://www.opensolaris.org/os/licensing.
+# 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 2009 Sun Microsystems, Inc.  All rights reserved.
+# Use is subject to license terms.
+#
+
+MODULE = mr_sas.so
+MDBTGT = kvm
+
+MODSRCS = mr_sas.c
+
+MRSASBASE = ../../../../../uts/common/io/mr_sas
+
+include ../../../../Makefile.cmd
+include ../../../../Makefile.cmd.64
+include ../../Makefile.sparcv9
+include ../../../Makefile.module
+
+CPPFLAGS += -I$(SRC)/uts/common
+CPPFLAGS += -I$(SRC)/uts/common/sys/scsi
+CPPFLAGS += -I$(SRC)/uts/common/sys/scsi/adapters
+CPPFLAGS += -I$(MRSASBASE)
+CPPFLAGS += -D KMDB_MODULE
--- a/usr/src/cmd/ntfsprogs/THIRDPARTYLICENSE	Fri Jun 05 10:27:16 2009 -0400
+++ b/usr/src/cmd/ntfsprogs/THIRDPARTYLICENSE	Fri Jun 05 10:28:40 2009 -0400
@@ -1,3 +1,11 @@
+"GPL Disclaimer
+For the avoidance of doubt, except that if any license choice other than GPL or
+LGPL is available it will apply instead, Sun elects to use only the General
+Public License version 2 (GPLv2) at this time for any software where a choice of
+GPL license versions is made available with the language indicating that GPLv2
+or any later version may be used, or where a choice of which version of the GPL
+is applied is otherwise unspecified."
+
 		    GNU GENERAL PUBLIC LICENSE
 		       Version 2, June 1991
 
--- a/usr/src/cmd/ntfsprogs/THIRDPARTYLICENSE.readme	Fri Jun 05 10:27:16 2009 -0400
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,7 +0,0 @@
-"GPL Disclaimer
-For the avoidance of doubt, except that if any license choice other than GPL or
-LGPL is available it will apply instead, Sun elects to use only the General
-Public License version 2 (GPLv2) at this time for any software where a choice of
-GPL license versions is made available with the language indicating that GPLv2
-or any later version may be used, or where a choice of which version of the GPL
-is applied is otherwise unspecified."
--- a/usr/src/cmd/parted/THIRDPARTYLICENSE	Fri Jun 05 10:27:16 2009 -0400
+++ b/usr/src/cmd/parted/THIRDPARTYLICENSE	Fri Jun 05 10:28:40 2009 -0400
@@ -1,3 +1,10 @@
+"For the avoidance of doubt, except that if any license choice other 
+than GPL or LGPL is available it will apply instead, Sun elects to 
+use only the General Public License version 3 (GPLv3) at this time 
+for any software where a choice of GPL license versions is made 
+available with the language indicating that GPLv3 or any later 
+version may be used, or where a choice of which version of the GPL 
+is applied is otherwise unspecified."
 
 		    GNU GENERAL PUBLIC LICENSE
 		       Version 3, 29 June 2007
--- a/usr/src/cmd/parted/THIRDPARTYLICENSE.readme	Fri Jun 05 10:27:16 2009 -0400
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,7 +0,0 @@
-"For the avoidance of doubt, except that if any license choice other 
-than GPL or LGPL is available it will apply instead, Sun elects to 
-use only the General Public License version 3 (GPLv3) at this time 
-for any software where a choice of GPL license versions is made 
-available with the language indicating that GPLv3 or any later 
-version may be used, or where a choice of which version of the GPL 
-is applied is otherwise unspecified."
--- a/usr/src/cmd/perl/contrib/Sun/Solaris/Privilege/Privilege.pm	Fri Jun 05 10:27:16 2009 -0400
+++ b/usr/src/cmd/perl/contrib/Sun/Solaris/Privilege/Privilege.pm	Fri Jun 05 10:28:40 2009 -0400
@@ -19,7 +19,7 @@
 #
 
 #
-# Copyright 2008 Sun Microsystems, Inc.  All rights reserved.
+# Copyright 2009 Sun Microsystems, Inc.  All rights reserved.
 # Use is subject to license terms.
 #
 
@@ -40,7 +40,7 @@
 
 our (@EXPORT_OK, %EXPORT_TAGS);
 my @constants = qw(PRIV_STR_SHORT PRIV_STR_LIT PRIV_STR_PORT PRIV_ON PRIV_OFF
-	PRIV_SET PRIV_AWARE PRIV_DEBUG);
+	PRIV_SET PRIV_AWARE PRIV_AWARE_RESET PRIV_DEBUG);
 my @syscalls = qw(setppriv getppriv setpflags getpflags);
 my @libcalls = qw(priv_addset priv_copyset priv_delset
     priv_emptyset priv_fillset priv_intersect priv_inverse priv_ineffect
--- a/usr/src/cmd/ptools/ppriv/ppriv.c	Fri Jun 05 10:27:16 2009 -0400
+++ b/usr/src/cmd/ptools/ppriv/ppriv.c	Fri Jun 05 10:28:40 2009 -0400
@@ -19,14 +19,12 @@
  * CDDL HEADER END
  */
 /*
- * Copyright 2008 Sun Microsystems, Inc.  All rights reserved.
+ * Copyright 2009 Sun Microsystems, Inc.  All rights reserved.
  * Use is subject to license terms.
  *
  * Program to examine or set process privileges.
  */
 
-#pragma ident	"%Z%%M%	%I%	%E% SMI"
-
 #include <stdio.h>
 #include <stdio_ext.h>
 #include <stdlib.h>
@@ -649,6 +647,7 @@
 	{ PRIV_DEBUG, "PRIV_DEBUG" },
 	{ PRIV_AWARE, "PRIV_AWARE" },
 	{ PRIV_AWARE_INHERIT, "PRIV_AWARE_INHERIT" },
+	{ PRIV_AWARE_RESET, "PRIV_AWARE_RESET" },
 	{ PRIV_XPOLICY, "PRIV_XPOLICY" },
 	{ NET_MAC_AWARE, "NET_MAC_AWARE" },
 	{ NET_MAC_AWARE_INHERIT, "NET_MAC_AWARE_INHERIT" },
--- a/usr/src/cmd/stat/common/acquire_iodevs.c	Fri Jun 05 10:27:16 2009 -0400
+++ b/usr/src/cmd/stat/common/acquire_iodevs.c	Fri Jun 05 10:28:40 2009 -0400
@@ -19,12 +19,10 @@
  * CDDL HEADER END
  */
 /*
- * Copyright 2008 Sun Microsystems, Inc.  All rights reserved.
+ * Copyright 2009 Sun Microsystems, Inc.  All rights reserved.
  * Use is subject to license terms.
  */
 
-#pragma ident	"%Z%%M%	%I%	%E% SMI"
-
 #include "statcommon.h"
 #include "dsr.h"
 
@@ -696,7 +694,7 @@
 {
 	int  num = 0;
 
-	num = sscanf(s, "%[a-z]%d%*[.]%[a-z]%d%*[.]%[a-z]%d", lname, l,
+	num = sscanf(s, "%[a-z]%d%*[.]%[a-z]%d%*[.]%[a-z_]%d", lname, l,
 	    tname, t, iname, i);
 	return ((num == 6) ? 1 : 0);
 }
--- a/usr/src/cmd/svc/shell/netservices.sh	Fri Jun 05 10:27:16 2009 -0400
+++ b/usr/src/cmd/svc/shell/netservices.sh	Fri Jun 05 10:28:40 2009 -0400
@@ -19,12 +19,10 @@
 #
 # CDDL HEADER END
 #
-# Copyright 2008 Sun Microsystems, Inc.  All rights reserved.
+# Copyright 2009 Sun Microsystems, Inc.  All rights reserved.
 # Use is subject to license terms.
 #
 
-# ident	"%Z%%M%	%I%	%E% SMI"
-
 DT_CHANGED=0
 
 LOG_FMRI=svc:/system/system-log
@@ -287,6 +285,12 @@
 #
 ln -sf ./$profile /var/svc/profile/generic.xml
 svccfg apply /var/svc/profile/generic.xml
+
+#
+# Create a hash entry so that manifest_import is aware of the
+# profile being applied and does not reapply the profile on reboot.
+#
+SVCCFG_CHECKHASH="TRUE" /lib/svc/bin/prophist hash /var/svc/profile/generic.xml
 if [ $profile = "generic_open.xml" ]
 then
 	# generic_open may not start inetd services on upgraded systems
--- a/usr/src/cmd/svc/startd/method.c	Fri Jun 05 10:27:16 2009 -0400
+++ b/usr/src/cmd/svc/startd/method.c	Fri Jun 05 10:28:40 2009 -0400
@@ -604,7 +604,7 @@
 	scf_handle_t *h;
 	scf_snapshot_t *snap;
 	const char *mname;
-	const char *errstr;
+	mc_error_t *m_error;
 	struct method_context *mcp;
 	int result = 0, timeout_fired = 0;
 	int sig, r;
@@ -750,11 +750,12 @@
 	log_framework(LOG_DEBUG, "%s: forking to run method %s\n",
 	    inst->ri_i.i_fmri, method);
 
-	errstr = restarter_get_method_context(RESTARTER_METHOD_CONTEXT_VERSION,
+	m_error = restarter_get_method_context(RESTARTER_METHOD_CONTEXT_VERSION,
 	    inst->ri_m_inst, snap, mname, method, &mcp);
 
-	if (errstr != NULL) {
-		log_instance(inst, B_TRUE, "%s", errstr);
+	if (m_error != NULL) {
+		log_instance(inst, B_TRUE, "%s", m_error->msg);
+		restarter_mc_error_destroy(m_error);
 		result = EINVAL;
 		goto out;
 	}
--- a/usr/src/cmd/svc/svccfg/svccfg_internal.c	Fri Jun 05 10:27:16 2009 -0400
+++ b/usr/src/cmd/svc/svccfg/svccfg_internal.c	Fri Jun 05 10:28:40 2009 -0400
@@ -20,7 +20,7 @@
  */
 
 /*
- * Copyright 2008 Sun Microsystems, Inc.  All rights reserved.
+ * Copyright 2009 Sun Microsystems, Inc.  All rights reserved.
  * Use is subject to license terms.
  */
 
@@ -1200,6 +1200,12 @@
 		case SCF_ERROR_NOT_FOUND:
 			rc = ENOENT;
 			goto errout;
+		case SCF_ERROR_INVALID_ARGUMENT:
+			rc = EINVAL;
+			goto errout;
+		case SCF_ERROR_CONSTRAINT_VIOLATED:
+			rc = ENOTSUP;
+			goto errout;
 		default:
 			bad_error("scf_handle_decode_fmri", scf_error());
 		}
--- a/usr/src/cmd/svc/svccfg/svccfg_libscf.c	Fri Jun 05 10:27:16 2009 -0400
+++ b/usr/src/cmd/svc/svccfg/svccfg_libscf.c	Fri Jun 05 10:28:40 2009 -0400
@@ -1458,7 +1458,8 @@
 }
 
 static int
-refresh_running_snapshot(void *entity) {
+refresh_running_snapshot(void *entity)
+{
 	scf_snapshot_t *snap;
 	int r;
 
@@ -1491,7 +1492,8 @@
 	int r;
 
 	if (!isservice) {
-		if (est->sc_repo_filename == NULL) {
+		if (est->sc_repo_filename == NULL &&
+		    est->sc_repo_doorname == NULL) {
 			if (_smf_refresh_instance_i(entity) == 0) {
 				if (g_verbose)
 					warn(gettext("Refreshed %s.\n"), fmri);
@@ -1567,7 +1569,8 @@
 			}
 		}
 
-		if (est->sc_repo_filename != NULL) {
+		if (est->sc_repo_filename != NULL ||
+		    est->sc_repo_doorname != NULL) {
 			r = refresh_running_snapshot(inst);
 			switch (r) {
 			case 0:
@@ -1627,7 +1630,7 @@
 	int issvc;
 	int r;
 
-	if (est->sc_repo_filename == NULL)
+	if (est->sc_repo_filename == NULL && est->sc_repo_doorname == NULL)
 		return;
 
 	assert(cur_svc != NULL);
@@ -9709,9 +9712,10 @@
 			    scf_strerror(scf_err));
 			goto cleanup;
 		}
-		if (err != 0)
+		if (err != 0) {
 			/* error message displayed by scf_walk_fmri */
 			goto cleanup;
+		}
 	}
 
 	ret = scf_tmpl_validate_fmri(g_hndl, inst_fmri, snapbuf, &errs,
@@ -9755,6 +9759,7 @@
 	}
 	if (errs != NULL)
 		scf_tmpl_errors_destroy(errs);
+
 cleanup:
 	free(inst_fmri);
 	free(snapbuf);
--- a/usr/src/cmd/svc/svccfg/svccfg_tmpl.c	Fri Jun 05 10:27:16 2009 -0400
+++ b/usr/src/cmd/svc/svccfg/svccfg_tmpl.c	Fri Jun 05 10:28:40 2009 -0400
@@ -20,7 +20,7 @@
  */
 
 /*
- * Copyright 2008 Sun Microsystems, Inc.  All rights reserved.
+ * Copyright 2009 Sun Microsystems, Inc.  All rights reserved.
  * Use is subject to license terms.
  */
 
@@ -1913,10 +1913,18 @@
 		 * not be in the repository yet.  In this case we
 		 * continue on without it.
 		 */
+		if (r == EINVAL)
+			warn(gettext("WARNING: restarter FMRI %s is invalid\n"),
+			    restarter);
+
+		if (r == ENOTSUP)
+			warn(gettext("WARNING: restarter FMRI %s is not valid; "
+			    "instance fmri required.\n"), restarter);
+
 		if (r == ENOMEM)
 			uu_die(emesg_nomem);
-		else
-			svc->sc_u.sc_service.sc_restarter = NULL;
+
+		svc->sc_u.sc_service.sc_restarter = NULL;
 	}
 	if (is_global == 0) {
 		if ((r = load_instance(SCF_INSTANCE_GLOBAL, "global",
@@ -1961,6 +1969,14 @@
 		 * in the repository yet.  In this case we continue on
 		 * without it.
 		 */
+		if (r == EINVAL)
+			warn(gettext("WARNING: restarter FMRI %s is invalid\n"),
+			    restarter);
+
+		if (r == ENOTSUP)
+			warn(gettext("WARNING: restarter FMRI %s is not valid; "
+			    "instance fmri required.\n"), restarter);
+
 		if (r == ENOMEM)
 			uu_die(emesg_nomem);
 	}
@@ -2484,7 +2500,8 @@
 static int
 property_is_type(property_t *prop, scf_type_t type)
 {
-	return (prop->sc_value_type == type);
+	return (scf_is_compatible_type(type, prop->sc_value_type) ==
+	    SCF_SUCCESS);
 }
 
 /*
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/usr/src/cmd/svr4pkg/Makefile	Fri Jun 05 10:28:40 2009 -0400
@@ -0,0 +1,64 @@
+#
+# 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
+# or http://www.opensolaris.org/os/licensing.
+# 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 2009 Sun Microsystems, Inc.  All rights reserved.
+# Use is subject to license terms.
+#
+
+CMDSUBDIRS=	\
+		installf	\
+		pkgadd		\
+		pkgadm		\
+		pkgchk		\
+		pkgcond		\
+		pkginfo		\
+		pkginstall	\
+		pkgmk		\
+		pkgname		\
+		pkgparam	\
+		pkgproto	\
+		pkgremove	\
+		pkgrm		\
+		pkgscripts	\
+		pkgtrans
+
+.PARALLEL=	$(CMDSUBDIRS)
+
+LIBSUBDIR=	libinst
+SUBDIRS=	$(CMDSUBDIRS) $(LIBSUBDIR)
+
+all:=		TARGET= all
+install :=	TARGET= install
+clean :=	TARGET= clean
+clobber:=	TARGET= clobber
+_msg :=		TARGET= _msg
+
+.KEEP_STATE:
+
+all clean clobber install _msg: $(SUBDIRS)
+
+$(CMDSUBDIRS): $(LIBSUBDIR)
+
+$(SUBDIRS): FRC
+	@cd $@; pwd; $(MAKE) $(TARGET)
+
+FRC:
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/usr/src/cmd/svr4pkg/Makefile.svr4pkg	Fri Jun 05 10:28:40 2009 -0400
@@ -0,0 +1,46 @@
+#
+# 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
+# or http://www.opensolaris.org/os/licensing.
+# 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 2009 Sun Microsystems, Inc.  All rights reserved.
+# Use is subject to license terms.
+#
+
+include $(SRC)/cmd/Makefile.cmd
+
+CFLAGS +=	$(CCVERBOSE)
+CPPFLAGS +=	-I$(SRC)/cmd/svr4pkg/hdrs \
+		-I$(SRC)/lib/libpkg/common \
+		-I$(SRC)/lib/libinstzones/common \
+		-D_FILE_OFFSET_BITS=64
+
+
+LIBINST =       $(SRC)/cmd/svr4pkg/libinst/libinst.a
+
+SRCS=           $(OBJS:.o=.c)
+
+
+#
+# For messaging catalog
+#
+POFILE=         $(PROG).po
+MSGFILES=       $(OBJS:.o=.i)
+CLOBBERFILES += $(PROG) $(POFILE)
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/usr/src/cmd/svr4pkg/Makefile.svr4pkg.targ	Fri Jun 05 10:28:40 2009 -0400
@@ -0,0 +1,43 @@
+#
+# 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
+# or http://www.opensolaris.org/os/licensing.
+# 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 2009 Sun Microsystems, Inc.  All rights reserved.
+# Use is subject to license terms.
+#
+
+$(PROG):	$(OBJS)
+		$(LINK.c) -o $@ $(OBJS) $(LDLIBS) $(LIBINST)
+		$(POST_PROCESS)
+
+$(POFILE):	$(MSGFILES)
+		$(BUILDPO.msgfiles)
+
+_msg:		$(MSGDOMAINPOFILE)
+
+clean:
+	$(RM) $(OBJS) $(MSGFILES)
+
+clobber:	clean
+	-$(RM) $(CLOBBERFILES)
+
+
+include $(SRC)/Makefile.msg.targ
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/usr/src/cmd/svr4pkg/hdrs/dryrun.h	Fri Jun 05 10:28:40 2009 -0400
@@ -0,0 +1,69 @@
+/*
+ * 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
+ * or http://www.opensolaris.org/os/licensing.
+ * 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 2007 Sun Microsystems, Inc.  All rights reserved.
+ * Use is subject to license terms.
+ */
+
+
+#ifndef __DRYRUN_H__
+#define	__DRYRUN_H__
+
+#include	"cfext.h"
+
+/* The various types of status entry in the info file. */
+#define	PARTIAL	1
+#define	RUNLEVEL 2
+#define	PKGFILES 3
+#define	DEPEND 4
+#define	SPACE 5
+#define	CONFLICT 6
+#define	SETUID 7
+#define	PRIV 8
+#define	PKGDIRS 9
+#define	REQUESTEXITCODE 10
+#define	CHECKEXITCODE 11
+#define	EXITCODE 12
+#define	DR_TYPE 13
+
+#define	INSTALL_TYPE	1
+#define	REMOVE_TYPE	0
+
+#if defined(__STDC__)
+#define	__P(protos) protos
+#else	/* __STDC__ */
+#define	__P(protos) ()
+#endif	/* __STDC__ */
+
+extern void	set_dryrun_mode __P((void));
+extern int	in_dryrun_mode __P((void));
+extern void	set_continue_mode __P((void));
+extern int	in_continue_mode __P((void));
+extern void	init_contfile __P((char *cn_dir));
+extern void	init_dryrunfile __P((char *dr_dir));
+extern void	set_dr_info __P((int type, int value));
+extern int	cmd_ln_respfile __P((void));
+extern int	is_a_respfile __P((void));
+extern void	write_dryrun_file __P((struct cfextra **extlist));
+extern boolean_t	read_continuation __P((int *error));
+
+#endif	/* __DRYRUN_H__ */
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/usr/src/cmd/svr4pkg/hdrs/install.h	Fri Jun 05 10:28:40 2009 -0400
@@ -0,0 +1,144 @@
+/*
+ * 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
+ * or http://www.opensolaris.org/os/licensing.
+ * 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 2004 Sun Microsystems, Inc.  All rights reserved.
+ * Use is subject to license terms.
+ */
+
+/* Copyright (c) 1984, 1986, 1987, 1988, 1989 AT&T */
+/* All Rights Reserved */
+
+#ifndef __INSTALL_H
+#define	__INSTALL_H
+
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#include <sys/types.h>
+#include <limits.h>
+#include <pkgstrct.h>
+
+/* Settings for procedure scripts */
+#define	PROC_USER	"root"
+#define	PROC_GRP	"other"
+#define	PROC_STDIN	"/dev/null"
+#define	PROC_XSTDIN	"/dev/tty"
+#define	PROC_STDOUT	"/dev/tty"
+
+/* Settings for class action scripts */
+#define	CAS_USER	"root"
+#define	CAS_GRP		"other"
+#define	CAS_STDIN	"/dev/null"
+#define	CAS_STDOUT	"/dev/tty"
+
+/* Settings for non-privileged scripts */
+#define	CHK_USER	"install"	/* default user i.d. to use */
+#define	CHK_USER_ALT	"noaccess"	/* alternate non-priv user */
+#define	CHK_USER_ROOT	"root"		/* root user */
+#define	CHK_USER_NON	"root"		/* user for non-compliant pkg's */
+#define	CHK_GRP		"other"
+#define	CHK_STDIN	"/dev/null"
+#define	CHK_STDOUT	"/dev/tty"
+
+/* Settings for admin "rscriptalt" option */
+#define	RSCRIPTALT		rscriptalt
+#define	RSCRIPTALT_KEYWORD	"rscriptalt"
+#define	RSCRIPTALT_ROOT		"root"
+#define	RSCRIPTALT_NOACCESS	"noaccess"
+
+#define	OAMBASE	"/usr/sadm/sysadm"
+#define	MAILCMD	"/usr/bin/mail"
+#define	DATSTRM	"datastream"
+#define	SHELL	"/sbin/sh"
+#define	PKGINFO	"pkginfo"
+#define	PKGMAP	"pkgmap"
+#define	LIVE_CONT	"__live_cont__"
+#define	RELOC "reloc"
+#define	ROOT "root"
+
+/* Additional cfent/cfextra codes. */
+#define	BADFSYS	(short)(-1) /* an fsys is needed */
+#define	BADINDEX    (-1)    /* pkg class idx not yet set */
+
+/* This holds admin file data. */
+struct admin {
+	char	*mail;
+	char	*instance;
+	char	*partial;
+	char	*runlevel;
+	char	*idepend;
+	char	*rdepend;
+	char	*space;
+	char	*setuid;
+	char	*conflict;
+	char	*action;
+	char	*networktimeout;
+	char	*networkretries;
+	char	*authentication;
+	char	*keystore;
+	char	*proxy;
+	char	*basedir;
+	char	*rscriptalt;
+};
+
+/*
+ * This table details the status of all filesystems available to the target
+ * host.
+ */
+struct fstable {
+	char	*name;	/* name of filesystem, (mount point) */
+	int	namlen;	/* The length of the name (mountpoint) */
+	fsblkcnt_t bsize;	/* fundamental file system block size */
+	fsblkcnt_t frsize;	/* file system fragment size */
+	fsblkcnt_t bfree;	/* total # of free blocks */
+	fsblkcnt_t bused;	/* total # of used blocks */
+	fsblkcnt_t ffree;	/* total # of free file nodes */
+	fsblkcnt_t fused;	/* total # of used file nodes */
+	char	*fstype;	/* type of filesystem - nfs, lo, ... */
+	char	*remote_name;	/* client's mounted filesystem */
+	unsigned	writeable:1;	/* access permission */
+	unsigned	write_tested:1;	/* access permission fully tested */
+	unsigned	remote:1;	/* on a remote filesystem */
+	unsigned	mounted:1;	/* actually mounted right now */
+	unsigned	srvr_map:1;	/* use server_map() */
+	unsigned	cl_mounted:1;	/* mounted in client space */
+	unsigned	mnt_failed:1;	/* attempt to loopback mount failed */
+	unsigned	served:1;	/* filesystem comes from a server */
+};
+
+#define	ADM(x, y)	((adm.x != NULL) && (y != NULL) && \
+			    strcmp(adm.x, y) == 0)
+#define	ADMSET(x)	(adm.x != NULL)
+#define	PARAMETRIC(x) (x[0] == '$')
+#define	RELATIVE(x)	(x[0] != '/')
+
+#if defined(lint) && !defined(gettext)
+#define	gettext(x)	x
+#endif	/* defined(lint) && !defined(gettext) */
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif	/* __INSTALL_H */
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/usr/src/cmd/svr4pkg/hdrs/libadm.h	Fri Jun 05 10:28:40 2009 -0400
@@ -0,0 +1,319 @@
+/*
+ * 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
+ * or http://www.opensolaris.org/os/licensing.
+ * 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 2004 Sun Microsystems, Inc.  All rights reserved.
+ * Use is subject to license terms.
+ */
+
+
+#ifndef	__PKG_LIBADM_H__
+#define	__PKG_LIBADM_H__
+
+#include <sys/types.h>
+#include <sys/vtoc.h>
+#include <limits.h>
+#include <stdio.h>
+#include <pkgstrct.h>
+#include <pkginfo.h>
+#include <valtools.h>
+#include "install.h"
+
+#if defined(__STDC__)
+#define	__P(protos) protos
+#else	/* __STDC__ */
+#define	__P(protos) ()
+#endif	/* __STDC__ */
+
+
+/*
+ * ckdate.c
+ */
+extern int	ckdate_err __P((char *fmt, char *error));
+extern int	ckdate_hlp __P((char *fmt, char *help));
+extern int	ckdate_val __P((char *fmt, char *input));
+extern int	ckdate __P((char *date, char *fmt, char *defstr, char *error,
+				char *help, char *prompt));
+
+/*
+ * ckgid.c
+ */
+extern int	ckgid_dsp __P((void));
+extern int	ckgid_val __P((char *grpnm));
+extern int	ckgrpfile __P((void));
+extern void	ckgid_err __P((int disp, char *error));
+extern void	ckgid_hlp __P((int disp, char *help));
+extern int	ckgid __P((char *gid, short disp, char *defstr, char *error,
+				char *help, char *prompt));
+
+/*
+ * ckint.c
+ */
+extern int	ckint_val __P((char *value, short base));
+extern void	ckint_err __P((short base, char *error));
+extern void	ckint_hlp __P((short base, char *help));
+extern int	ckint __P((long *intval, short base, char *defstr, char *error,
+				char *help, char *prompt));
+
+/*
+ * ckitem.c
+ */
+extern CKMENU	*allocmenu __P((char *label, int attr));
+extern void	ckitem_err __P((CKMENU *menup, char *error));
+extern void	ckitem_hlp __P((CKMENU *menup, char *help));
+extern int	ckitem __P((CKMENU *menup, char *item[], short max,
+				char *defstr, char *error, char *help,
+				char *prompt));
+extern int	setitem __P((CKMENU *menup, char *choice));
+extern int	setinvis __P((CKMENU *menup, char *choice));
+extern void	printmenu __P((CKMENU *menup));
+
+/*
+ * ckkeywd.c
+ */
+extern int	ckkeywd __P((char *strval, char *keyword[], char *defstr,
+				char *error, char *help, char *prompt));
+
+/*
+ * ckpath.c
+ */
+extern int	ckpath_stx __P((int pflags));
+extern int	ckpath_val __P((char *path, int pflags));
+extern void	ckpath_err __P((int pflags, char *error, char *input));
+extern void	ckpath_hlp __P((int pflags, char *help));
+extern int	ckpath __P((char *pathval, int pflags, char *defstr,
+				char *error, char *help, char *prompt));
+
+/*
+ * ckrange.c
+ */
+extern void	ckrange_err __P((long lower, long upper, int base,
+				char *error));
+extern void	ckrange_hlp __P((long lower, long upper, int base, char *help));
+extern int	ckrange_val __P((long lower, long upper, int base,
+				char *input));
+extern int	ckrange __P((long *rngval, long lower, long upper, short base,
+				char *defstr, char *error, char *help,
+				char *prompt));
+
+/*
+ * ckstr.c
+ */
+extern int	ckstr_val __P((char *regexp[], int length, char *input));
+extern void	ckstr_err __P((char *regexp[], int length, char *error,
+				char *input));
+extern void	ckstr_hlp __P((char *regexp[], int length, char *help));
+extern int	ckstr __P((char *strval, char *regexp[], int length,
+				char *defstr, char *error, char *help,
+				char *prompt));
+
+/*
+ * cktime.c
+ */
+extern int	cktime_val __P((char *fmt, char *input));
+extern int	cktime_err __P((char *fmt, char *error));
+extern int	cktime_hlp __P((char *fmt, char *help));
+extern int	fmtcheck __P((char *fmt));
+extern int	cktime __P((char *tod, char *fmt, char *defstr, char *error,
+				char *help, char *prompt));
+
+/*
+ * ckuid.c
+ */
+extern int	ckuid_dsp __P((void));
+extern int	ckuid_val __P((char *usrnm));
+extern int	ckpwdfile __P((void));
+extern void	ckuid_err __P((short disp, char *error));
+extern void	ckuid_hlp __P((int disp, char *help));
+extern int	ckuid __P((char *uid, short disp, char *defstr, char *error,
+				char *help, char *prompt));
+
+/*
+ * ckyorn.c
+ */
+extern int	ckyorn_val __P((char *str));
+extern void	ckyorn_err __P((char *error));
+extern void	ckyorn_hlp __P((char *help));
+extern int	ckyorn __P((char *yorn, char *defstr, char *error, char *help,
+				char *prompt));
+
+/*
+ * devattr.c
+ */
+extern char	*devattr __P((char *device, char *attribute));
+
+/*
+ * devreserv.c
+ */
+extern char	*_rsvtabpath __P((void));
+extern int	_openlkfile __P((void));
+extern int	_closelkfile __P((void));
+extern int	unreserv __P((long key, char *device));
+extern char	**devreserv __P((long key, char **rsvlst[]));
+extern int	devfree __P((long key, char *device));
+extern struct	reservdev	**reservdev __P((void));
+
+/*
+ * devtab.c
+ */
+extern void	_setdevtab __P((void));
+extern void	_enddevtab __P((void));
+extern char	*_devtabpath __P((void));
+extern int	_opendevtab __P((char *mode));
+extern int	_validalias __P((char *alias));
+extern struct	devtabent	*_getdevtabent __P((void));
+extern void	_freedevtabent __P((struct devtabent *ent));
+extern struct	devtabent	*_getdevrec __P((char *device));
+
+/*
+ * dgrpent.c
+ */
+extern void	_setdgrptab __P((void));
+extern void	_enddgrptab __P((void));
+extern char	*_dgrptabpath __P((void));
+extern int	_opendgrptab __P((char *mode));
+extern struct	dgrptabent	*_getdgrptabent __P((void));
+extern void	_freedgrptabent __P((struct dgrptabent *ent));
+extern struct	dgrptabent	*_getdgrprec __P((char *dgroup));
+
+/*
+ * fulldevnm.c
+ */
+extern char	*getfullblkname __P((char *cp));
+extern char	*getfullrawname __P((char *cp));
+
+/*
+ * getdev.c
+ */
+extern char	**getdev __P((char **devices, char **criteria, int options));
+
+/*
+ * getdgrp.c
+ */
+extern char	**getdgrp __P((char **dgroups, char **criteria, int options));
+
+/*
+ * getinput.c
+ */
+extern int	getinput __P((char *s));
+
+/*
+ * getvol.c
+ */
+extern int	getvol __P((char *device, char *label, int options,
+				char *prompt));
+extern int	_getvol __P((char *device, char *label, int options,
+				char *prompt, char *norewind));
+extern void	doremovecmd __P((char *device, int echo));
+
+/*
+ * listdev.c
+ */
+extern char	**listdev __P((char *device));
+
+/*
+ * listdgrp.c
+ */
+extern char	**listdgrp __P((char *dgroup));
+
+/*
+ * memory.c
+ */
+extern long	sysmem __P((void));
+extern long	asysmem __P((void));
+
+/*
+ * pkginfo.c
+ */
+extern int	pkginfo __P((struct pkginfo *info, char *pkginst, ...));
+extern int	fpkginfo __P((struct pkginfo *info, char *pkginst));
+extern char	*fpkginst __P((char *pkg, ...));
+
+/*
+ * pkgnmchk.c
+ */
+extern int	pkgnmchk __P((register char *pkg, register char *spec,
+				int presvr4flg));
+extern void	set_ABI_namelngth __P((void));
+extern int	get_ABI_namelngth __P((void));
+
+/*
+ * pkgparam.c
+ */
+extern char	*fpkgparam __P((FILE *fp, char *param));
+extern char	*pkgparam __P((char *pkg, char *param));
+extern void	set_PKGpaths __P((char *path));
+extern char	*get_PKGLOC __P((void));
+extern char	*get_PKGOLD __P((void));
+extern char	*get_PKGADM __P((void));
+extern void	set_PKGADM(char *newpath);
+extern void	set_PKGLOC(char *newpath);
+
+/*
+ * putdev.c
+ */
+extern int	_putdevtabrec __P((FILE *stream, struct devtabent *rec));
+extern int	_adddevtabrec __P((char *alias, char **attrval));
+extern int	_moddevtabrec __P((char *device, char **attrval));
+extern int	_rmdevtabrec __P((char *device));
+extern int	_rmdevtabattrs __P((char *device, char **attributes,
+				    char ***notfounds));
+
+/*
+ * putdgrp.c
+ */
+extern int	_putdgrptabrec __P((FILE *stream, struct dgrptabent *rec));
+extern int	_adddgrptabrec __P((char *dgrp, char **members));
+extern int	_rmdgrptabrec __P((char *dgrp));
+extern int	_rmdgrpmems __P((char *dgrp, char **mems, char ***notfounds));
+
+/*
+ * puterror.c
+ */
+extern void	puterror __P((FILE *fp, char *defmesg, char *error));
+
+/*
+ * puthelp.c
+ */
+extern void	puthelp __P((FILE *fp, char *defmesg, char *help));
+
+/*
+ * putprmpt.c
+ */
+extern void	putprmpt __P((FILE *fp, char *prompt, char *choices[],
+				char *defstr));
+
+/*
+ * puttext.c
+ */
+extern int	puttext __P((FILE *fp, char *str, int lmarg, int rmarg));
+
+/*
+ * rdwr_vtoc.c
+ */
+extern int	read_vtoc __P((int fd, struct vtoc *vtoc));
+extern int	write_vtoc __P((int fd, struct vtoc *vtoc));
+
+#if defined(lint) && !defined(gettext)
+#define	gettext(x)	x
+#endif	/* defined(lint) && !defined(gettext) */
+
+#endif	/* __PKG_LIBADM_H__ */
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/usr/src/cmd/svr4pkg/hdrs/libinst.h	Fri Jun 05 10:28:40 2009 -0400
@@ -0,0 +1,595 @@
+/*
+ * 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
+ * or http://www.opensolaris.org/os/licensing.
+ * 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 2009 Sun Microsystems, Inc.  All rights reserved.
+ * Use is subject to license terms.
+ */
+
+#ifndef	__HDRS_LIBINST_H__
+#define	__HDRS_LIBINST_H__
+
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#include <stdio.h>
+#include <fcntl.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <pkgstrct.h>
+#include <pkginfo.h>
+#include "pkglib.h"
+#include <cfext.h>
+#include "install.h"
+
+#define	DEF_NONE_SCR	"i.CompCpio"
+
+#define	BL_ALL		-1	/* refers to all allocated lists */
+
+/* signal handler function definition */
+
+typedef void (sighdlrFunc_t)(int);
+
+/* maximum parameter length */
+
+#define	MAX_PKG_PARAM_LENGTH	(64+1)	/* +1 for null termination */
+
+/* flag for check_applicability */
+
+typedef unsigned long CAF_T;
+
+/* flags for check_applicability */
+
+#define	CAF_IN_GLOBAL_ZONE	0x00000001	/* in global zone */
+#define	CAF_SCOPE_GLOBAL	0x00000002	/* -G specified */
+#define	CAF_SCOPE_NONGLOBAL	0x00000004	/* -Z specified */
+
+/* path to the request file in the package directory */
+
+#define	REQUEST_FILE	"install/request"
+
+/* path to the copyright file in the package directory */
+
+#define	COPYRIGHT_FILE	"install/copyright"
+
+/* path to the depend file in the package directory */
+
+#define	DEPEND_FILE	"install/depend"
+
+/*
+ * name of environment variable set to non-global zone name being installed:
+ * pkgadd/pkginstall expects this name and passes it on to any scripts that
+ * are run if it is set.
+ */
+
+#define	PKG_ZONENAME_VARIABLE	"SUNW_PKG_INSTALL_ZONENAME"
+
+/*
+ * name of environment variable set to indicate this package should be installed
+ * in the current zone only - see PSARC/2004/789 - New Pkginfo(4) attributes
+ * for zones
+ */
+
+#define	PKG_THISZONE_VARIABLE	"SUNW_PKG_THISZONE"
+
+/*
+ * name of environment variable set to indicate this package should be installed
+ * in all zones, and only from the global zone - see PSARC/2003/460
+ */
+
+#define	PKG_ALLZONES_VARIABLE	"SUNW_PKG_ALLZONES"
+
+/*
+ * name of environment variable set to indicate this package should be installed
+ * hollow (db update only) when installed in nonglobal zone - see PSARC/2003/460
+ */
+
+#define	PKG_HOLLOW_VARIABLE	"SUNW_PKG_HOLLOW"
+
+/*
+ * General purpose return codes used for functions which don't return a basic
+ * success or failure. For those functions wherein a yes/no result is
+ * possible, then 1 means OK and 0 means FAIL.
+ */
+#define	RESULT_OK	0x0
+#define	RESULT_WRN	0x1
+#define	RESULT_ERR	0x2
+
+/* These are the file status indicators for the contents file */
+#define	INST_RDY	'+'	/* entry is ready to installf -f */
+#define	RM_RDY		'-'	/* entry is ready for removef -f */
+#define	NOT_FND		'!'	/* entry (or part of entry) was not found */
+#define	SERVED_FILE	'%'	/* using the file server's RO partition */
+#define	STAT_NEXT	'@'	/* this is awaiting eptstat */
+#define	DUP_ENTRY	'#'	/* there's a duplicate of this */
+#define	CONFIRM_CONT	'*'	/* need to confirm contents */
+#define	CONFIRM_ATTR	'~'	/* need to confirm attributes */
+#define	ENTRY_OK	'\0'	/* entry is a confirmed file */
+
+/* control bits for pkgdbmerg() */
+#define	NO_COPY		0x0001
+#define	CLIENT_PATHS	0x0002	/* working with a client database */
+
+/* control bits for file verification by class */
+#define	DEFAULT		0x0	/* standard full verification */
+#define	NOVERIFY	0x1	/* do not verify */
+#define	QKVERIFY	0x2	/* do a quick verification instead */
+
+/* control bit for path type to pass to CAS */
+#define	DEFAULT		0x0	/* standard server-relative absolute path */
+#define	REL_2_CAS	0x1	/* pass pkgmap-type relative path */
+
+/* findscripts() argument */
+#define	I_ONLY		0x0	/* find install class action scripts */
+#define	R_ONLY		0x1	/* find removal class action scripts */
+
+struct cl_attr {
+	char	name[CLSSIZ+1];	/* name of class */
+	char	*inst_script;	/* install class action script */
+	char	*rem_script;	/* remove class action script */
+	unsigned	src_verify:3;	/* source verification level */
+	unsigned 	dst_verify:4;	/* destination verification level */
+	unsigned	relpath_2_CAS:1;	/* CAS gets relative paths */
+};
+
+#if defined(__STDC__)
+#define	__P(protos) protos
+#else	/* __STDC__ */
+#define	__P(protos) ()
+#endif	/* __STDC__ */
+
+/* Common quit declaration used across many package commands */
+extern void	quit(int) __NORETURN;
+
+
+/* listmgr.c */
+extern int	bl_create __P((int count_per_block, int struct_size,
+		    char *desc));
+extern char	*bl_next_avail __P((int list_handle));
+extern char	*bl_get_record __P((int list_handle, int recno));
+extern void	bl_free __P((int list_handle));
+extern int	ar_create __P((int count_per_block, int struct_size,
+		    char *desc));
+extern char	**ar_next_avail __P((int list_handle));
+extern char	**ar_get_head __P((int list_handle));
+extern int	ar_delete __P((int list_handle, int index));
+extern void	ar_free __P((int list_handle));
+
+/* doulimit.c */
+extern int	set_ulimit __P((char *script, char *err_msg));
+extern int	clr_ulimit __P((void));
+extern int	assign_ulimit __P((char *fslimit));
+
+/* dryrun.c */
+extern void	set_continue_not_ok __P((void));
+extern int	continue_is_ok __P((void));
+extern int	in_dryrun_mode __P((void));
+extern int	in_continue_mode __P((void));
+extern void	init_dryrunfile __P((char *dr_dir));
+extern void	init_contfile __P((char *cn_dir));
+extern void	set_dr_exitmsg __P((char *value));
+extern void	set_dr_info __P((int type, int value));
+extern void	write_dryrun_file __P((struct cfextra **extlist));
+
+/* instvol.c */
+extern void	regfiles_free __P((void));
+
+/* lockinst.c */
+extern int	lockinst __P((char *util_name, char *pkg_name, char *place));
+extern void	lockupd __P((char *place));
+extern void	unlockinst __P((void));
+
+extern char	*pathdup __P((char *s));
+extern char	*pathalloc __P((int n));
+extern char	*fixpath __P((char *path));
+extern char	*get_info_basedir __P((void));
+extern char	*get_basedir __P((void));
+extern char	*get_client_basedir __P((void));
+extern int	set_basedirs __P((int reloc, char *adm_basedir,
+		    char *pkginst, int nointeract));
+extern int	eval_path __P((char **server_ptr, char **client_ptr,
+		    char **map_ptr, char *path));
+extern int	get_orig_offset __P((void));
+extern char	*get_inst_root __P((void));
+extern char	*get_mount_point __P((short n));
+extern char	*get_remote_path __P((short n));
+extern void	set_env_cbdir __P((void));
+extern int	set_inst_root __P((char *path));
+extern void	put_path_params __P((void));
+extern int	mkpath __P((char *p));
+extern void	mkbasedir __P((int flag, char *path));
+extern int	is_an_inst_root __P((void));
+extern int	is_a_basedir __P((void));
+extern int	is_a_cl_basedir __P((void));
+extern int	is_relocatable __P((void));
+extern char	*orig_path __P((char *path));
+extern char	*orig_path_ptr __P((char *path));
+extern char	*qreason __P((int caller, int retcode, int started,
+			int includeZonename));
+extern char	*qstrdup __P((char *s));
+extern char	*srcpath __P((char *d, char *p, int part, int nparts));
+extern int	copyf __P((char *from, char *to, time_t mytime));
+extern int	copyFile __P((int, int, char *, char *, struct stat *, long));
+extern int	openLocal __P((char *a_path, int a_oflag, char *a_tmpdir));
+extern int	dockdeps __P((char *depfile, int removeFlag,
+			boolean_t a_preinstallCheck));
+extern int	finalck __P((struct cfent *ept, int attrchg, int contchg,
+			boolean_t a_warning));
+
+/* dockdeps.c */
+extern void setUpdate __P((void));
+extern int  isUpdate __P((void));
+extern void setPatchUpdate __P((void));
+extern int  isPatchUpdate __P((void));
+
+/* mntinfo.c */
+extern int	get_mntinfo __P((int map_client, char *vfstab_file));
+extern short	fsys __P((char *path));
+extern struct fstable *get_fs_entry __P((short n));
+extern int	mount_client __P((void));
+extern int	unmount_client __P((void));
+extern short	resolved_fsys __P((char *path));
+extern char	*get_server_host __P((short n));
+extern char	*server_map __P((char *path, short fsys_value));
+extern int	use_srvr_map __P((char *path, short *fsys_value));
+extern int	use_srvr_map_n __P((short n));
+extern int	is_fs_writeable __P((char *path, short *fsys_value));
+extern int	is_remote_fs __P((char *path, short *fsys_value));
+extern int	is_served __P((char *path, short *fsys_value));
+extern int	is_mounted __P((char *path, short *fsys_value));
+extern int	is_fs_writeable_n __P((short n));
+extern int	is_remote_fs_n __P((short n));
+extern int	is_served_n __P((short n));
+extern int	is_mounted_n __P((short n));
+extern fsblkcnt_t	get_blk_size_n __P((short n));
+extern fsblkcnt_t	get_frag_size_n __P((short n));
+extern fsblkcnt_t	get_blk_used_n __P((short n));
+extern fsblkcnt_t	get_blk_free_n __P((short n));
+extern fsblkcnt_t	get_inode_used_n __P((short n));
+extern fsblkcnt_t	get_inode_free_n __P((short n));
+extern void	set_blk_used_n __P((short n, fsblkcnt_t value));
+extern char	*get_source_name_n __P((short n));
+extern char	*get_fs_name_n __P((short n));
+extern int	load_fsentry __P((struct fstable *fs_entry, char *name,
+		    char *fstype, char *remote_name));
+extern int	isreloc __P((char *pkginstdir));
+extern int	is_local_host __P((char *hostname));
+extern void	fs_tab_free __P((void));
+
+/* pkgdbmerg.c */
+extern int	pkgdbmerg __P((VFP_T *mapvfp, VFP_T *tmpvfp,
+		    struct cfextra **extlist, int notify));
+extern int	files_installed __P((void));
+extern void	notice __P((int n));
+
+/* ocfile.c */
+extern int	trunc_tcfile __P((int fd));
+extern int	ocfile __P((VFP_T **mapvfp, VFP_T **tmpvfp,
+			fsblkcnt_t map_blks));
+extern int	swapcfile __P((VFP_T **a_mapvfp, VFP_T **a_tmpvfp,
+			char *pkginst, int dbchg));
+extern int	set_cfdir __P((char *cfdir));
+extern int	socfile __P((VFP_T **vfp));
+extern int	relslock __P((void));
+extern int	iscfile __P((void));
+extern int	vcfile __P((void));
+
+extern fsblkcnt_t	nblk __P((fsblkcnt_t size, ulong_t bsize,
+			ulong_t frsize));
+extern struct	cfent **procmap __P((VFP_T *vfp, int mapflag, char *ir));
+extern void	repl_cfent __P((struct cfent *new, struct cfent *old));
+extern struct	cfextra **pkgobjmap __P((VFP_T *vfp, int mapflag, char *ir));
+extern void	pkgobjinit __P((void));
+extern int	seed_pkgobjmap __P((struct cfextra *ext_entry, char *path,
+		    char *local));
+extern int	init_pkgobjspace __P((void));
+
+/* eptstat.c */
+extern void	pinfo_free __P((void));
+extern struct	pinfo *eptstat __P((struct cfent *entry, char *pkg, char c));
+
+/* echo.c */
+/*PRINTFLIKE1*/
+extern void	echo __P((char *a_fmt, ...));
+/*PRINTFLIKE1*/
+extern void	echoDebug __P((char *a_fmt, ...));
+extern boolean_t	echoGetFlag __P((void));
+extern boolean_t	echoDebugGetFlag __P((void));
+extern boolean_t	echoSetFlag __P((boolean_t a_debugFlag));
+extern boolean_t	echoDebugSetFlag __P((boolean_t a_debugFlag));
+
+/* psvr4ck.c */
+extern void	psvr4cnflct __P((void));
+extern void	psvr4mail __P((char *list, char *msg, int retcode, char *pkg));
+extern void	psvr4pkg __P((char **ppkg));
+
+/* ptext.c */
+/*PRINTFLIKE2*/
+extern void	ptext __P((FILE *fp, char *fmt, ...));
+
+/* putparam.c */
+extern void	putparam __P((char *param, char *value));
+extern void	getuserlocale __P((void));
+extern void	putuserlocale __P((void));
+extern void	putConditionInfo __P((char *, char *));
+
+/* setadmin.c */
+extern void		setadminFile __P((char *file));
+extern char		*setadminSetting __P((char *a_paramName,
+				char *a_paramValue));
+extern char		*set_keystore_admin __P((void));
+extern boolean_t	get_proxy_port_admin __P((char **, ushort_t *));
+extern boolean_t	check_keystore_admin __P((char **));
+extern int		web_ck_retries __P((void));
+extern int		web_ck_timeout __P((void));
+extern int		web_ck_authentication __P((void));
+
+/* setlist.c */
+extern char	*cl_iscript __P((int idx));
+extern char	*cl_rscript __P((int idx));
+extern void	find_CAS __P((int CAS_type, char *bin_ptr, char *inst_ptr));
+extern int	setlist __P((struct cl_attr ***plist, char *slist));
+extern void	addlist __P((struct cl_attr ***plist, char *item));
+extern char	*cl_nam __P((int cl_idx));
+extern char	*flex_device(char *device_name, int dev_ok);
+extern int	cl_getn __P((void));
+extern int	cl_idx __P((char *cl_nam));
+extern void	cl_sets __P((char *slist));
+extern void	cl_setl __P((struct cl_attr **cl_lst));
+extern void	cl_putl __P((char *parm_name, struct cl_attr **list));
+extern int	cl_deliscript __P((int i));
+extern unsigned	cl_svfy __P((int i));
+extern unsigned	cl_dvfy __P((int i));
+extern unsigned	cl_pthrel __P((int i));
+
+/* passwd.c */
+extern int	pkg_passphrase_cb __P((char *, int, int, void *));
+extern void	set_passarg __P((char *));
+extern void	set_prompt __P((char *));
+
+/* fixpath.c */
+extern void __P(export_client_env(char *));
+extern void __P(set_partial_inst(void));
+extern int __P(is_partial_inst(void));
+extern void __P(set_depend_pkginfo_DB(boolean_t a_setting));
+extern boolean_t __P(is_depend_pkginfo_DB(void));
+extern void __P(disable_spool_create(void));
+extern int __P(is_spool_create(void));
+
+/* open_package_datastream.c */
+extern boolean_t	open_package_datastream(int a_argc, char **a_argv,
+				char *a_spoolto, char *a_device,
+				int *r_repeat, char **r_idsName,
+				char *a_tmpdir, struct pkgdev *a_pkgdev,
+				int a_optind);
+
+/* setup_temporary_directory.c */
+extern boolean_t	setup_temporary_directory(char **r_dirname,
+				char *a_tmpdir, char *a_suffix);
+
+/* unpack_package_from_stream.c */
+extern boolean_t	unpack_package_from_stream(char *a_idsName,
+				char *a_pkginst, char *a_tempDir);
+
+/* pkgops.c */
+
+extern boolean_t	pkgAddPackageToGzonlyList(char *a_pkgInst,
+				char *a_rootPath);
+extern void		pkgAddThisZonePackage(char *a_pkgInst);
+extern boolean_t	pkgRemovePackageFromGzonlyList(char *a_rootPath,
+				char *a_pkgInst);
+extern FILE		*pkgOpenInGzOnlyFile(char *a_rootPath);
+extern void		pkginfoFree(struct pkginfo **r_info);
+extern boolean_t	pkginfoIsPkgInstalled(struct pkginfo **r_pinfo,
+				char *a_pkgInst);
+extern boolean_t	pkgIsPkgInGzOnly(char *a_rootPath, char *a_pkgInst);
+extern boolean_t	pkgIsPkgInGzOnlyFP(FILE *a_fp, char *a_pkgInst);
+extern boolean_t	pkginfoParamTruth(FILE *a_fp, char *a_param,
+				char *a_value, boolean_t a_default);
+extern int		pkgGetPackageList(char ***r_pkgList, char **a_argv,
+				int a_optind, char *a_categories,
+				char **a_categoryList, struct pkgdev *a_pkgdev);
+extern void		pkgLocateHighestInst(char *r_path, int r_pathLen,
+				char *r_pkgInst, int r_pkgInstLen,
+				char *a_rootPath, char *a_pkgInst);
+extern boolean_t	pkgPackageIsThisZone(char *a_pkgInst);
+extern boolean_t	pkgMatchInherited(char *a_src, char *a_dst,
+				char *a_rootDir, char a_mode, time_t a_modtime,
+				char a_ftype, unsigned long a_cksum);
+extern char		*pkgGetGzOnlyPath(void);
+extern boolean_t	pkgTestInstalled(char *a_packageName, char *a_rootPath);
+
+/* depchk.c */
+
+struct depckErrorRecord {
+	int	ier_numZones;
+	char	*ier_packageName;
+	char	**ier_zones;
+	char	**ier_values;
+};
+
+typedef struct depckErrorRecord depckErrorRecord_t;
+
+struct depckError {
+	int			er_numEntries;
+	depckErrorRecord_t	*er_theEntries;
+};
+
+typedef struct depckError depckError_t;
+
+typedef int (depcklFunc_t)(char *a_msg, char *a_pkg);
+
+/*
+ * ignore_values:
+ *	== NULL - record one message for each instance of "name" found
+ *	== "" - record multiple instances
+ *	!= "" - record multiple instances if value not in ignore_values
+ */
+
+struct depckl_struct {
+	char		*name;
+	char		*ignore_values;
+	char		**err_msg;
+	depcklFunc_t	*depcklFunc;
+	depckError_t	*record;
+};
+
+typedef struct depckl_struct depckl_t;
+
+extern int		depchkReportErrors(depckl_t *depckl);
+extern void		depchkRecordError(depckError_t *a_erc,
+				char *a_pkginst, char *a_zoneName,
+				char *a_value);
+
+/* log.c */
+
+/* types of log messages we recognize */
+typedef enum {
+	LOG_MSG_ERR,
+	LOG_MSG_WRN,
+	LOG_MSG_INFO,
+	LOG_MSG_DEBUG
+} LogMsgType;
+
+/*PRINTFLIKE2*/
+extern	void		log_msg(LogMsgType, const char *, ...);
+extern	void		log_set_verbose(boolean_t);
+extern	boolean_t	log_get_verbose(void);
+
+/*
+ * typedef for the 'ckreturn' function
+ */
+typedef void (ckreturnFunc_t)(int a_retcode);
+
+/* sml.c */
+
+/* null reference to SML_TAG object */
+
+#define	SML_TAG__NULL		((SML_TAG*)NULL)
+
+/* null reference to SML_TAG * object */
+
+#define	SML_TAG__R_NULL		((SML_TAG**)NULL)
+
+/* is reference to SML_TAG object valid? */
+
+#define	SML_TAG__ISVALID(tag)	((tag) != (SML_TAG__NULL))
+
+/* is indirect reference to SML_TAG object valid? */
+
+#define	SML_TAG__R_ISVALID(r_tag)	\
+	((r_tag) != ((SML_TAG**)(SML_TAG__NULL)))
+
+/* definitions for sml passed from pkginstall to pkgcond */
+
+#define	PKGCOND_GLOBAL_VARIABLE	"SUNW_PKGCOND_GLOBAL_DATA"
+#define	TAG_COND_TOPLEVEL	"environmentConditionInformation"
+#define	TAG_COND_PARENT_ZONE	"parentZone"
+#define	TAG_COND_CURRENT_ZONE	"currentZone"
+#define	TAG_COND_ZONE_NAME	"zoneName"
+#define	TAG_COND_ZONE_TYPE	"zoneType"
+#define	TAG_COND_INHERITED_FS	"inheritedFileSystem"
+#define	TAG_COND_FS_NAME	"fileSystemName"
+#define	TAG_VALUE_GLOBAL_ZONE	"global"
+#define	TAG_VALUE_NONGLOBAL_ZONE	"nonglobal"
+
+typedef struct _sml_tag_struct SML_TAG;
+typedef struct _sml_parameter_struct SML_PARAM;
+
+struct _sml_tag_struct {
+	char		*name;		/* tag name */
+	int		params_num;	/* # params in *params */
+	SML_PARAM	*params;	/* tag parameters */
+	int		tags_num;	/* # subtags in *tags */
+	SML_TAG		*tags;		/* tag subtags */
+};
+
+struct _sml_parameter_struct {
+	char	*name;		/* tag name */
+	char	*value;		/* parameters */
+};
+
+SML_TAG		*smlAddTag(SML_TAG **r_tag, int a_index,
+			SML_TAG *a_subTag);
+boolean_t	smlFstatCompareEq(struct stat *statbuf,
+				SML_TAG *tag, char *path);
+char		*smlConvertTagToString(SML_TAG *tag);
+/*PRINTFLIKE2*/
+void		smlDbgPrintTag(SML_TAG *a_tag, char *a_format, ...);
+void		smlDelTag(SML_TAG *tag, SML_TAG *sub_tag);
+void		smlDelParam(SML_TAG *tag, char *name);
+SML_TAG		*smlDup(SML_TAG *tag);
+boolean_t	smlFindAndDelTag(SML_TAG *tag, char *findTag);
+void		smlFreeTag(SML_TAG *tag);
+char		*smlGetElementName(SML_TAG *a_tag);
+int		smlGetNumParams(SML_TAG *a_tag);
+char		*smlGetParam(SML_TAG *tag, char *name);
+/*PRINTFLIKE2*/
+char		*smlGetParamF(SML_TAG *tag, char *format, ...);
+void		smlGetParam_r(SML_TAG *tag, char *name, char *buf,
+			int bufLen);
+char		*smlGetParamByTag(SML_TAG *tag, int index,
+			char *tagName, char *parmName);
+char		*smlGetParamByTagParam(SML_TAG *tag, int index,
+			char *tagName, char *parmName, char *parmValue,
+			char *parmReturn);
+char		*smlGetParamName(SML_TAG *tag, int index);
+SML_TAG		*smlGetTag(SML_TAG *tag, int index);
+SML_TAG		*smlGetTagByName(SML_TAG *tag, int index, char *name);
+SML_TAG		*smlGetTagByTagParam(SML_TAG *tag, int index,
+			char *tagName, char *paramName, char *paramValue);
+boolean_t	smlGetVerbose(void);
+int		smlLoadTagFromFile(SML_TAG **r_tag, char *a_fileName);
+SML_TAG		*smlNewTag(char *name);
+boolean_t	smlParamEq(SML_TAG *tag, char *findTag,
+			char *findParam, char *str);
+/*PRINTFLIKE4*/
+boolean_t	smlParamEqF(SML_TAG *tag, char *findTag, char *findParam,
+			char *format, ...);
+void		smlPrintTag(SML_TAG *tag);
+int		smlReadOneTag(SML_TAG **r_tag, char *a_str);
+int		smlConvertStringToTag(SML_TAG **r_tag, char *str);
+void		smlSetFileStatInfo(SML_TAG **tag,
+				struct stat *statbuf, char *path);
+void		smlSetParam(SML_TAG *tag, char *name, char *value);
+/*PRINTFLIKE3*/
+void		smlSetParamF(SML_TAG *tag, char *name, char *format, ...);
+void		smlSetVerbose(boolean_t a_setting);
+int		smlWriteTagToFd(SML_TAG *tag, int fd);
+int		smlWriteTagToFile(SML_TAG *tag, char *filename);
+/*PRINTFLIKE3*/
+void		sml_strPrintf_r(char *a_buf, int a_bufLen, char *a_format, ...);
+/*PRINTFLIKE1*/
+char 		*sml_strPrintf(char *a_format, ...);
+char		*sml_XmlEncodeString(char *a_plainTextString);
+char		*sml_XmlDecodeString(char *a_xmlEncodedString);
+
+#if defined(lint) && !defined(gettext)
+#define	gettext(x)	x
+#endif	/* defined(lint) && !defined(gettext) */
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif	/* __HDRS_LIBINST_H__ */
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/usr/src/cmd/svr4pkg/hdrs/messages.h	Fri Jun 05 10:28:40 2009 -0400
@@ -0,0 +1,1214 @@
+/*
+ * 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
+ * or http://www.opensolaris.org/os/licensing.
+ * 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 2007 Sun Microsystems, Inc.  All rights reserved.
+ * Use is subject to license terms.
+ */
+
+#ifndef	_MESSAGES_H
+#define	_MESSAGES_H
+
+
+/*
+ * Module:	messages
+ * Group:	pkg commands
+ * Description: l10n strings for all pkg commands
+ */
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#define	MSG_MAX	1024
+#define	MAXARGS 100
+#define	MAX_CAT_ARGS 64
+
+/* BEGIN CSTYLED */
+
+/*
+ * I18N: these messages are questions asked of the user
+ */
+
+#define	ASK_CONFIRM			gettext("Do you want to remove this package?")
+#define	ASK_CONT			gettext("Do you want to continue with the installation of <%s>")
+#define	ASK_CONTINUE_ADD		gettext("Do you want to continue with package installation?")
+#define	ASK_CONTINUE_RM			gettext("Do you want to continue with package removal?")
+#define	ASK_PKGREMOVE_CONTINUE		gettext("Do you want to continue with the removal of this package")
+#define	ASK_PKGRMCHK_CONT		gettext("Do you want to continue with the removal of <%s>")
+
+/*
+ * I18N: these messages are debugging message and are only displayed
+ * when special debugging output has been enabled - these messages
+ * will never be displayed during normal product usage
+ */
+
+#define	DBG_ADDPACKAGES_ARGS		gettext("npkgs <%d> uri <%s> stream device <%s> repeat <%d> altBinDir <%s> device <%s>")
+#define	DBG_ADDPACKAGES_ENTRY		gettext("add_packages:")
+#define	DBG_ADDPACKAGES_GZ_NO_LZ_ARGS	gettext("npkgs <%d> uri <%s> stream device <%s> repeat <%d> device <%s>")
+#define	DBG_ADDPACKAGES_GZ_NO_LZ_ENTRY	gettext("add_pkgs_in_gz_no_zones: adding packages in global zone with NO non-global zones")
+#define	DBG_ADDPACKAGES_GZ_W_LZ_ARGS	gettext("npkgs <%d> uri <%s> stream device <%s> repeat <%d> device <%s>")
+#define	DBG_ADDPACKAGES_GZ_W_LZ_ENTRY	gettext("add_pkgs_in_gz_with_zones: adding packages in global zone with non-global zones present")
+#define	DBG_ADDPACKAGES_LZ_ARGS		gettext("npkgs <%d> uri <%s> stream device <%s> repeat <%d> device <%s>")
+#define	DBG_ADDPACKAGES_LZ_ENTRY	gettext("add_pkgs_in_lz: adding packages in non-global zone")
+#define	DBG_ARG				gettext("argument <%d> = <%s>")
+#define	DBG_BOOTCHECKINSTALLINZONES_ARGS gettext("ids <%s> admin <%s> tempdir <%s>")
+#define	DBG_BOOTCHECKINSTALLINZONES_ENTRY gettext("boot_and_check_install_in_zones:")
+#define	DBG_BOOTING_ZONE		gettext("booting up non-running zone <%s>")
+#define	DBG_BOOTINSTALLINZONES_ARGS	gettext("ids <%s> admin <%s> tempdir <%s>")
+#define	DBG_BOOTINSTALLINZONES_ENTRY	gettext("boot_and_install_in_zones:")
+#define	DBG_BRANDS_ARE_IMPLEMENTED	gettext("brands are implemented")
+#define	DBG_BRANDS_NOT_IMPLEMENTED	gettext("brands are NOT implemented")
+#define	DBG_CANNOT_GET_PKGLIST		gettext("unable to get package list")
+#define	DBG_CHECKAPP_ARGS		gettext("package <%s> directory <%s> rootpath <%s>")
+#define	DBG_CHECKAPP_ENTRY		gettext("check install applicability:")
+#define	DBG_CHECKAPP_THISZONE_INSTREQ	gettext("WARNING: the package <%s> to be installed does not contain a request script, but the currently installed instance (package <%s>) does contain a request script, so the package to be installed can only be installed in the current zone, and will not be installed in any future zones created.")
+#define	DBG_CHECKAPP_THISZONE_REQUEST	gettext("WARNING: package <%s> contains a request script, and can only be installed in the current zone, and will not be installed in any future zones created.")
+#define	DBG_CHECKINSTALL_IN_ZONE	gettext("checking install of package <%s> in zone <%s> from stream <%s>")
+#define	DBG_CHECKREMOVE_PKG_IN_ZONE	gettext("verifying package <%s> dependencies in zone <%s>")
+#define	DBG_CLOSING_STREAM		gettext("closing datastream <%s> at <%s>")
+#define	DBG_CONVERTING_PKG		gettext("converting package <%s/%s> to stream <%s>")
+#define	DBG_COPY_FILE			gettext("copy <%s> to <%s>")
+#define	DBG_CPPATH_ENTRY		gettext("copy path: control <0x%02x> mode <0%04lo> source <%s> destination <%s>")
+#define	DBG_CREATED_ZONE_ADMINFILE	gettext("created temporary zone administration file <%s>")
+#define	DBG_CREATED_ZONE_TEMPDIR	gettext("created temporary zone directory <%s>")
+#define	DBG_CREATE_ZONE_ADMINFILE	gettext("create temporary zone administration file in directory <%s> template <%s>")
+#define	DBG_CREATE_ZONE_TEMPDIR		gettext("create temporary zone directory in temporary directory <%s>")
+#define	DBG_DEPCHK_COLLECT_ERROR	gettext("dependency report error: ign <null> ret <%d> package <%s> msg <%s>")
+#define	DBG_DEPCHK_COLLECT_IGNORE	gettext("dependency report error: ign <null> no check function package <%s> msg <%s>")
+#define	DBG_DEPCHK_ENTRY		gettext("depchkReportErrors:")
+#define	DBG_DEPCHK_IGNORE_ERROR		gettext("dependency report error: ign <%s> no check function package <%s> msg <%s>")
+#define	DBG_DEPCHK_RECORD_ERROR		gettext("dependency record error: erc <0x%08lx> add first package <%s> zone <%s> value <%s>")
+#define	DBG_DEPCHK_RECORD_PERROR	gettext("dependency record error: erc <0x%08lx> add package <%d> <%s> zone <%s> value <%s>")
+#define	DBG_DEPCHK_RECORD_ZERROR	gettext("dependency record error: erc <0x%08lx> add zone <%s> value <%s> to existing package <%s> # <%d> zones[0] <%s>")
+#define	DBG_DEPCHK_REPORT_ERROR		gettext("dependency report error: ign <%s> ret <%d> package <%s> msg <%s>")
+#define	DBG_DOMERG_NOT_THERE		gettext("object does not exist or has incorrect contents: type <%c> class <%s> path <%s>")
+#define	DBG_DOMERG_NOT_WRITABLE		gettext("object not writable or cannot be created: type <%c> class <%s> path <%s>")
+#define	DBG_DOMERG_NO_SUCH_FILE		gettext("file does not exist or has incorrect contents: type <%c> class <%s> path <%s>")
+#define	DBG_DOREMOVE_ARGS		gettext("found package <%s> name <%s> arch <%s> version <%s> basedir <%s> catg <%s> status <%d>\n")
+#define	DBG_DOREMOVE_ENTRY		gettext("doremove:")
+#define	DBG_DOREMOVE_INTERRUPTED	gettext("interrupted: package <%s> not installed")
+#define	DBG_DO_EXEC_REQUEST_USER	gettext("running request script <%s> output <%s> as user <%s> i.d. <%ld> group <%s> i.d. <%ld>")
+#define	DBG_ENTRY_IN_GZ			gettext("[<%s> in global zone]")
+#define	DBG_ENTRY_IN_LZ			gettext("[<%s> in non-global zone <%ld>:<%s>]")
+#define	DBG_EXIT_WITH_CODE		gettext("exiting with code <%d>")
+#define	DBG_FINALCK_ERROR		gettext("final check (error): attrchg <%d> contchg <%d> ftype <%c> path <%s>")
+#define	DBG_FINALCK_ERROR_AVERIFY	gettext("final check (error): attribute verification = <%d>")
+#define	DBG_FINALCK_ERROR_CVERIFY	gettext("final check (error): content verification = <%d>")
+#define	DBG_FINALCK_EXIT		gettext("final check (return): error <%d> type <%c> path <%s>")
+#define	DBG_FINALCK_WARNING		gettext("final check (warning): attrchg <%d> contchg <%d> ftype <%c> path <%s>")
+#define	DBG_FINALCK_WARNING_AVERIFY	gettext("final check (warning): attribute verification = <%d>")
+#define	DBG_FINALCK_WARNING_CVERIFY	gettext("final check (warning): content verification = <%d>")
+#define	DBG_GETPKGLIST_ARGS		gettext("stream device <%s> directory <%s> repeat <%d>")
+#define	DBG_GETPKGLIST_ENTRY		gettext("get_package_list:")
+#define	DBG_INSTALLING_TO_SPOOL		gettext("installing packages to spool directory <%s>")
+#define	DBG_INSTALLINZONES_ARGS		gettext("ids <%s> admin <%s> tempdir <%s>")
+#define	DBG_INSTALLINZONES_ENTRY	gettext("install_in_zones:")
+#define	DBG_INSTALL_FLAG_VALUES		gettext("%s: admnflag <%d> doreboot <%d> failflag <%d> interrupted <%d> intrflag <%d> ireboot <%d> needconsult <%d> nullflag <%d> warnflag <%d>")
+#define	DBG_INSTALL_IN_ZONE		gettext("installing package <%s> in zone <%s> from stream <%s>")
+#define	DBG_INSTALL_SKIP_THISZONE	gettext("skipping installation of package <%s>: marked this zone only")
+#define	DBG_INSTINONEZONE_ARGS		gettext("zone <%s> ids <%s> admin <%s> tempdir <%s> altbindir <%s>")
+#define	DBG_INSTINONEZONE_ENTRY		gettext("install_in_one_zone:")
+#define	DBG_INSTVOL_CAS_INFO		gettext("is partial <%d> updated <%s> skipped <%s> local <%s>\n")
+#define	DBG_INSTVOL_NOT_RUNNING_CAS	gettext("not running zone <%s> object <%s> class <%s> action script <%s>")
+#define	DBG_INSTVOL_OBJ_LOCAL		gettext("objects local <%s>")
+#define	DBG_INSTVOL_OBJ_SKIPPED		gettext("objects skipped <%s>")
+#define	DBG_INSTVOL_OBJ_UPDATED		gettext("objects updated <%s>")
+#define	DBG_INSTVOL_PARTIAL_INST	gettext("partial install check: pkgMatchInherited(srcp <%s>, dstp <%s>, get_inst_root() <%s>, ept->ainfo.mode <0%04lo>, ept->cinfo.modtime <%lx>, ept->ftype <%c>, ept->cinfo.cksum <%lx>) = %d")
+#define	DBG_INSTVOL_RUNNING_CAS		gettext("running zone <%s> object <%s> class <%s> action script <%s>")
+#define	DBG_IN_GZ_NO_LZ			gettext("running in global zone with NO non-global zones")
+#define	DBG_IN_GZ_WITH_LZ		gettext("running in global zone with non-global zones")
+#define	DBG_IN_LZ			gettext("running in non-global zone")
+#define	DBG_MERGINFOS_ASK_BASEDIR	gettext("merg_pkginfos: ask for BASEDIR change later")
+#define	DBG_MERGINFOS_ENTRY		gettext("merg_pkginfos: installed pkginfo <%s>")
+#define	DBG_MERGINFOS_EXIT		gettext("merg_pkginfos: done changing <%s> result <%d>")
+#define	DBG_MERGINFOS_SET_BASEDIR	gettext("merg_pkginfos: set BASEDIR to <%s>")
+#define	DBG_MERGINFOS_SET_CHANGE	gettext("merg_pkginfos: change existing attribute <%s> from <%s> to <%s>")
+#define	DBG_MERGINFOS_SET_CLASSES	gettext("merg_pkginfos: set CLASSES to <%s>")
+#define	DBG_MERGINFOS_SET_DUPLICATE	gettext("merg_pkginfos: set existing attribute <%s> to current value <%s>")
+#define	DBG_MERGINFOS_RETAIN_OLD	gettext("merg_pkginfos: retain existing attribute <%s> value <%s>")
+#define	DBG_MERGINFOS_SET_TO		gettext("merg_pkginfos: validate change attribute <%s> from <%s>")
+#define	DBG_MERGINFO_ATTRCOMP		gettext("merginfo: attribute <%s> currently set to <%s>")
+#define	DBG_MERGINFO_DIFFERENT		gettext("merginfo: pkginfo file source <%s> different than merged <%s>: open source pkginfo file")
+#define	DBG_MERGINFO_ENTRY		gettext("merginfo: instdir <%s> get_inst_root() <%s> saveSpoolInstallDir <%s> pkgloc <%s> is_spool_create <%d> get_info_basedir() <%s> installed pkginfo <%s> merged pkginfo <%s>")
+#define	DBG_MERGINFO_EXCLUDING		gettext("merginfo: excluding attribute <%s>")
+#define	DBG_MERGINFO_FINAL		gettext("merginfo: accepting attribute <%s>")
+#define	DBG_MERGINFO_GREATER_THAN	gettext("merginfo: attribute <%s> greater than last entry <%s>")
+#define	DBG_MERGINFO_LESS_THAN		gettext("merginfo: attribute <%s> less than first entry <%s>")
+#define	DBG_MERGINFO_SAME		gettext("merginfo: pkginfo file source and merged <%s> identical: no source pkginfo file used")
+#define	DBG_MERGINFO_SEARCHING		gettext("merginfo: attribute <%s> within range of <%s> and <%s>: searching")
+#define	DBG_NUM_PKGS_TO_ADD		gettext("number of packages to add <%d>")
+#define	DBG_NUM_PKGS_TO_REMOVE		gettext("number of packages to remove <%d> longest package name length <%d>")
+#define	DBG_ODS_ARGS			gettext("bdevice <%s> cdevice <%s> pathname <%s> argc <%d> spool-device <%s>")
+#define	DBG_ODS_DATASTREAM_BDEV		gettext("package source is block device <%s>")
+#define	DBG_ODS_DATASTREAM_CDEV		gettext("package source is character device <%s>")
+#define	DBG_ODS_DATASTREAM_INIT		gettext("initializing package datastream <%s>")
+#define	DBG_ODS_DATASTREAM_ISFILE	gettext("package source is ordinary file <%s>")
+#define	DBG_ODS_DATASTREAM_MOUNTING	gettext("mounting package datastream device <%s> on <%s>")
+#define	DBG_ODS_DATASTREAM_UNK		gettext("package source not contained in a recognized datastream")
+#define	DBG_ODS_ENTRY			gettext("open_package_datastream:")
+#define	DBG_PKGADD_ADMINFILE		gettext("using admin file <%s>")
+#define	DBG_PKGADD_CKRETURN		gettext("check return code <%d> package <%s> function <add packages>")
+#define	DBG_PKGADD_ENABLING_HOLLOW	gettext("enabling hollow package support")
+#define	DBG_PKGADD_HOLLOW_ENABLED	gettext("hollow package support is enabled")
+#define	DBG_PKGADD_PKGPATHS		gettext("locations set: pkg <%s> adm <%s>")
+#define	DBG_PKGADD_RESPFILE		gettext("using response file <%s> directory <%s>")
+#define	DBG_PKGADD_TMPDIR		gettext("using temporary directory <%s>")
+#define	DBG_PKGDBMRG_INHERITED		gettext("path inherited and assumed correct: <%s>")
+#define	DBG_PKGINSTALL_ADMINFILE	gettext("using admin file <%s>")
+#define	DBG_PKGINSTALL_ARGS		gettext("package <%s> dirname <%s> bdevice <%s> mount <%s> ir <%s> idsName <%s> pkgdir <%s>")
+#define	DBG_PKGINSTALL_COC_DBUPD	gettext("skipping checkinstall package <%s> script <%s> zone <%s> (db update only)")
+#define	DBG_PKGINSTALL_COC_NODEL	gettext("skipping checkinstall package <%s> script <%s> zone <%s> (nodelete)")
+#define	DBG_PKGINSTALL_COC_NONE		gettext("no checkinstall in package <%s> script <%s> zone <%s>")
+#define	DBG_PKGINSTALL_DS_ISFILE	gettext("package source <%s> is an ordinary file - treating as a package data stream")
+#define	DBG_PKGINSTALL_ENTRY		gettext("pkgInstall:")
+#define	DBG_PKGINSTALL_EXECOC_GZ	gettext("executing checkinstall package <%s> script <%s>")
+#define	DBG_PKGINSTALL_EXECOC_LZ	gettext("executing checkinstall package <%s> script <%s> zone <%s>")
+#define	DBG_PKGINSTALL_EXEPIC_GZ	gettext("executing postinstall package <%s> script <%s>")
+#define	DBG_PKGINSTALL_EXEPIC_LZ	gettext("executing postinstall package <%s> script <%s> zone <%s>")
+#define	DBG_PKGINSTALL_EXEPOC_GZ	gettext("executing preinstall package <%s> script <%s>")
+#define	DBG_PKGINSTALL_EXEPOC_LZ	gettext("executing preinstall package <%s> script <%s> zone <%s>")
+#define	DBG_PKGINSTALL_HAS_LOCKFILE	gettext("before removing package <%s> found existing lockfile <%s> zone <%s>")
+#define	DBG_PKGINSTALL_INSDONE		gettext("install completed: hollow support <%d> is hollow <%d> fresh install <%d> updated <%s> skipped <%s> script <%s> access <%d>")
+#define	DBG_PKGINSTALL_POCALT_NONE	gettext("no pkgbin preinstall package <%s> script <%s> zone <%s>")
+#define	DBG_PKGINSTALL_POC_DBUPD	gettext("skipping preinstall package <%s> script <%s> zone <%s> (db update only)")
+#define	DBG_PKGINSTALL_POC_NONE		gettext("has no media preinstall package <%s> script <%s> zone <%s>")
+#define	DBG_PKGINSTALL_POIS_DBUPD	gettext("skipping postinstall package <%s> script <%s> zone <%s> (db update only)")
+#define	DBG_PKGINSTALL_POIS_NONE	gettext("no postinstall in package <%s> script <%s> zone <%s>")
+#define	DBG_PKGINSTALL_POIS_NOPATH	gettext("no postinstall in package <%s> zone <%s>")
+#define	DBG_PKGINSTALL_POIS_SKIPPING	gettext("all objects skipped when installing in zone <%s>: skipping postinstall package <%s> script <%s>")
+#define	DBG_PKGINSTALL_PREINSCHK	gettext("preinstallation check of package <%s> zone <%s>")
+#define	DBG_PKGINSTALL_PREINSCHK_OK	gettext("preinstall check successful")
+#define	DBG_PKGINSTALL_RSCRIPT_IS_ROOT	gettext("request script run as root = <%d>")
+#define	DBG_PKGINSTALL_RSCRIPT_NOT_SET	gettext("admin file parameter <%s> is not set")
+#define	DBG_PKGINSTALL_RSCRIPT_SET_TO	gettext("admin file parameter <%s> is set to <%s>")
+#define	DBG_PKGINSTALL_TMPDIR		gettext("using temporary directory <%s>")
+#define	DBG_PKGLIST_ERROR		gettext("unable to get package list from device <%s> directory <%s>: fatal error <%d>")
+#define	DBG_PKGLIST_NONFOUND		gettext("unable to get package list from device <%s> directory <%s>: no packages found")
+#define	DBG_PKGLIST_RM_ERROR		gettext("unable to get package list from directory <%s>: fatal error <%d>")
+#define	DBG_PKGLIST_RM_NONFOUND		gettext("unable to get package list from directory <%s>: no packages found")
+#define	DBG_PKGOPS_ADDED_GZPKG		gettext("added package <%s> to global zone only file")
+#define	DBG_PKGOPS_ADDGZPKG		gettext("add package <%s> to global zone only file at <%s>")
+#define	DBG_PKGOPS_ADD_TZP		gettext("add package entry <%d> instance <%s> as this zone only")
+#define	DBG_PKGOPS_CKSUM_MISMATCH	gettext("checksum <%s>:<0x%08lx> does not match <%s>:<0x%08lx>")
+#define	DBG_PKGOPS_EDITABLE_EXISTS	gettext("editable file <%s> exists: ok")
+#define	DBG_PKGOPS_GETPKGLIST_ARGS	gettext("directory <%s> category <%s>")
+#define	DBG_PKGOPS_GETPKGLIST_ENTRY	gettext("pkgGetPackageList:")
+#define	DBG_PKGOPS_GPKGLIST_CATFAILED	gettext("no packages found for category <%s>")
+#define	DBG_PKGOPS_GPKGLIST_CATOK	gettext("successfully generated package list for category <%s>")
+#define	DBG_PKGOPS_GPKGLIST_EINTR	gettext("search interrupted looking for packages from list of packages specified")
+#define	DBG_PKGOPS_GPKGLIST_ENOPKG	gettext("no packages found from list of packages specified")
+#define	DBG_PKGOPS_GPKGLIST_ESRCH	gettext("search failed looking for packages from list of packages specified")
+#define	DBG_PKGOPS_GPKGLIST_OK		gettext("successfully generated package list from list of packages specified")
+#define	DBG_PKGOPS_GPKGLIST_UNKNOWN	gettext("unknown value <%d> returned from gpkglist")
+#define	DBG_PKGOPS_IS_INHERITED		gettext("path <%s> is inherited from <%s>")
+#define	DBG_PKGOPS_IS_NOT_THISZONE	gettext("package <%s> is NOT this zone only")
+#define	DBG_PKGOPS_IS_THISZONE		gettext("package <%s> is this zone only")
+#define	DBG_PKGOPS_LOCHIGH_ARGS		gettext("rootpath <%s> pkginst <%s>")
+#define	DBG_PKGOPS_LOCHIGH_ENTRY	gettext("pkgLocateHighestInst:")
+#define	DBG_PKGOPS_LOCHIGH_INSTANCE	gettext("instance <%d> = pkginst <%s> name <%s> arch <%s> version <%s> vendor <%s> basedir <%s> catg <%s> status <0x%02x>")
+#define	DBG_PKGOPS_LOCHIGH_RETURN	gettext("npkgs is <%d> returned pkginst <%s> path <%s>")
+#define	DBG_PKGOPS_LOCHIGH_WILDCARD	gettext("package <%s> wild card specification <%s>")
+#define DBG_PKGOPS_MATCHINHERIT_ARGS    gettext("<%s> vs <%s> root <%s> mode <0%04o> modtime <0x%08lx> ftype <%c> cksum <0x%08lx>")
+#define	DBG_PKGOPS_MATCHINHERIT_ENTRY	gettext("match inherited:")
+#define	DBG_PKGOPS_MOD_MISMATCH		gettext("mod time <%s>:<0x%08lx> does not match <%s>:<0x%08lx>")
+#define	DBG_PKGOPS_NOT_THISZONE		gettext("package <%s> is NOT this zone only: no this zone only packages")
+#define	DBG_PKGOPS_PARAMTRUTH_RESULTS	gettext("lookup param <%s> compare-value <%s> default-value <%s> param-is <%s> result <%s>")
+#define	DBG_PKGOPS_PKGINFO_RETURNED	gettext("pkginfo for path <%s> returned <%d>")
+#define	DBG_PKGOPS_PKG_IS_GZONLY	gettext("package <%s> IS recorded as installed in the global zone only")
+#define	DBG_PKGOPS_PKG_NOT_GZONLY	gettext("package <%s> not recorded as installed in the global zone only")
+#define	DBG_PKGOPS_REMOVED_GZPKG	gettext("removed package <%s> from global zone only file")
+#define	DBG_PKGOPS_VOLATILE_EXISTS	gettext("volatile file <%s> exists")
+#define	DBG_PKGREMOVE_ADMINFILE		gettext("using admin file <%s>")
+#define	DBG_PKGREMOVE_ARGS		gettext("package <%s> dirname <%s> nodelete <%d> adminFile <%s>")
+#define	DBG_PKGREMOVE_ENTRY		gettext("pkgRemove:")
+#define	DBG_PKGREMOVE_EXEPIC_GZ		gettext("executing postremove package <%s> script <%s>.")
+#define	DBG_PKGREMOVE_EXEPIC_LZ		gettext("executing postremove package <%s> script <%s> zone <%s>.")
+#define	DBG_PKGREMOVE_EXEPOC_GZ		gettext("executing preremove package <%s> script <%s>.")
+#define	DBG_PKGREMOVE_EXEPOC_LZ		gettext("executing preremove package <%s> script <%s> zone <%s>.")
+#define	DBG_PKGREMOVE_HOLLOW_DISABLED	gettext("hollow package support is disabled")
+#define	DBG_PKGREMOVE_HOLLOW_ENABLED	gettext("hollow package support is enabled")
+#define	DBG_PKGREMOVE_PIC_DBUPD		gettext("skipping postremove package <%s> script <%s> zone <%s> (db update only)")
+#define	DBG_PKGREMOVE_PIC_NODEL		gettext("skipping postremove package <%s> script <%s> zone <%s> (nodelete)")
+#define	DBG_PKGREMOVE_PIC_NONE		gettext("package <%s> zone <%s> has no postremove script")
+#define	DBG_PKGREMOVE_POC_DBUPD		gettext("skipping preremove package <%s> script <%s> zone <%s> (db update only)")
+#define	DBG_PKGREMOVE_POC_NODEL		gettext("skipping preremove package <%s> script <%s> zone <%s> (nodelete)")
+#define	DBG_PKGREMOVE_POC_NONE		gettext("package <%s> zone <%s> has no preremove script")
+#define	DBG_PKGREMOVE_PRERMCHK		gettext("preremoval check of package <%s> zone <%s>")
+#define	DBG_PKGREMOVE_PRERMCHK_OK	gettext("preremoval check successful")
+#define	DBG_PKGREMOVE_PROCPKG_GZ	gettext("begin processing package <%s> information lockfile <%s>")
+#define	DBG_PKGREMOVE_PROCPKG_LZ	gettext("begin processing package <%s> information lockfile <%s> zone <%s>")
+#define	DBG_PKGREMOVE_REM		gettext("performing class removal package <%s> zone <%s>")
+#define	DBG_PKGREMOVE_REM_DBUPD		gettext("skipping class removal package <%s> zone <%s> (db update only)")
+#define	DBG_PKGREMOVE_REM_NODEL		gettext("skipping class removal package <%s> zone <%s> (nodelete)")
+#define	DBG_PKGREMOVE_TMPDIR		gettext("using temporary directory <%s>")
+#define	DBG_PKGREMPKGSGZNNGZ_ARGS	gettext("nodelete <%d> longest package <%d> repeat <%d> altbindir <%s>")
+#define	DBG_PKGREMPKGSGZNNGZ_ENTRY	gettext("remove_packages_in_global_no_zones:")
+#define	DBG_PKGREMPKGSGZWNGZ_ARGS	gettext("nodelete <%d> longest package <%d> repeat <%d> altbindir <%s> pkgdir <%s>")
+#define	DBG_PKGREMPKGSGZWNGZ_ENTRY	gettext("remove_packages_in_global_with_zones:")
+#define	DBG_PKGREMPKGSNGZ_ARGS		gettext("nodelete <%d> longest package <%d> repeat <%d> altbindir <%s> pkgdir <%s>")
+#define	DBG_PKGREMPKGSNGZ_ENTRY		gettext("remove_packages_in_nonglobal_zone:")
+#define	DBG_PKGRM_ADMINFILE		gettext("using admin file <%s>")
+#define	DBG_PKGRM_CKRETURN		gettext("check return code <%d> package <%s> function <remove packages>")
+#define	DBG_PKGRM_ENABLING_HOLLOW	gettext("enabling hollow package support")
+#define	DBG_PKGRM_HOLLOW_ENABLED	gettext("hollow package support is enabled")
+#define	DBG_PKGRM_TMPDIR		gettext("using temporary directory <%s>")
+#define	DBG_PKGZONECHECKINSTALL_ARGS	gettext("zone <%s> package <%s> dirname <%s> bdevice <%s> mount <%s> ir <%s> idsName <%s> adminFile <%s> stdout <%s>")
+#define	DBG_PKGZONECHECKINSTALL_ENTRY	gettext("pkgZoneCheckInstall:")
+#define	DBG_PKGZONECHECKREMOVE_ARGS	gettext("zone <%s> package <%s> dirname <%s> adminFile <%s> stdoutpath <%s>")
+#define	DBG_PKGZONECHECKREMOVE_ENTRY	gettext("pkgZoneCheckRemove:")
+#define	DBG_PKGZONEINSTALL_ARGS		gettext("zone <%s> package <%s> dirname <%s> bdevice <%s> mount <%s> ir <%s> idsName <%s> adminFile <%s>")
+#define	DBG_PKGZONEINSTALL_ENTRY	gettext("pkgZoneInstall:")
+#define	DBG_PKGZONEREMOVE_ARGS		gettext("zone <%s> package <%s> dirname <%s> nodelete <%d> adminFile <%s>")
+#define	DBG_PKGZONEREMOVE_ENTRY		gettext("pkgZoneRemove:")
+#define	DBG_PKG_INSTALLED		gettext("package <%s> is installed at <%s>")
+#define	DBG_PKG_IN_DIR			gettext("package <%s> available in directory <%s>")
+#define	DBG_PKG_NOT_INSTALLED		gettext("package <%s> is not installed at <%s>")
+#define	DBG_PKG_SELECTED		gettext("-> package [%d] = <%s>")
+#define	DBG_PKG_TEST_EXISTENCE		gettext("test existence of package <%s> at <%s>")
+#define	DBG_PREIVFY_CKCFCONTENT		gettext("check content conflict: package <%s> message <%s>")
+#define	DBG_PREIVFY_CKCONFLICT		gettext("check conflicting installed object: package <%s> message <%s>")
+#define	DBG_PREIVFY_CKDEPEND		gettext("check dependency: package <%s> message <%s>")
+#define	DBG_PREIVFY_CKDIRS		gettext("check directories: package <%s> message <%s>")
+#define	DBG_PREIVFY_CKINSTANCE		gettext("check instance: package <%s> message <%s>")
+#define	DBG_PREIVFY_CKPARTIALINSTALL	gettext("check partially installed: package <%s> message <%s>")
+#define	DBG_PREIVFY_CKPARTIALREMOVE	gettext("check partially removed: package <%s> message <%s>")
+#define	DBG_PREIVFY_CKPKGDIRS		gettext("check package directories: package <%s> message <%s>")
+#define	DBG_PREIVFY_CKPKGFILEBAD	gettext("check file bad: package <%s> message <%s>")
+#define	DBG_PREIVFY_CKPKGFILES		gettext("check package files: package <%s> message <%s>")
+#define	DBG_PREIVFY_CKPRENCI		gettext("check prerequisite incomplete: package <%s> message <%s>")
+#define	DBG_PREIVFY_CKPREREQ		gettext("check prerequisite installed: package <%s> message <%s>")
+#define	DBG_PREIVFY_CKPRIV		gettext("check privileges: package <%s> message <%s>")
+#define	DBG_PREIVFY_CKRUNLEVEL		gettext("check run level: package <%s> message <%s>")
+#define	DBG_PREIVFY_CKSETUID		gettext("check setuid: package <%s> message <%s>")
+#define	DBG_PREIVFY_CKSPACE		gettext("check space: package <%s> message <%s>")
+#define	DBG_PREIVFY_ENTRY		gettext("performing preinstallation dependency verification")
+#define	DBG_PREIVFY_GETYORN_ARGS	gettext("package <%s> nocheck <%d> quit <%d> message <%s> admin-msg <%s>")
+#define	DBG_PREIVFY_GETYORN_CKYORN	gettext("package <%s> ckyorn return non-zero <%d>")
+#define	DBG_PREIVFY_GETYORN_NOCHECK	gettext("package <%s> no check - return <0> (success)")
+#define	DBG_PREIVFY_GETYORN_NOT_Y	gettext("package <%s> ckyorn answer <%s> - return <3> (interruption)")
+#define	DBG_PREIVFY_GETYORN_QUIT	gettext("package <%s> quit - return <4> (administration)")
+#define	DBG_PREIVFY_GETYORN_QUIT_USER	gettext("package <%s> noninteractive mode - return <5> (administration required)")
+#define	DBG_PREIVFY_GETYORN_SUCCESS	gettext("package <%s> continue installation")
+#define	DBG_PREIVFY_NOFILE		gettext("unable to perform preinstallation check of package <%s> in zone <%s> data file <%s>: %s")
+#define	DBG_PREIVFY_SCAN		gettext("scanning for line <%s> found package <%s> zone <%s>")
+#define	DBG_PREIVFY_SKIP_THISZONE	gettext("skipping preinstall verification of package <%s>: marked this zone only")
+#define	DBG_PRERVFY_ENTRY		gettext("performing preremoval dependency verification")
+#define	DBG_PRERVFY_GETYORN_ARGS	gettext("package <%s> nocheck <%d> quit <%d> message <%s> admin-msg <%s>")
+#define	DBG_PRERVFY_GETYORN_CKYORN	gettext("package <%s> ckyorn return non-zero <%d>")
+#define	DBG_PRERVFY_GETYORN_NOCHECK	gettext("package <%s> no check - return <0> (success)")
+#define	DBG_PRERVFY_GETYORN_NOT_Y	gettext("package <%s> ckyorn answer <%s> - return <3> (interruption)")
+#define	DBG_PRERVFY_GETYORN_QUIT	gettext("package <%s> quit - return <4> (administration)")
+#define	DBG_PRERVFY_GETYORN_QUIT_USER	gettext("package <%s> noninteractive mode - return <5> (administration required)")
+#define	DBG_PRERVFY_GETYORN_SUCCESS	gettext("package <%s> continue removal")
+#define	DBG_PRERVFY_NOFILE		gettext("unable to perform preremoval check of package <%s> in zone <%s> data file <%s>: %s")
+#define	DBG_PRERVFY_RCKDEPEND		gettext("check dependency: package <%s> message <%s>")
+#define	DBG_PRERVFY_RCKDEPSONME		gettext("check depends on this package: package <%s> message <%s>")
+#define	DBG_PRERVFY_RCKPRENCI		gettext("check prerequisite incomplete: package <%s> message <%s>")
+#define	DBG_PRERVFY_RCKPREREQ		gettext("check prerequisite installed: package <%s> message <%s>")
+#define	DBG_PRERVFY_RCKPRIV		gettext("check privileges: package <%s> message <%s>")
+#define	DBG_PRERVFY_RCKRUNLEVEL		gettext("check run level: package <%s> message <%s>")
+#define	DBG_PRERVFY_SCAN		gettext("scanning for line <%s> found package <%s> zone <%s>")
+#define	DBG_PUTPARAM_PUTCONDINFO_ENTRY	gettext("generating environment condition information")
+#define	DBG_PUTPARAM_PUTCONDINFO_EXIT	gettext("environment condition information is <%s>")
+#define	DBG_QUIT_REMOVING_PKGDIR	gettext("install not yet started and not updating existing: removing package directory <%s>")
+#define	DBG_QUIT_REMOVING_PKGSAV	gettext("install started and updating existing: removing package temp directory <%s>")
+#define	DBG_REMOVEPKGS_ARGS		gettext("npkgs <%d> nodelete <%d> longest pkg <%d> repeat <%d> pkgdir <%s> spooldir <%s>")
+#define	DBG_REMOVEPKGS_ENTRY		gettext("remove_packages:")
+#define	DBG_REMOVE_FLAG_VALUES		gettext("%s: admnflag <%d> doreboot <%d> failflag <%d> interrupted <%d> intrflag <%d> ireboot <%d> nullflag <%d> warnflag <%d>")
+#define	DBG_REMOVE_PKGS_FROM_SPOOL	gettext("removing packages from spool directory <%s>")
+#define	DBG_REMOVE_PKG_FROM_ZONE	gettext("removing package <%s> from zone <%s>")
+#define	DBG_REMOVING_DSTREAM_PKGDIR	gettext("removing temporary stream <%s> for package <%s>")
+#define	DBG_REMOVING_DSTREAM_TMPDIR	gettext("removing package datastream temporary directory <%s>")
+#define	DBG_REMOVING_DWNLD_TMPDIR	gettext("removing download temporary directory <%s>")
+#define	DBG_REMOVING_PKG_TMPDIR		gettext("removing temporary directory <%s> for package <%s>")
+#define	DBG_REMOVING_ZONE_TMPDIR	gettext("removing zones temporary directory <%s>")
+#define	DBG_RESTORE_ZONE_STATE		gettext("restoring state of zone <%s>")
+#define	DBG_SETUP_TEMPDIR		gettext("created temporary directory <%s>")
+#define	DBG_SKIPPING_ZONE		gettext("skipping processing of zone <%s>: zone not running")
+#define	DBG_SKIPPING_ZONE_BOOT		gettext("not booting zone <%s>: zone is running")
+#define	DBG_SKIPPING_ZONE_NOT_RUNNABLE	gettext("not booting zone <%s>: zone cannot be booted")
+#define	DBG_SML_ADD_TAG			gettext("add element <%s> to tag <%s>")
+#define	DBG_SML_CREATED_NEW_TAG_OBJECT	gettext("new tag <0x%08lx> name=<%s> created")
+#define	DBG_SML_CREATE_NEW_TAG_OBJECT	gettext("create new tag name=<%s>")
+#define	DBG_SML_DELETE_PARAM		gettext("delete parameter tag <%s> name <%s>: ")
+#define	DBG_SML_DELETE_PARAM_FOUND	gettext("parameter <%s> value=<%s> - deleted")
+#define	DBG_SML_DELETE_PARAM_NOT_FOUND	gettext("parameter <%s> not found - not deleted")
+#define	DBG_SML_DELETE_PARAM_NO_PARAMS	gettext("tag contains no parameters - not deleted")
+#define	DBG_SML_DEL_TAG			gettext("delete element <%s> from tag <%s>")
+#define	DBG_SML_FREE_TAG		gettext("freeing tag <0x%08lx> name=<%s>")
+#define	DBG_SML_GET_PARAM		gettext("get parameter <%s> tag <%s>")
+#define	DBG_SML_GET_PARAM_BY_TAG	gettext("get param by tag name <%s> index <%d> param <%s>")
+#define	DBG_SML_GET_PARAM_NAME		gettext("tag <%s> get parameter number <%d>")
+#define	DBG_SML_GET_TAG_BY_NAME		gettext("get tag by name <%s> index <%d>")
+#define	DBG_SML_GOT_PARAM		gettext("tag <%s> %s = <%s>")
+#define	DBG_SML_GOT_PARAM_NAME		gettext("tag <%s> got parameter number <%d> name=<%s>")
+#define	DBG_SML_HAVE_PARM_NAME		gettext("read tag: parameter name <%s> tag <%s>")
+#define	DBG_SML_HAVE_PARM_VALUE		gettext("read tag: parameter %s=\"%s\" tag <%s>")
+#define	DBG_SML_HAVE_TAG_NAME		gettext("read tag: open tag <%s>parent <%s>")
+#define	DBG_SML_INT_FREE_PARAMS		gettext("freeing parameters at <0x%08lx>")
+#define	DBG_SML_INT_FREE_PARAM_NAME	gettext("free param name <0x%08lx> name=<0x%08lx> value=<%s>")
+#define	DBG_SML_INT_FREE_PARAM_VALUE	gettext("free param value <0x%08lx> name=<0x%08lx> value=<%s>")
+#define	DBG_SML_INT_FREE_TAG		gettext("free tag <0x%08lx> name=<%s> param <%d> tags <%d>")
+#define	DBG_SML_INT_FREE_TAGS		gettext("freeing tags at <0x%08lx>")
+#define	DBG_SML_INT_FREE_TAG_NAME	gettext("freeing tag name <0x%08lx> name=<%s>")
+#define	DBG_SML_LOADED_TAGS_FROM_STR	gettext("tag <0x%08lx> <%s> loaded from string")
+#define	DBG_SML_ONE_TAG_READ		gettext("one tag read - tag <0x%08lx> name=<%s>")
+#define	DBG_SML_PRINTTAG		gettext("dump of tag <%s> size <%d> bytes\n\n****************************************************************************\n%s****************************************************************************\n")
+#define	DBG_SML_READTAG_BLANKLINE	gettext("read tag: blank line (no tag returned) at <%s>")
+#define	DBG_SML_READTAG_CLOSED_TAG	gettext("read tag: closed tag <%s> inside tag <%s>")
+#define	DBG_SML_READTAG_CLOSE_TAG	gettext("read tag: close tag <%s> found")
+#define	DBG_SML_READTAG_EXPECTED_EOF	gettext("read tag: provider <%s> EOF outside of tag input")
+#define	DBG_SML_READTAG_UNEXPECTED_EOF	gettext("read tag: provider <%s> unexpected EOF in middle of tag input")
+#define	DBG_SML_READ_IN_TOP_TAG		gettext(" --> read in top tag <%s>")
+#define	DBG_SML_READ_ONE_TAG		gettext("read one tag from <%s>")
+#define	DBG_SML_READ_ONE_TAG_NOTAG	gettext("cannot read tag - no tag present")
+#define	DBG_SML_READ_TAG		gettext("read tag: loading subtag for <%s>")
+#define	DBG_SML_SET_PARAM		gettext("set parameter tag <%s> %s = <%s>: ")
+#define	DBG_SML_SET_PARAM_CREATE_NEW	gettext("create new parameter")
+#define	DBG_SML_SET_PARAM_LEAVE_ALONE	gettext("existing value=<%s> identical - not changed")
+#define	DBG_SML_SET_PARAM_MODIFY	gettext("modify existing value=<%s>")
+#define	DBG_SML_START_CLOSE_TAG		gettext("read tag: close tag found current tag=<%s>")
+#define	DBG_SML_TAG_HEAD_DONE		gettext("read tag: tag <%s> started inside tag <%s>")
+#define	DBG_SML_TAG_ONLY		gettext("read tag: line with tag name only tag <%s>")
+#define	DBG_UNMOUNTING_DEV		gettext("unmounting package device <%s>")
+#define	DBG_UNPACKCHECK_ARGS		gettext("idsname <%s> packagedir <%s>")
+#define	DBG_UNPACKCHECK_ENTRY		gettext("unpack_and_check_packages:")
+#define	DBG_UNPACKSTRM_ARGS		gettext("unpack package <%s> from stream <%s> into directory <%s>")
+#define	DBG_UNPACKSTRM_ENTRY		gettext("unpack_package_from_stream:")
+#define	DBG_UNPACKSTRM_UNPACKING	gettext("unpacking package <%s> from stream <%s> into temporary directory <%s>")
+#define	DBG_VERIFY_SKIP_THISZONE	gettext("skipping dependency checking of package <%s>: marked this zone only")
+#define	DBG_WRITEFILE_ENTRY		gettext("write file: control <0x%02x> mode <0%04lo> file <%s>")
+#define	DBG_ZONES_SKIPPED		gettext("skipped <%d> zones that are not currently booted")
+#define	DBG_ZONE_EXEC_ENTER		gettext("zone_exec: enter zone <%s> command <%s> args:")
+#define	DBG_ZONE_EXEC_EXIT		gettext("zone_exec: exit zone <%s> command <%s> exit code <%d> stdout <%s>")
+
+/*
+ * I18N: these messages are error messages that can be displayed
+ * during the normal usage of the products
+ */
+
+#define	ERR_ACCRESP			gettext("unable to access response file <%s>")
+#define	ERR_ADMBD			gettext("%s is already installed at %s. Admin file will force a duplicate installation at %s.")
+#define	ERR_ADM_KEYSTORE		gettext("unable to determine keystore location")
+#define	ERR_ADM_PROXY			gettext("Admin file proxy setting invalid")
+#define	ERR_ALLZONES_AND_G_USED		gettext("The -G option (install packages in the global zone only)\nmay not be used with package <%s> because the package must be\ninstalled in all zones.")
+#define	ERR_ALLZONES_AND_IN_LZ		gettext("The package <%s> may only be installed by the global zone administrator")
+#define	ERR_ALLZONES_AND_IN_LZ_PKGRM	gettext("The package <%s> may only be removed by the global zone administrator")
+#define	ERR_ALLZONES_AND_THISZONE	gettext("The package <%s> has <%s> = true and <%s> = true: the package may set either parameter to true, but may not set both parameters to true. NOTE: if the package contains a request script, it is treated as though it has <SUNW_PKG_THISZONE> = true")
+#define	ERR_ALLZONES_AND_Z_USED		gettext("The -Z option (extend packages installed in the global zone to all non-global zones) may not be used with the package <%s> which may only be installed in all zones by the global zone administrator")
+#define	ERR_ARG				gettext("URL <%s> is not valid")
+#define	ERR_BADULIMIT  			gettext("cannot process invalid ULIMIT value of <%s>.")
+#define	ERR_BADUSER			gettext("unable to find user <%s> or <%s>.")
+#define	ERR_BAD_DEVICE			gettext("bad device <%s> specified")
+#define	ERR_BAD_N_PKGRM			gettext("you must specify a category (-Y) or list of packages to remove")
+#define	ERR_BRAND_GETBRAND		gettext("unable to get zone brand: zonecfg_get_brand: %s")
+#define	ERR_CANNOT_BOOT_ZONE		gettext("no changes made to zone <%s>: unable to boot zone")
+#define	ERR_CANNOT_CKSUM_FILE		gettext("unable to determine checksum of file <%s>: %s")
+#define	ERR_CANNOT_CONVERT_PKGSTRM	gettext("unable to convert package <%s> to stream from <%s> to <%s>")
+#define	ERR_CANNOT_COPY			gettext("unable to copy <%s>\n\tto <%s>")
+#define	ERR_CANNOT_COPY_LOCAL		gettext("cannot obtain local copy of <%s>: (%d) %s")
+#define	ERR_CANNOT_CREATE_PKGPATH	gettext("unable to create package path <%s>")
+#define	ERR_CANNOT_GET_ZONE_LIST	gettext("unable to determine list of non-global zones installed on this system")
+#define	ERR_CANNOT_LOCK_THIS_ZONE	gettext("Unable to lock this zone for administration")
+#define	ERR_CANNOT_LOCK_ZONES		gettext("unable to lock zones to perform operations")
+#define	ERR_CANNOT_OPEN_DEPEND_FILE	gettext("unable to open depend file <%s>: %s")
+#define	ERR_CANNOT_OPEN_FOR_WRITING	gettext("unable to open <%s> for writing: %s")
+#define	ERR_CANNOT_OPEN_PKG_STREAM	gettext("unable to open package datastream <%s>")
+#define	ERR_CANNOT_UNPACK_PKGSTRM	gettext("unable to unpack package <%s> from stream <%s> into directory <%s>")
+#define	ERR_CANNOT_USE_DIR		gettext("cannot use directory <%s>: %s")
+#define	ERR_CASFAIL			gettext("class action script did not complete successfully")
+#define	ERR_CAT_FND			gettext("Category argument <%s> cannot be found.")
+#define	ERR_CAT_INV			gettext("Category argument <%s> is invalid.")
+#define	ERR_CAT_LNGTH			gettext("The category argument exceeds the SVR4 ABI defined maximum supported length of 16 characters.")
+#define	ERR_CAT_SYS			gettext("Unable to remove packages that are part of the SYSTEM category with the -Y option.")
+#define	ERR_CFBAD			gettext("bad entry read of contents file")
+#define	ERR_CFMISSING			gettext("missing entry in contents file for <%s>")
+#define	ERR_CHDIR			gettext("unable to change current working directory to <%s>")
+#define	ERR_CHGDIR			gettext("unable to change directory to <%s>")
+#define	ERR_CHKINSTALL 			gettext("checkinstall script did not complete successfully")
+#define	ERR_CHKINSTALL_NOSCRIPT		gettext("unable to access checkinstall script <%s>")
+#define	ERR_CHMOD			gettext("unable to change the mode of the response file <%s>")
+#define	ERR_CHMOD_CHK 			gettext("unable to change the mode of the checkinstall script")
+#define	ERR_CLASSES			gettext("CLASSES parameter undefined in <%s>")
+#define	ERR_CLIDX			gettext("invalid class index of <%d> detected for file %s.")
+#define	ERR_COPY_MEMORY			gettext("unable to allocate memory to copy file <%s>: (%d) %s")
+#define	ERR_CORRUPT			gettext("source path <%s> is corrupt")
+#define	ERR_COULD_NOT_INSTALL		gettext("%s could not be installed.")
+#define	ERR_CREATE_PATH_2		gettext("unable to create path from <%s> and <%s>")
+#define	ERR_CREATE_PATH_3		gettext("unable to create path from <%s> and <%s> and <%s>")
+#define	ERR_CREATE_PKGOBJ		gettext("unable to create package object <%s>.")
+#define	ERR_CREATE_TMPADMIN		gettext("unable to create temporary admin file <%s>: %s")
+#define	ERR_CREAT_CONT 			gettext("unable to create contents file <%s>")
+#define	ERR_CRERESP			gettext("unable to create response file <%s>")
+#define	ERR_DB				gettext("unable to query or modify database")
+#define	ERR_DB_QUERY			gettext("unable to find <%s> in the database.")
+#define	ERR_DB_TBL			gettext("unable to remove database entries for package <%s> in table <%s>.")
+#define	ERR_DEPENDENCY_IGNORED		gettext("\nERROR: %s <%s> on %s <%s>\n")
+#define	ERR_DEPENDENCY_REPORT		gettext("\nERROR: <%s> %s <%s> on %s <%s>\n")
+#define	ERR_DEPNAM			gettext("The <%s> package \"%s\" depends on the package currently being removed.")
+#define	ERR_DEPONME			gettext("The <%s> package depends on the package currently being removed.")
+#define	ERR_DIR_CONST			gettext("unable to construct download directory <%s>")
+#define	ERR_DSARCH			gettext("unable to find archive for <%s> in datastream")
+#define	ERR_DSINIT			gettext("could not process datastream from <%s>")
+#define	ERR_DSTREAM			gettext("unable to unpack datastream")
+#define	ERR_DSTREAMCNT 			gettext("datastream early termination problem")
+#define	ERR_DWNLDTEMPDIR		gettext("unable to make temporary directory for download operations in directory <%s>: %s")
+#define	ERR_FCHMOD			gettext("unable to change mode of file <%s> to <0x%04lx>: (%d) %s")
+#define	ERR_FINALCK_ATTR		gettext("ERROR: attribute verification of <%s> failed")
+#define	ERR_FINALCK_CONT		gettext("ERROR: content verification of <%s> failed")
+#define	ERR_FSYS_FELLOUT		gettext("fsys(): fell out of loop looking for <%s>")
+#define	ERR_F_REQUIRES_M		gettext("the -f option must be used in conjunction with the -m option")
+#define	ERR_FSTAT			gettext("unable to fstat fd <%d> pathname <%s>: (%d) %s")
+#define	ERR_GPKGLIST_ERROR		gettext("unable to determine list of packages to operate on (internal error in gpkglist)")
+#define	ERR_GZ_USED_TOGETHER		gettext("the -G and zonelist options cannot be used together")
+#define	ERR_ILL_HTTP_OPTS		gettext("The -i and (-k or -P) options are mutually exclusive.")
+#define	ERR_ILL_PASSWD			gettext("A password is required to retrieve the public certificate from the keystore.")
+#define	ERR_INCOMP_VERS			gettext("A version of <%s> package \"%s\" (which is incompatible with the package that is being installed) is currently installed and must be removed.")
+#define	ERR_INPUT			gettext("error while reading file <%s>: (%d) %s")
+#define	ERR_INSTALL_ZONES_SKIPPED	gettext("unable to boot <%d> zones that are not currently running - no packages installed on those zones")
+#define	ERR_INTONLY			gettext("unable to install <%s> without user interaction")
+#define	ERR_INTR			gettext("Interactive request script supplied by package")
+#define ERR_INVALID_O_OPTION            gettext("option <%s> passed to -O is not recognized: option ignored")
+#define	ERR_IN_GZ_AND_ALLZONES_AND_INSTALLED	gettext("The package <%s> is currently installed on\nthe system in the global zone only.  When this package was last installed\nthe -G option was used (install package in the global zone only).  The new\ninstance of this package to be installed may only be installed in all zones.\nBefore you can install the latest version of this package, you must first\nremove all instances of this package from the global zone (via pkgrm).")
+#define	ERR_IN_GZ_AND_NOT_INSTALLED		gettext("WARNING: The package <%s> is marked as being installed in the\nglobal zone only. The package is NOT installed on the system. This condition\nis not possible. The file <%s> must be edited\nand the line for this package removed.")
+
+#ifdef Z_OPTION_IS_SUPPORTED
+#define	ERR_IN_GZ_AND_NO_G_USED		gettext("The package <%s> is currently installed on the\nsystem in the global zone. To install the new instance of this package in the\nglobal zone only, you must specify the -G option. To install the new instance\nof this package in all zones you must first run pkgadd on the current package\nwith the -Z option so that the existing instance of this package is installed\nin all zones first. You may optionally remove the existing instance of this\npackage from the global zone first (via pkgrm) and then install the new\ninstance of this package in all zones.")
+#else
+#define	ERR_IN_GZ_AND_NO_G_USED		gettext("The package <%s> is currently installed on the system in the\nglobal zone. To install the new instance of this package in the global\nzone only, you must specify the -G option. To install the new instance\nof this package in all zones you must first remove the existing instance\nof this package from the global zone first (via pkgrm) and then install\nthe new instance of this package in all zones.")
+#endif
+
+#define	ERR_LINK			gettext("unable to link <%s> to <%s>: %s")
+#define	ERR_LIVE_CONTINUE_NOT_SUPPORTED	gettext("live continue mode is not supported")
+#define	ERR_LOCKFILE			gettext("unable to create lockfile <%s>")
+#define	ERR_LOG				gettext("unable to open logfile <%s>: (%d) %s")
+#define	ERR_LOG_FAIL			gettext("failed to log message using format <%s>")
+#define	ERR_MALLOC			gettext("unable to allocate %s memory, errno %d: %s")
+#define	ERR_MAPFAILED			gettext("unable to mmap <%s> for reading: (%d) %s")
+#define	ERR_MEM				gettext("unable to allocate memory.")
+#define	ERR_MEMORY	 		gettext("memory allocation failure, errno=%d")
+#define	ERR_MERGINFOS_CHANGE_ZONEATTR	gettext("attempt to change package <%s> version <%s> package zone attribute <%s> from <%s> to <%s>: the package zone attribute values of installed packages cannot be changed")
+#define	ERR_MERGINFOS_UNSET_ZONEATTR	gettext("attempt to unset package <%s> version <%s> package zone attribute <%s> from <%s>: the package zone attribute values of installed packages that are set to <true> cannot be unset")
+#define	ERR_MERGINFOS_SET_ZONEATTR	gettext("attempt to set package <%s> version <%s> package zone attribute <%s> that is not currently set to <%s>: the package zone attribute values of installed packages cannot be set to any value except <false>")
+#define	ERR_MISSING_DIR_AND_PKG		gettext("missing directory containing package to install and package instance (name of package ) to install from end of command line")
+#define	ERR_MISSING_PKG_INSTANCE	gettext("missing package instance (name of package) to install from end of command line")
+#define	ERR_MKDIR			gettext("unable to make temporary directory <%s>")
+#define	ERR_MAKE_DIR			gettext("unable to create directory <%s>: (%d) %s")
+#define	ERR_MKTEMP			gettext("unable to create unique temporary file <%s>: (%d) %s")
+#define	ERR_MNT_NOMOUNTS		gettext("get_mntinfo() could find no filesystems")
+#define	ERR_MNT_NOROOT			gettext("get_mntinfo() identified <%s> as root file system instead of <%s> errno %d: %s")
+#define	ERR_MODTIM			gettext("unable to reset access/modification time of <%s>: (%d) %s")
+#define	ERR_NEWBD			gettext("%s is already installed at %s. Duplicate installation attempted at %s.")
+#define	ERR_NOCOPY			gettext("unable to create copy of UNINSTALL script in <%s>")
+#define	ERR_NODIR			gettext("unable to create directory <%s>: (%d) %s")
+#define	ERR_NORESPCOPY			gettext("unable to copy response file <%s> to <%s>")
+#define	ERR_NODEVICE			gettext("unable to determine device to install from")
+#define	ERR_NOINT			gettext("-n option cannot be used when removing pre-SVR4 packages")
+#define	ERR_NOPKGS			gettext("no packages were found in <%s>")
+#define	ERR_NOREQUEST  			gettext("package does not contain an interactive request script")
+#define	ERR_NORESP			gettext("response file <%s> must not exist")
+#define	ERR_NOSUCH_INHERITED		gettext("cannot use inherited file system <%s>")
+#define	ERR_NOTABLE			gettext("unable to open %s table <%s>: %s")
+#define	ERR_NOT_IN_GZ_AND_Z_USED	gettext("The package <%s> is not currently installed\nin the global zone only; you may not specify the -Z option to install the\npackage in all non-global zones until the package is first install in the\nglobal zone only. You may optionally install the package without the -Z option\nto install the package in all zones.")
+#define	ERR_NOT_ROOT			gettext("You must be \"root\" for %s to execute properly.")
+#define	ERR_NOW_ALLZONES_AND_HOLLOW	gettext("The package <%s> has <%s> = false and <%s> = true: a hollow package must also be set to install in all zones")
+#define	ERR_NO_LIVE_MODE		gettext("live continue mode is not supported")
+#define	ERR_NO_PKGDIR			gettext("unable to use package directory <%s> for package <%s>: %s")
+#define	ERR_NO_PKG_INFOFILE		gettext("unable to open package <%s> pkginfo file <%s>: %s")
+#define	ERR_NO_PKG_MAPFILE		gettext("unable to open package <%s> pkgmap file <%s>: %s")
+#define	ERR_NO_SUCH_INSTANCE		gettext("instance <%s> does not exist")
+#define	ERR_OPEN_ADMIN_FILE		gettext("unable to open admin file <%s>: %s")
+#define	ERR_OPEN_READ			gettext("unable to open <%s> for reading: (%d) %s")
+#define	ERR_OPEN_WRITE			gettext("unable to open <%s> for writing: (%d) %s")
+#define	ERR_OPRESVR4			gettext("unable to unlink options file <%s>")
+#define	ERR_OUTPUT_WRITING		gettext("error while writing file <%s>: (%d) %s")
+#define	ERR_PACKAGEBINREN		gettext("unable to rename <%s>\n\tto <%s>")
+#define	ERR_PATCHPKG			gettext("unable to update patch_table with patches that have been pre installed")
+#define	ERR_PATH			gettext("the path <%s> is invalid!")
+#define	ERR_PKGABRV			gettext("illegal package abbreviation <%s> in dependency file")
+#define	ERR_PKGADDCHK_CNFFAILED		gettext("Conflicting file dependency checking failed.")
+#define	ERR_PKGADDCHK_DEPFAILED		gettext("Dependency checking failed.")
+#define	ERR_PKGADDCHK_MKPKGDIR		gettext("Unable to make required packaging directory")
+#define	ERR_PKGADDCHK_PRIVFAILED	gettext("Privilege checking failed.")
+#define	ERR_PKGADDCHK_SPCFAILED		gettext("Space checking failed.")
+#define	ERR_PKGASK_AND_IGNORE_SIG	gettext("cannot use the -i option with pkgask")
+#define	ERR_PKGASK_AND_KEYSTORE_FILE	gettext("cannot use the -k option with pkgask")
+#define	ERR_PKGASK_AND_NOINTERACT	gettext("cannot use the -n option with pkgask")
+#define	ERR_PKGASK_AND_PROXY		gettext("cannot use the -x option with pkgask")
+#define	ERR_PKGASK_AND_SPOOLDIR		gettext("cannot use the -s option with pkgask")
+#define	ERR_PKGASK_AND_URI		gettext("cannot use web based sources via -d with pkgask")
+#define	ERR_PKGBINCP			gettext("unable to copy <%s>\n\tto <%s>")
+#define	ERR_PKGBINREN  			gettext("unable to rename <%s>\n\tto <%s>")
+#define	ERR_PKGINFO			gettext("unable to open pkginfo file <%s>")
+#define	ERR_PKGINFO_ATTR_ADDED		gettext("package <%s> is attempting to add the package attribute <%s>: this attribute cannot be added once the package is installed")
+#define	ERR_PKGINFO_ATTR_CHANGED	gettext("package <%s> is attempting to change the package attribute <%s> from <%s> to <%s>: this attribute cannot be changed once the package is installed")
+#define	ERR_PKGINSTALL_GZONLY_ADD	gettext("unable to add package <%s> to global zone only package list file")
+#define	ERR_PKGINSTALL_STATOF		gettext("unable to get space usage of <%s>: %s")
+#define	ERR_PKGINSTALL_STATVFS		gettext("unable to determine file system space for <%s>: %s")
+#define	ERR_PKGMAP			gettext("unable to open pkgmap file <%s>")
+#define	ERR_PKGOPS_CANNOT_OPEN_GZONLY	gettext("unable to open global zone only package list file at <%s>")
+#define	ERR_PKGOPS_LOCHIGH_BAD_PKGNAME	gettext("package name is not valid: %s")
+#define	ERR_PKGOPS_OPEN_GZONLY		gettext("unable to open global zone only package list file <%s>: %s")
+#define	ERR_PKGOPS_TMPOPEN		gettext("unable to create temporary global zone only package list file <%s>: %s")
+#define	ERR_PKGREMOVE_GZONLY_REMOVE	gettext("unable to remove package <%s> from global zone only package list file")
+#define	ERR_PKGRMCHK_DEPFAILED		gettext("Dependency checking failed.")
+#define	ERR_PKGRMCHK_PRIVFAILED		gettext("Privilege checking failed.")
+#define	ERR_PKGS_AND_CAT_PKGADD		gettext("cannot specify both a list of packages and a category (-Y) to install")
+#define	ERR_PKGS_AND_CAT_PKGRM		gettext("cannot specify both a list of packages and a category (-Y) to remove")
+#define	ERR_PKGUNMOUNT			gettext("unable to unmount <%s>")
+#define	ERR_PKGVOL			gettext("unable to obtain package volume")
+#define	ERR_PKGZONEINSTALL_NO_STREAM	gettext("internal error - package to install in zone not in stream format")
+#define	ERR_PKG_NOT_APPLICABLE		gettext("package <%s> cannot be installed on this system/zone")
+#define	ERR_PKG_NOT_INSTALLABLE		gettext("unable to install package <%s>")
+#define	ERR_PKG_NOT_REMOVABLE		gettext("unable to remove package <%s>")
+#define	ERR_POSTINSTALL			gettext("postinstall script did not complete successfully")
+#define	ERR_POSTREMOVE			gettext("postremove script did not complete successfully")
+#define	ERR_PREINSTALL 			gettext("preinstall script did not complete successfully")
+#define	ERR_PREIVFY_NOFILE		gettext("unable to perform preinstallation check of package <%s> in zone <%s>")
+#define	ERR_PREIVFY_OPEN_FILE		gettext("unable to examine preinstallation check file <%s> for package <%s> in zone <%s>: %s")
+#define	ERR_PREIVFY_UNKNOWN_LINE	gettext("unknown preinstallation dependency check line <%s> for package <%s> zone <%s>: ignored")
+#define	ERR_PRENCI			gettext("The <%s> package \"%s\" is a prerequisite package and is not completely installed.")
+#define	ERR_PREREMOVE			gettext("preremove script did not complete successfully")
+#define	ERR_PREREQ			gettext("The <%s> package \"%s\" is a prerequisite package and should be installed.")
+#define	ERR_PRERVFY_NOFILE		gettext("unable to perform preremoval check of package <%s> in zone <%s>")
+#define	ERR_PRERVFY_OPEN_FILE		gettext("unable to examine preremoval check file <%s> for package <%s> in zone <%s>: %s")
+#define	ERR_PRERVFY_UNKNOWN_LINE	gettext("unknown preremoval dependency check line <%s> for package <%s> zone <%s>: ignored")
+#define	ERR_PROXY			gettext("Proxy specification <%s> invalid")
+#define	ERR_RDONLY			gettext("read-only parameter <%s> cannot be assigned a value")
+#define	ERR_READ			gettext("unable to read <%s>: (%d) %s")
+#define	ERR_REMOVE			gettext("unable to remove file <%s>: %s")
+#define	ERR_RENAME			gettext("unable to rename <%s> to <%s>: %s")
+#define	ERR_REQUEST			gettext("request script did not complete successfully")
+#define	ERR_RESOLVEPATH			gettext("unable to resolve path <%s>: %s")
+#define	ERR_RESPFILE			gettext("response file is invalid for pre-SVR4 package")
+#define	ERR_RESPFILE_AND_URI		gettext("cannot use -r option with web based sources via -d")
+#define	ERR_RESPONSE			gettext("unable to open response file <%s>")
+#define	ERR_RMDIR			gettext("unable to remove existing directory at <%s>")
+#define	ERR_RMPATH			gettext("unable to remove <%s>")
+#define	ERR_RMRESP			gettext("unable to remove response file <%s>")
+#define	ERR_ROOT_CMD			gettext("Command line install root contends with environment.")
+#define	ERR_ROOT_SET   			gettext("Could not set install root from the environment.")
+#define	ERR_RSP_FILE_NOTFULLPATH	gettext("response file <%s> must be full pathname")
+#define	ERR_RSP_FILE_NOT_GIVEN		gettext("response file (to write) is required")
+#define	ERR_RUNSTATE			gettext("unable to determine current run-state")
+#define	ERR_SCRULIMIT			gettext("script <%s> created a file exceeding ULIMIT.")
+#define	ERR_SML_CANNOT_READ_TAG						gettext("cannot read tag")
+#define	ERR_SML_EOF_BEFORE_TAG_NAME					gettext("reading tag: unexpected EOF before reading tag name expecting tag=<%s>")
+#define	ERR_SML_PARM_SEP_BAD						gettext("reading tag: parameter value start found <%c> (0x%02x) expected '\"'")
+#define	ERR_SML_READTAG_BADPARMNAME_CLOSE				gettext("reading tag: expected '>' after '/' to close parm <%s> tag <%s> inside tag <%s>")
+#define	ERR_SML_READTAG_BADTAG_CLOSE					gettext("reading tag: expected '>' after '/' to close tag <%s> inside tag <%s>")
+#define	ERR_SML_READTAG_BAD_START_CHAR					gettext("reading tag: invalid character <%c> (0x%02x) before start of tag")
+#define	ERR_SML_READTAG_CLOSE_EMPTY_TAG					gettext("reading tag: no element name provided before close of tag")
+#define	ERR_SML_READTAG_CLOSE_NO_PARENT					gettext("reading tag: close tag <%s> not within any tag to close")
+#define	ERR_SML_READTAG_CLOSE_TAG_EOF					gettext("reading tag: unexpected EOF reading close tag name expecting tag=<%s>")
+#define	ERR_SML_READTAG_CLOSE_TAG_ILLCHAR				gettext("reading tag: invalid character <%c> (0x%02x) in close tag name <%s>")
+#define	ERR_SML_READTAG_CLOSE_WRONG_TAG					gettext("reading tag: close tag <%s> does not match current tag <%s>")
+#define	ERR_SML_READTAG_EMPTY_PARMNAME					gettext("reading tag: no parameter name provided tag <%s> inside tag <%s>")
+#define	ERR_SML_READTAG_EMPTY_TAG					gettext("reading tag: no element name provided before close of tag inside tag <%s>")
+#define	ERR_SML_READTAG_PARMNAME_ILLCHAR				gettext("reading tag: invalid character <%c> (0x%02x) in parameter name <%s> tag <%s> inside tag <%s>")
+#define	ERR_SML_READTAG_PARMVAL_EOF					gettext("reading tag: unexpected EOF reading parameter value name <%s> tag <%s> inside tag <%s>")
+#define	ERR_SML_READTAG_PARMVAL_NL					gettext("reading tag: unexpected newline reading parameter value name <%s> tag <%s> inside tag <%s>")
+#define	ERR_SML_READTAG_PARM_EOF					gettext("reading tag: unexpected EOF reading parameter name tag <%s> inside tag <%s>")
+#define	ERR_SML_READTAG_TAG_EOF						gettext("reading tag: unexpected EOF reading tag name <%s> inside tag <%s>")
+#define	ERR_SML_READTAG_TAG_ILLCHAR					gettext("reading tag: invalid character <%c> (0x%02x) in tag name <%s>")
+#define	ERR_SNPRINTF			gettext("Not enough memory to format, %s")
+#define	ERR_SPOOLDIR_AND_ADMNFILE	gettext("cannot use the -s option with the -a option")
+#define	ERR_SPOOLDIR_AND_INST_ROOT	gettext("cannot use the -s option with the -R option")
+#define	ERR_SPOOLDIR_AND_NOINTERACT	gettext("cannot use the -s option with the -n option")
+#define	ERR_SPOOLDIR_AND_PKGRMREMOTE	gettext("cannot use the -s option with the -A option")
+#define	ERR_SPOOLDIR_AND_PKGVERBOSE	gettext("cannot use the -s option with the -v option")
+#define	ERR_SPOOLDIR_AND_RESPFILE	gettext("cannot use the -s option with the -r option")
+#define	ERR_SPOOLDIR_CANNOT_BE_SYS	gettext("the -s option cannot specify %s")
+#define	ERR_SPOOLDIR_USED_WITH_G	gettext("the -G option cannot be used with the -s option")
+#define	ERR_SPOOLDIR_USED_WITH_Z	gettext("the zonelist option cannot be used with the -s option")
+#define	ERR_STAT			gettext("unable to stat <%s>: %s")
+#define	ERR_STREAMDIR			gettext("unable to make temporary directory to unpack datastream: %s")
+#define	ERR_STREAM_UNAVAILABLE		gettext("unable to open stream <%s> for package <%s>: %s")
+#define	ERR_SYSINFO			gettext("unable to process installed package information, errno=%d")
+#define	ERR_THISZONE_AND_Z_USED		gettext("The package <%s> has <%s> = true: the -Z option may not be used to extend this type of package to all non-global zones")
+#define	ERR_TMPFILE			gettext("unable to establish temporary file")
+#define	ERR_TMPFILE_CHK			gettext("unable to create temporary checkinstall script")
+#define	ERR_TMPRESP			gettext("unable to create temporary response file")
+#define	ERR_TOO_MANY_CMD_ARGS		gettext("too many arguments to command")
+#define	ERR_TOO_MANY_PKGS		gettext("too many packages referenced specified at the end of the command line: only one package may be specified")
+#define	ERR_UNKNOWN_DEPENDENCY		gettext("unknown dependency type specified: %c\n")
+#define	ERR_UNKNOWN_DEV			gettext("unknown device <%s>")
+#define	ERR_UNPACK_DSREAD		gettext("unable to read part <%d> of stream <%s> to directory <%s> for package <%s>")
+#define	ERR_UNPACK_FMKDIR		gettext("unable to create temporary package area <%s>: %s")
+#define	ERR_UNSUCC			gettext("(A previous attempt may have been unsuccessful.)")
+
+#ifdef Z_OPTION_IS_SUPPORTED
+#define	ERR_USAGE_PKGADD_GLOBALZONE	gettext("usage:\n\t%s [-nvi] [-d device] [[-M] -R host_path] [-V fs_file] [-a admin_file] [-r response] [-x proxy] [-k keystore] [-G|-Z] [-P passwd] [-Y category[,category ...] | pkg [pkg ...]]\n\t%s -s dir [-d device] [-x proxy] [-k keystore] [-G|-Z] [-P passwd] [-Y category[,category ...] | pkg [pkg ...]]\n")
+#else
+#define	ERR_USAGE_PKGADD_GLOBALZONE	gettext("usage:\n\t%s [-nvi] [-d device] [[-M] -R host_path] [-V fs_file] [-a admin_file] [-r response] [-x proxy] [-k keystore] [-G] [-P passwd] [-Y category[,category ...] | pkg [pkg ...]]\n\t%s -s dir [-d device] [-x proxy] [-k keystore] [-G] [-P passwd] [-Y category[,category ...] | pkg [pkg ...]]\n")
+#endif
+
+#define	ERR_USAGE_PKGADD_NONGLOBALZONE	gettext("usage:\n\t%s [-nvi] [-d device] [[-M] -R host_path] [-V fs_file] [-a admin_file] [-r response] [-x proxy] [-k keystore] [-P passwd] [-Y category[,category ...] | pkg [pkg ...]]\n\t%s -s dir [-d device] [-x proxy] [-k keystore] [-P passwd] [-Y category[,category ...] | pkg [pkg ...]]\n")
+#define	ERR_USAGE_PKGASK		gettext("usage: %s -r response [-d device]  [-R host_path] [-Y category[,category ...]] | [pkg [pkg ...]]\n")
+#define	ERR_USAGE_PKGINSTALL  		gettext("usage:\n\tpkginstall [-o] [-n] [-d device] [-m mountpt [-f fstype]] [-v] [[-M] -R host_path] [-V fs_file] [-b bindir] [-a admin_file] [-r resp_file] [-N calling_prog] directory pkginst\n")
+#define	ERR_USAGE_PKGREMOVE		gettext("usage:\n\tpkgremove [-a admin_file] [-n] [-V ...] [[-M|-A] -R host_path] [-v] [-o] [-N calling_prog] pkginst\n")
+#define	ERR_USAGE_PKGRM  		gettext("usage:\n\t%s [-a admin] [-n] [[-M|-A] -R host_path] [-V fs_file] [-v] [-Y category[,category ...] | pkg [pkg ...]]\n\t%s -s spool [-Y category[,category ...] | pkg [pkg ...]]\n")
+#define	ERR_VALINST			gettext(" Allowable instances include (in order of preference:)\n")
+#define	ERR_V_USED_AND_PKGRMREMOTE	gettext("cannot use the -V option with the -A option")
+#define	ERR_V_USED_WITH_GZS		gettext("cannot use the -V option when non-global zones exist")
+#define	ERR_WARNING			gettext("WARNING:")
+#define	ERR_WRITE			gettext("unable to write <%s>: (%d) %s")
+#define	ERR_WTMPFILE			gettext("unable to write temporary file <%s>")
+#define	ERR_ZONETEMPDIR			gettext("unable to make temporary directory for non-global zone operations in directory <%s>: %s")
+#define	ERR_Z_USED_IN_NONGLOBAL_ZONE	gettext("the zonelist option may not be used in a non-global zone")
+#define	ERR_PKGINFO_COPY 		gettext("unable to copy %s to %s")
+#define	ERR_CANNOT_ENABLE_LOCAL_FS	gettext("Failed to enable the filesystem/local service.\n")
+#define	ERR_CANNOT_RESTORE_LOCAL_FS	gettext("Failed to bring the filesystem/local service back to its original state.\n")
+
+/*
+ * I18N: these messages are help messages that are displayed when the
+ * user answers a question with "?" - asking for help to be displayed
+ */
+
+#define	HLP_PKGADDCHK_CONFLICT		gettext("If you choose to install conflicting files, the files listed above will be overwritten and/or have their access permissions changed.  If you choose not to install these files, installation will proceed but these specific files will not be installed.  Note that sane operation of the software being installed may require these files be installed; thus choosing to not to do so may cause inappropriate operation.  If you wish to stop installation of this package, enter 'q' to quit.")
+#define	HLP_PKGADDCHK_CONT		gettext("If you choose 'y', installation of this package will continue.  If you want to stop installation of this package, choose 'n'.")
+#define	HLP_PKGADDCHK_DEPEND		gettext("The package being installed has indicated a dependency on the existence (or non-existence) of another software package.  If this dependency is not met before continuing, the package may not install or operate properly.  If you wish to disregard this dependency, answer 'y' to continue the installation process.")
+#define	HLP_PKGADDCHK_PARTIAL		gettext("Installation of partially installed packages is normally allowable, but some packages providers may suggest that a partially installed package be completely removed before re-attempting installation.  Check the documentation provided with this package, and then answer 'y' if you feel it is advisable to continue the installation process.")
+#define	HLP_PKGADDCHK_PRIV		gettext("During the installation of this package, certain scripts provided with the package will execute with super-user permission.  These scripts may modify or otherwise change your system without your knowledge.  If you are certain of the origin and trustworthiness of the package being installed, answer 'y' to continue the installation process.")
+#define	HLP_PKGADDCHK_SETUID		gettext("The package being installed appears to contain processes which will have their effective user or group ids set upon execution.  History has shown that these types of processes can be a source of security problems on your system.  If you choose not to install these as setuid files, installation will proceed but these specific files will be installed as regular files with setuid and/or setgid permissions reset.  Note that sane operation of the software being installed may require that these files be installed with setuid or setgid permissions as delivered; thus choosing to install them as regular files may cause inappropriate operation.  If you wish to stop installation of this package, enter 'q' to quit.")
+#define	HLP_PKGADDCHK_SPACE		gettext("It appears that there is not enough free space on your system in which to install this package.  It is possible that one or more filesystems are not properly mounted.  Neither installation of the package nor its operation can be guaranteed under these conditions.  If you choose to disregard this warning, enter 'y' to continue the installation process.")
+#define	HLP_PKGREMOVE_DEPEND	gettext("Other packages currently installed on the system have indicated a dependency on the package being removed.  If removal of this package occurs, it may render other packages inoperative.  If you wish to disregard this dependency, answer 'y' to continue the package removal process.")
+#define	HLP_PKGREMOVE_PRIV	gettext("During the removal of this package, certain scripts provided with the package will execute with super-user permission.  These scripts may modify or otherwise change your system without your knowledge.  If you are certain of the origin of the package being removed and trust its worthiness, answer 'y' to continue the package removal process.")
+#define	HLP_PKGREMOVE_RUNLEVEL	gettext("If this package is not removed in a run-level which has been suggested, it is possible that the package may not remove properly.  If you wish to follow the run-level suggestions, answer 'n' to stop the package removal process.")
+#define	HLP_PKGREMOVE_WSDEPEND	gettext("Installed products in the Solaris Product Registry have registered a\ndependency on the package being removed.  If removal of this package\noccurs, it may render these products inoperative.  If you wish to disregard this dependency, answer 'y' to continue the package removal process.\n")
+#define	HLP_PKGRMCHK_DEPEND		gettext("The package being removed has indicated a dependency on the existence (or non-existence) of another software package.  If this dependency is not met before continuing, the package may not remove or operate properly.  If you wish to disregard this dependency, answer 'y' to continue the removal process.")
+#define	HLP_PKGRMCHK_PRIV		gettext("During the removal of this package, certain scripts provided with the package will execute with super-user permission.  These scripts may modify or otherwise change your system without your knowledge.  If you are certain of the origin and trustworthiness of the package being removed, answer 'y' to continue the removal process.")
+
+#define	INFO_INSTALL			gettext("\nThe following package is currently installed:")
+#define	INFO_RMSPOOL			gettext("\nRemoving spooled package instance <%s>")
+#define	INFO_SPOOLED			gettext("\nThe following package is currently spooled:")
+
+#define	LOG_GETVOL_RET			gettext("getvol() returned <%d>")
+
+#define	MSG_1MORETODO			gettext("\nThere is 1 more package to be removed.")
+#define	MSG_1MORE_INST			gettext("\nThere is 1 more package to be installed.")
+#define	MSG_1MORE_PROC			gettext("\nThere is 1 more package to be processed.")
+#define	MSG_1_PKG_NOT_PROCESSED		gettext("\n1 package was not processed!\n")
+#define	MSG_ATTRIB			gettext("%s <attribute change only>")
+#define	MSG_BASE_USED   		gettext("Using <%s> as the package base directory.")
+#define	MSG_BOOTING_ZONE		gettext("## Booting non-running zone <%s> into administrative state")
+#define	MSG_BYPASSING_ZONE		gettext("## pkgask - bypassing zone <%s>")
+#define	MSG_CHECKINSTALL_INTERRUPT_B4_Z	gettext("## interrupted: package <%s> not installed")
+#define	MSG_CHECKINSTALL_PKG_IN_ZONE	gettext("## Verifying package <%s> dependencies in zone <%s>")
+#define	MSG_CHECKREMOVE_PKG_IN_GZ	gettext("## Verifying package <%s> dependencies in global zone")
+#define	MSG_CHECKREMOVE_PKG_IN_ZONE	gettext("## Verifying package <%s> dependencies in zone <%s>")
+#define	MSG_DBUPD_N_N			gettext("## Database update of part %d of %d is complete.")
+#define	MSG_DBUPD_N_N_LZ		gettext("## Database update of part %d of %d in zone <%s> is complete.")
+#define	MSG_DIRBUSY			gettext("%s <mount point not removed>")
+#define	MSG_DOREMOVE_INTERRUPTED	gettext("## interrupted: package <%s> not installed")
+#define	MSG_DOREMOVE_INTERRUPTED_B4_Z	gettext("## interrupted: package <%s> not removed")
+#define	MSG_DRYRUN_DONE			gettext("Dryrun complete.")
+#define	MSG_EXE_INSTALL_SCRIPT		gettext("## Executing INSTALL script provided by package")
+#define	MSG_FAIL			gettext("\n## Pre-SVR4 package reports failed installation.")
+#define	MSG_HRDLINK			gettext("%s <linked pathname>")
+#define	MSG_IMPDIR			gettext("%s <implied directory>")
+#define	MSG_INSERT_VOL			gettext("Insert %v into %p.")
+#define	MSG_INSTALLING_PKG_IN_GZ	gettext("## Installing package <%s> in global zone")
+#define	MSG_INSTALLING_PSVR4		gettext("*** Installing Pre-SVR4 Package ***")
+#define	MSG_INSTALL_INTERRUPT_B4_ZONES	gettext("## Interrupted: package <%s> not installed in any non-global zones")
+#define	MSG_INSTALL_PKG_IN_ZONE		gettext("## Installing package <%s> in zone <%s>")
+#define	MSG_INST_MANY  			gettext("   %d package pathnames are already properly installed.")
+#define	MSG_INST_N_N			gettext("## Installation of part %d of %d is complete.")
+#define	MSG_INST_N_N_LZ			gettext("## Installation of part %d of %d in zone <%s> is complete.")
+#define	MSG_INST_ONE			gettext("   %d package pathname is already properly installed.")
+#define	MSG_INS_N_N			gettext("## Installing part %d of %d.")
+#define	MSG_INS_N_N_LZ			gettext("## Installing part %d of %d in zone <%s>.")
+#define	MSG_IS_PRESENT			gettext("%s <already present on Read Only file system>")
+#define	MSG_LOG_ERROR			gettext("ERROR")
+#define	MSG_LOG_WARNING			gettext("WARNING")
+#define	MSG_LOG_DEBUG			gettext("DEBUG")
+#define	MSG_MAIL			gettext("An attempt to install the <%s> pre-SVR4 package on <%s> completed with exit status <%d>.")
+#define	MSG_MANMOUNT			gettext("Assuming mounts have been provided.")
+#define	MSG_MORETODO			gettext("\nThere are %d more packages to be removed.")
+#define	MSG_MORE_INST			gettext("\nThere are %d more packages to be installed.")
+#define	MSG_MORE_PROC			gettext("\nThere are %d more packages to be processed.")
+#define	MSG_NOCHANGE			gettext("No changes were made to the system.")
+#define	MSG_NODENAME			gettext("(unknown)")
+#define	MSG_NOTEMPTY			gettext("%s <non-empty directory not removed>")
+#define	MSG_NOTREMOVED_INHERITED	gettext("%s <not removed - inherited from global zone>")
+#define	MSG_N_PKGS_NOT_PROCESSED	gettext("\n%d packages were not processed!\n")
+#define	MSG_PASSPROMPT			gettext("Enter keystore password:")
+#define	MSG_PKGADDCHK_ABADFILE		gettext("\\nPackaging file <%s> is corrupt for %s <%s> on %s <%s>")
+#define	MSG_PKGADDCHK_BADFILE		gettext("\\nPackaging files are corrupt for %s <%s> on %s <%s>.")
+#define	MSG_PKGADDCHK_CFCONTENT		gettext("\\nThe file <%s> is already installed and in use by %s <%s> on %s <%s>.")
+#define	MSG_PKGADDCHK_CKRUNLVL		gettext("\\nThe current run-level of this machine is <s%>, which is not a run-level suggested for installation of the %s <%s> on %s <%s>.")
+#define	MSG_PKGADDCHK_CNFFAILED		gettext("\\nConflict checking issues for %s <%s> on %s <%s>.")
+#define	MSG_PKGADDCHK_DEPEND		gettext("\\nDependency checking issues for %s <%s> on %s <%s>.")
+#define	MSG_PKGADDCHK_DIRS 		gettext("\\nThe required packaging directory <%s> cannot be created or accessed for %s <%s> on %s <%s>.")
+#define	MSG_PKGADDCHK_NEWONLY		gettext("\\nA version of %s <%s> is already installed on %s <%s>.  Current administration does not allow new instances of an existing package to be created, nor existing instances to be overwritten.")
+#define	MSG_PKGADDCHK_OVERWRITE		gettext("\\nCurrent administration does not allow new instances of a %s <%s> on %s <%s> to be created. However, the installation service was unable to determine which package instance to overwrite.")
+#define	MSG_PKGADDCHK_PARTINST		gettext("\\nThe installation of %s <%s> on %s <%s> previously terminated and installation was never successfully completed.")
+#define	MSG_PKGADDCHK_PARTREM		gettext("\\nThe removal of %s <%s> on %s <%s> was terminated at some point in time, and package removal was only partially completed.")
+#define	MSG_PKGADDCHK_PKGDIRS		gettext("\\nA required packaging directory cannot be created or accessed for %s <%s> on %s <%s>.")
+#define	MSG_PKGADDCHK_PRENCI 		gettext("\\nThe package <%s> is a prerequisite package and is not completely installed for %s <%s> on %s <%s>.")
+#define	MSG_PKGADDCHK_PREREQ 		gettext("\\nThe package <%s> is a prerequisite package and should be installed for %s <%s> on %s <%s>.")
+#define	MSG_PKGADDCHK_PRIV		gettext("\\nThe %s <%s> contains scripts which will be executed on %s <%s> with super-user permission during the process of installing this package.")
+#define	MSG_PKGADDCHK_RUNLEVEL		gettext("\\n run level <%s> for %s <%s> on %s <%s>.")
+#define	MSG_PKGADDCHK_SAME		gettext("\\nThis appears to be an attempt to install the same architecture and version of %s <%s> which is already installed on %s <%s>.  This installation will attempt to overwrite this package.\\n")
+#define	MSG_PKGADDCHK_SETUID		gettext("\\nFiles that are setuid and/or setgid will be installed and/or modified for %s <%s> on %s <%s>.")
+#define	MSG_PKGADDCHK_SPCFAILED		gettext("\\nSpace checking failed for %s <%s> on %s <%s>.")
+#define	MSG_PKGADDCHK_UNIQ1		gettext("\\nCurrent administration requires that a unique instance of %s <%s> on %s <%s> be created.  However, the maximum number of instances of the package which may be supported at one time on the same system has already been met.")
+#define	MSG_PKGINSTALL_DRYRUN		gettext("\nDryrunning install of %s as <%s>\n")
+#define	MSG_PKGINSTALL_EXECOC_GZ	gettext("## Executing checkinstall script.")
+#define	MSG_PKGINSTALL_EXECOC_LZ	gettext("## Executing checkinstall script in zone <%s>.")
+#define	MSG_PKGINSTALL_EXEPIC_GZ	gettext("## Executing postinstall script.")
+#define	MSG_PKGINSTALL_EXEPIC_LZ	gettext("## Executing postinstall script in zone <%s>.")
+#define	MSG_PKGINSTALL_EXEPOC_GZ	gettext("## Executing preinstall script.")
+#define	MSG_PKGINSTALL_EXEPOC_LZ	gettext("## Executing preinstall script in zone <%s>.")
+#define	MSG_PKGINSTALL_INSIN_GZ		gettext("\nInstalling %s as <%s>\n")
+#define	MSG_PKGINSTALL_INSIN_LZ		gettext("\nInstalling %s as <%s> in zone <%s>\n")
+#define	MSG_PKGREMOVE_DEPEND		gettext("Dependency checking failed.")
+#define	MSG_PKGREMOVE_EXEPIC_GZ		gettext("## Executing postremove script.")
+#define	MSG_PKGREMOVE_EXEPIC_LZ		gettext("## Executing postremove script in zone <%s>.")
+#define	MSG_PKGREMOVE_EXEPOC_GZ		gettext("## Executing preremove script.")
+#define	MSG_PKGREMOVE_EXEPOC_LZ		gettext("## Executing preremove script in zone <%s>.")
+#define	MSG_PKGREMOVE_ID_STR		gettext("ID")
+#define	MSG_PKGREMOVE_NAME_STR		gettext("Name")
+#define	MSG_PKGREMOVE_PRIV		gettext("\\nThis package contains scripts which will be executed with super-user permission during the process of removing this package.")
+#define	MSG_PKGREMOVE_PROCPKG_GZ	gettext("## Processing package information.")
+#define	MSG_PKGREMOVE_PROCPKG_LZ	gettext("## Processing package information in zone <%s>.")
+#define	MSG_PKGREMOVE_REMPATHCLASS_GZ	gettext("## Removing pathnames in class <%s>")
+#define	MSG_PKGREMOVE_REMPATHCLASS_LZ	gettext("## Removing pathnames in class <%s> in zone <%s>")
+#define	MSG_PKGREMOVE_RUNLEVEL		gettext("\\nThe current run-level of this machine is <%s>, which is not a run-level suggested for removal of this package.  Suggested run-levels (in order of preference) include:")
+#define	MSG_PKGREMOVE_UPDINF_GZ		gettext("## Updating system information.")
+#define	MSG_PKGREMOVE_UPDINF_LZ		gettext("## Updating system information in zone <%s>.")
+#define	MSG_PKGREMOVE_WSDEPEND		gettext("Product Registry dependency checking failed.  This package is assumed\nto be installed by the following products.  If the package is removed, it will be 'damaged' - that is, it is lacking essential software that the product\nrequires to function.  The recommended way to uninstall products is to\nuse the prodreg(1m) uninstall command.\n\nThe following products depend on the package:\n")
+#define	MSG_PKGRMCHK_CKRUNLVL		gettext("\\nThe current run-level of this machine is <s%>, which is not a run-level suggested for removal of the %s <%s> on %s <%s>.")
+#define	MSG_PKGRMCHK_DEPEND		gettext("\\nDependency checking failed for %s <%s> on %s <%s>.")
+#define	MSG_PKGRMCHK_DEPSONME		gettext("\\nThe package <%s> depends on %s <%s> currently being removed from %s <%s>.")
+#define	MSG_PKGRMCHK_PRENCI 		gettext("\\nThe package <%s> is a prerequisite package and is not completely installed for %s <%s> on %s <%s>.")
+#define	MSG_PKGRMCHK_PREREQ 		gettext("\\nThe package <%s> is a prerequisite package and should be removed for %s <%s> on %s <%s>.")
+#define	MSG_PKGRMCHK_PRIV		gettext("\\nThe %s <%s> contains scripts which will be executed on %s <%s> with super-user permission during the process of removing this package.")
+#define	MSG_PKGRMCHK_RUNLEVEL		gettext("\\n run level <%s> for %s <%s> on %s <%s>.")
+#define	MSG_PKGSCRIPTS_FOUND		gettext("Package scripts were found.")
+#define	MSG_PREIVFY_GETYORN_SUSP	gettext("\\nInstallation of <%s> was suspended (interaction required).")
+#define	MSG_PREIVFY_GETYORN_TERM	gettext("\\nInstallation of <%s> was terminated.")
+#define	MSG_PREIVFY_GETYORN_TERM_USER	gettext("\\nInstallation of <%s> was terminated due to user request.")
+#define	MSG_PREREMOVE_REMINST		gettext("\n## Removing installed package instance <%s>")
+#define	MSG_PRERVFY_GETYORN_SUSP	gettext("\\nRemoval of <%s> was suspended (interaction required).")
+#define	MSG_PRERVFY_GETYORN_TERM	gettext("\\nRemoval of <%s> was terminated.")
+#define	MSG_PRERVFY_GETYORN_TERM_USER	gettext("\\nRemoval of <%s> was terminated due to user request.")
+#define	MSG_PROCMV			gettext("- executing process moved to <%s>")
+#define	MSG_PROC_CONT			gettext("\nProcessing continuation packages from <%s>")
+#define	MSG_PROC_INST			gettext("\nProcessing package instance <%s> from <%s>")
+#define	MSG_REMOVE_PKG_FROM_ZONE	gettext("## Removing package <%s> from zone <%s>")
+#define	MSG_RESTORE_ZONE_STATE		gettext("## Restoring state of global zone <%s>")
+#define	MSG_RMSRVR			gettext("%s <removed from server's file system>")
+#define	MSG_SERVER			gettext("%s <server package pathname not removed>")
+#define	MSG_SHARED			gettext("%s <shared pathname not removed>")
+#define	MSG_SHIGN			gettext("%s <conflicting pathname not installed>")
+#define	MSG_SKIPPING_ZONE_NOT_RUNNABLE	gettext("## Not processing zone <%s>: the zone is not running and cannot be booted")
+#define	MSG_SLINK			gettext("%s <symbolic link>")
+#define	MSG_SUCCEED			gettext("\n## Pre-SVR4 package reports successful installation.")
+#define	MSG_SUSPEND_ADD			gettext("Installation of <%s> has been suspended.")
+#define	MSG_SUSPEND_RM			gettext("Removals of <%s> has been suspended.")
+#define	MSG_UGID			gettext("%s <installed with setuid/setgid bits reset>")
+#define	MSG_UGMOD			gettext("%s <reset setuid/setgid bits>")
+#define	MSG_VERIFYING			gettext("Verifying signer <%s>")
+#define	MSG_VERIFYING_CLASS		gettext("[ verifying class <%s> ]")
+
+#define	PASSWD_CMDLINE			gettext("## WARNING: USING \"%s\" MAKES PASSWORD VISIBLE TO ALL USERS.")
+#define	SPECIAL_ACCESS			gettext("unable to maintain package contents text due to an access failure: %s")
+#define	SPECIAL_INPUT			gettext("unable to maintain package contents text: alternate root path too long")
+#define	SPECIAL_MALLOC			gettext("unable to maintain package contents text due to insufficient memory: %s")
+#define	SPECIAL_MAP			gettext("unable to maintain package contents text due to a failure to map the database into memory: %S")
+
+#define	WRN_BAD_FORK			gettext("WARNING: bad fork(), errno=%d: %s")
+#define	WRN_BAD_WAIT			gettext("WARNING: wait for process %ld failed, pid <%ld> status <0x%08lx> errno <%d> (%s)")
+#define	WRN_CHKINSTALL 			gettext("checkinstall script suspends")
+#define	WRN_DEF_MODE			gettext("WARNING: installing <%s> with default mode of 644")
+#define WRN_SET_DEF_MODE		gettext("WARNING: setting mode of <%s> to default mode (%o)")
+#define	WRN_FINALCK_ATTR		gettext("WARNING: attribute verification of <%s> failed")
+#define	WRN_FINALCK_CONT		gettext("WARNING: content verification of <%s> failed")
+#define	WRN_FLMAIL			gettext("WARNING: e-mail notification may have failed")
+#define	WRN_FSTAB_MOUNT			gettext("WARNING: unable to mount client's file system at %s - errcode=%d")
+#define	WRN_FSTAB_UMOUNT		gettext("WARNING: unable to unmount client's file system at %s - errcode=%d.")
+#define	WRN_INSTVOL_NONE		gettext("WARNING: %s <not present on Read Only file system>")
+#define	WRN_INSTVOL_NOTDIR		gettext("WARNING: %s may not overwrite a populated directory.")
+#define	WRN_INSTVOL_NOVERIFY		gettext("WARNING: %s <cannot install to or verify on %s>")
+#define	WRN_NOMAIL			gettext("WARNING: unable to send e-mail notification")
+#define	WRN_PKGREMOVE_PATCHES		gettext("\\nWARNING: The following patch(es) are installed to <%s>. If <%s> is removed, the patches applied to it will be removed as well leaving the patch partially installed. It is recommended that the patch(es) be removed before removing <%s>.\\n\\t%s")
+#define	WRN_RELATIVE			gettext("attempting to rename a relative file <%s>")
+#define	WRN_RSCRIPTALT_BAD		gettext("WARNING: the admin parameter <%s> is set to <%s> which is not recognized; the parameter may only be set to <%s> or <%s>")
+#define	WRN_RSCRIPTALT_USING		gettext("WARNING: the admin parameter <%s> is assumed to be set to <%s>")
+#define	WRN_UNKNOWN_ADM_PARAM		gettext("WARNING: unknown admin parameter <%s>")
+
+#define	NOTE_INSTVOL_FINALCKFAIL	gettext("NOTE: When the package <%s> was installed in the global zone,\nthe file <%s> was also installed. After the file was\ninstalled in the global zone, the contents and/or attributes of the file\nchanged. The contents of this file must never be changed. As a result,\nthe changes in this file have been duplicated in the non-global zone\n<%s> in the file <%s>.")
+
+#define	MSG_REBOOT			gettext("\\n*** IMPORTANT NOTICE ***\\n" \
+			"\\tThis machine must now be rebooted in order to " \
+			"ensure\\n" \
+			"\\tsane operation.  Execute\\n\\t\\tshutdown -y -i6 " \
+			"-g0\\n" \
+			"\\tand wait for the \"Console Login:\" prompt.")
+
+/*
+ * These messages are output by qreason() - they are the "reason"
+ * for the success/fail of the operation
+ */
+
+#define	MSG_UNKREQ						gettext \
+			("qreason(): unrecognized message request.")
+#define	MSG_RE_SUC						gettext \
+			("Processing of request script was successful.")
+#define	MSG_IN_SUC0						gettext \
+			("Installation of <%s> was successful.")
+#define	MSG_IN_SUC1						gettext \
+			("\nInstallation of %s on %s as package instance " \
+			"<%s> was successful.")
+#define	MSG_RM_SUC0						gettext \
+			("Removal of <%s> was successful.")
+#define	MSG_RM_SUC1						gettext \
+			("\nRemoval of <%s> package instance on %s was " \
+			"successful.")
+#define	MSG_RE_FAIL						gettext \
+			("Processing of request script failed.")
+#define	MSG_IN_FAIL0						gettext \
+			("Installation of <%s> failed.")
+#define	MSG_IN_FAIL1						gettext \
+			("\nInstallation of %s on %s as package instance " \
+			"<%s> failed.")
+#define	MSG_RM_FAIL0						gettext \
+			("Removal of <%s> failed.")
+#define	MSG_RM_FAIL1						gettext \
+			("\nRemoval of <%s> package instance on %s failed.")
+#define	MSG_RE_PARFAIL						gettext \
+			("Processing of request script partially failed.")
+#define	MSG_IN_PARFAIL0						gettext \
+			("Installation of <%s> partially failed.")
+#define	MSG_IN_PARFAIL1						gettext \
+			("\nInstallation of %s on %s as package instance " \
+			"<%s> partially failed.")
+#define	MSG_RM_PARFAIL0						gettext \
+			("Removal of <%s> partially failed.")
+#define	MSG_RM_PARFAIL1						gettext \
+			("\nRemoval of <%s> package instance on %s partially " \
+			"failed.")
+#define	MSG_RE_USER						gettext \
+			("Processing of request script was terminated due to " \
+			"user request.")
+#define	MSG_IN_USER0						gettext \
+			("Installation of <%s> was terminated due to user " \
+			"request.")
+#define	MSG_IN_USER1						gettext \
+			("\nInstallation of %s on %s as package instance " \
+			"<%s> was terminated due to user request.")
+#define	MSG_RM_USER0						gettext \
+			("Removal of <%s> was terminated due to user request.")
+#define	MSG_RM_USER1						gettext \
+			("\nRemoval of <%s> package instance on %s was " \
+			"terminated due to user request.")
+#define	MSG_RE_SUA						gettext \
+			("Processing of request script was suspended " \
+			"(administration).")
+#define	MSG_IN_SUA0						gettext \
+			("Installation of <%s> was suspended (administration).")
+#define	MSG_IN_SUA1						gettext \
+			("\nInstallation of %s on %s as package instance " \
+			"<%s> was suspended (administration).")
+#define	MSG_RM_SUA0						gettext \
+			("Removal of <%s> was suspended (administration).")
+#define	MSG_RM_SUA1						gettext \
+			("\nRemoval of <%s> package instance on %s was " \
+			"suspended (administration).")
+#define	MSG_RE_SUI						gettext \
+			("Processing of request script was suspended " \
+			"(interaction required).")
+#define	MSG_IN_SUI0						gettext \
+			("Installation of <%s> was suspended (interaction " \
+			"required).")
+#define	MSG_IN_SUI1						gettext \
+			("\nInstallation of %s on %s as package instance " \
+			"<%s> was suspended (interaction required).")
+#define	MSG_RM_SUI0						gettext \
+			("Removal of <%s> was suspended (interaction " \
+			"required).")
+#define	MSG_RM_SUI1						gettext \
+			("\nRemoval of <%s> package instance on %s was " \
+			"suspended (interaction required).")
+#define	MSG_RE_IEPI						gettext \
+			("Processing of request script failed (internal " \
+			"error) - package partially installed.")
+#define	MSG_IN_IEPI0						gettext \
+			("Installation of <%s> failed (internal error) - " \
+			"package partially installed.")
+#define	MSG_IN_IEPI1						gettext \
+			("\nInstallation of %s on %s as package instance " \
+			"<%s> failed (internal error) - package partially " \
+			"installed.")
+#define	MSG_RM_IEPI0						gettext \
+			("Removal of <%s> failed (internal error) - package " \
+			"partially installed.")
+#define	MSG_RM_IEPI1						gettext \
+			("\nRemoval of <%s> package instance on %s failed " \
+			"(internal error) - package partially installed.")
+#define	MSG_RE_IE						gettext \
+			("Processing of request script failed (internal " \
+			"error).")
+#define	MSG_IN_IE0						gettext \
+			("Installation of <%s> failed (internal error).")
+#define	MSG_IN_IE1						gettext \
+			("\nInstallation of %s on %s as package instance " \
+			"<%s> failed (internal error).")
+#define	MSG_RM_IE0						gettext \
+			("Removal of <%s> failed (internal error).")
+#define	MSG_RM_IE1						gettext \
+			("\nRemoval of <%s> package instance on %s failed " \
+			"(internal error).")
+#define	MSG_RE_UNK						gettext \
+			("Processing of request script failed with an " \
+			"unrecognized error code.")
+#define	MSG_IN_UNK0						gettext \
+			("Installation of <%s> failed with an unrecognized " \
+			"error code.")
+#define	MSG_IN_UNK1						gettext \
+			("\nInstallation of %s on %s as package instance " \
+			"<%s> failed with an unrecognized error code.")
+#define	MSG_RM_UNK0						gettext \
+			("Removal of <%s> failed with an unrecognized error " \
+			"code.")
+#define	MSG_RM_UNK1						gettext \
+			("\nRemoval of <%s> package instance on %s failed " \
+			"with an unrecognized error code.")
+/* WITH ZONE NAME */
+#define	MSG_UNKREQ_ZONE						gettext \
+			("qreason(): unrecognized message request.")
+#define	MSG_RE_SUC_ZONE						gettext \
+			("Processing of request script for zone <%s> was " \
+			"successful.")
+#define	MSG_IN_SUC0_ZONE					gettext \
+			("Installation of <%s> on zone <%s> was successful.")
+#define	MSG_IN_SUC1_ZONE					gettext \
+			("\nInstallation of %s on %s as package instance " \
+			"<%s> on zone <%s> was successful.")
+#define	MSG_RM_SUC0_ZONE					gettext \
+			("Removal of <%s> from zone <%s> was successful.")
+#define	MSG_RM_SUC1_ZONE					gettext \
+			("\nRemoval of <%s> package instance on %s from " \
+			"zone <%s> was successful.")
+#define	MSG_RE_FAIL_ZONE					gettext \
+			("Processing of request script for zone <%s> failed.")
+#define	MSG_IN_FAIL0_ZONE					gettext \
+			("Installation of <%s> on zone <%s> failed.")
+#define	MSG_IN_FAIL1_ZONE					gettext \
+			("\nInstallation of %s on %s as package instance " \
+			"<%s> on zone <%s> failed.")
+#define	MSG_RM_FAIL0_ZONE					gettext \
+			("Removal of <%s> from zone <%s> failed.")
+#define	MSG_RM_FAIL1_ZONE					gettext \
+			("\nRemoval of <%s> package instance on %s from zone " \
+			"<%s> failed.")
+#define	MSG_RE_PARFAIL_ZONE					gettext \
+			("Processing of request script partially failed on " \
+			"zone <%s>.")
+#define	MSG_IN_PARFAIL0_ZONE					gettext \
+			("Installation of <%s> on zone <%s> partially failed.")
+#define	MSG_IN_PARFAIL1_ZONE					gettext \
+			("\nInstallation of %s on %s as package instance " \
+			"<%s> on zone <%s> partially failed.")
+#define	MSG_RM_PARFAIL0_ZONE					gettext \
+			("Removal of <%s> from zone <%s> partially failed.")
+#define	MSG_RM_PARFAIL1_ZONE					gettext \
+			("\nRemoval of <%s> package instance on %s from zone " \
+			"<%s> partially failed.")
+#define	MSG_RE_USER_ZONE					gettext \
+			("Processing of request script on zone <%s> was " \
+			"terminated due to user request.")
+#define	MSG_IN_USER0_ZONE					gettext \
+			("Installation of <%s> on zone <%s> was terminated " \
+			"due to user request.")
+#define	MSG_IN_USER1_ZONE					gettext \
+			("\nInstallation of %s on %s as package instance " \
+			"<%s> on zone <%s> was terminated due to user request.")
+#define	MSG_RM_USER0_ZONE					gettext \
+			("Removal of <%s> from zone <%s> was terminated due " \
+			"to user request.")
+#define	MSG_RM_USER1_ZONE					gettext \
+			("\nRemoval of <%s> package instance on %s from " \
+			"zone <%s> was terminated due to user request.")
+#define	MSG_RE_SUA_ZONE						gettext \
+			("Processing of request script on zone <%s> was " \
+			"suspended (administration).")
+#define	MSG_IN_SUA0_ZONE					gettext \
+			("Installation of <%s> on zone <%s> was suspended " \
+			"(administration).")
+#define	MSG_IN_SUA1_ZONE					gettext \
+			("\nInstallation of %s on %s as package instance " \
+			"<%s> on zone <%s> was suspended (administration).")
+#define	MSG_RM_SUA0_ZONE					gettext \
+			("Removal of <%s> from zone <%s> was suspended " \
+			"(administration).")
+#define	MSG_RM_SUA1_ZONE					gettext \
+			("\nRemoval of <%s> package instance on %s from " \
+			"zone <%s> was suspended (administration).")
+#define	MSG_RE_SUI_ZONE						gettext \
+			("Processing of request script on zone <%s> was " \
+			"suspended (interaction required).")
+#define	MSG_IN_SUI0_ZONE					gettext \
+			("Installation of <%s> on zone <%s> was suspended " \
+			"(interaction required).")
+#define	MSG_IN_SUI1_ZONE					gettext \
+			("\nInstallation of %s on %s as package instance " \
+			"<%s> on zone <%s> was suspended (interaction " \
+			"required).")
+#define	MSG_RM_SUI0_ZONE					gettext \
+			("Removal of <%s> from zone <%s> was suspended " \
+			"(interaction required).")
+#define	MSG_RM_SUI1_ZONE					gettext \
+			("\nRemoval of <%s> package instance on %s from " \
+			"zone <%s> was suspended (interaction required).")
+#define	MSG_RE_IEPI_ZONE					gettext \
+			("Processing of request script on zone <%s> " \
+			"failed (internal error) - package partially " \
+			"installed.")
+#define	MSG_IN_IEPI0_ZONE					gettext \
+			("Installation of <%s> on zone failed (internal " \
+			"error) on zone <%s> - package partially installed.")
+#define	MSG_IN_IEPI1_ZONE					gettext \
+			("\nInstallation of %s on %s as package instance " \
+			" <%s> on zone <%s> failed (internal error) - " \
+			"package partially installed.")
+#define	MSG_RM_IEPI0_ZONE					gettext \
+			("Removal of <%s> from zone <%s> failed (internal " \
+			"error) - package partially installed.")
+#define	MSG_RM_IEPI1_ZONE					gettext \
+			("\nRemoval of <%s> package instance on %s from " \
+			"zone <%s> failed (internal error) - package " \
+			"partially installed.")
+#define	MSG_RE_IE_ZONE						gettext \
+			("Processing of request script on zone <%s> failed " \
+			"(internal error).")
+#define	MSG_IN_IE0_ZONE						gettext \
+			("Installation of <%s> on zone <%s> failed (internal " \
+			"error).")
+#define	MSG_IN_IE1_ZONE						gettext \
+			("\nInstallation of %s on %s as package instance " \
+			"<%s> on zone <%s> failed (internal error).")
+#define	MSG_RM_IE0_ZONE						gettext \
+			("Removal of <%s> on zone <%s> failed (internal " \
+			"error).")
+#define	MSG_RM_IE1_ZONE						gettext \
+			("\nRemoval of <%s> package instance on %s from " \
+			"zone <%s> failed (internal error).")
+#define	MSG_RE_UNK_ZONE						gettext \
+			("Processing of request script on zone <%s> failed " \
+			"with an unrecognized error code.")
+#define	MSG_IN_UNK0_ZONE					gettext \
+			("Installation of <%s> on zone <%s> failed with an " \
+			"unrecognized error code.")
+#define	MSG_IN_UNK1_ZONE					gettext \
+			("\nInstallation of %s on %s as package instance " \
+			"<%s> on zone <%s> failed with an unrecognized " \
+			"error code.")
+#define	MSG_RM_UNK0_ZONE					gettext \
+			("Removal of <%s> from zone <%s> failed with an " \
+			"unrecognized error code.")
+#define	MSG_RM_UNK1_ZONE					gettext \
+			("\nRemoval of <%s> package instance on %s from " \
+			"zone <%s> failed with an unrecognized error code.")
+
+#define	MSG_UNIQ1						gettext( \
+			"\\nCurrent administration requires that a unique " \
+			"instance of the <%s> package be created.  However, " \
+			"the maximum number of instances of the package " \
+			"which may be supported at one time on the same " \
+			"system has already been met.")
+
+#define	MSG_NOINTERACT						gettext( \
+			"\\nUnable to determine whether to overwrite an " \
+			"existing package instance, or add a new instance.")
+
+#define	MSG_NEWONLY						gettext( \
+			"\\nA version of the <%s> package is already " \
+			"installed on this machine.  Current administration " \
+			"does not allow new instances of an existing package " \
+			"to be created, nor existing instances to be " \
+			"overwritten.")
+
+#define	MSG_SAME						gettext( \
+			"\\nThis appears to be an attempt to install the " \
+			"same architecture and version of a package which " \
+			"is already installed.  This installation will " \
+			"attempt to overwrite this package.\\n")
+
+#define	MSG_OVERWRITE						gettext( \
+			"\\nCurrent administration does not allow new " \
+			"instances of an existing package to be created.  " \
+			"However, the installation service was unable to " \
+			"determine which package instance to overwrite.")
+
+
+#define	MSG_GETINST_PROMPT0				gettext( \
+		"Do you want to overwrite this installed instance")
+
+#define	MSG_GETINST_PROMPT1				gettext( \
+		"Do you want to create a new instance of this package")
+
+#define	MSG_GETINST_HELP1				gettext( \
+		"The package you are attempting to install already exists " \
+		"on this machine.  You may choose to create a new instance " \
+		"of this package by answering 'y' to this prompt.  If you " \
+		"answer 'n' you will be asked to choose one of the instances " \
+		"which is already to be overwritten.")
+
+#define	MSG_GETINST_HEADER				gettext( \
+		"The following instance(s) of the <%s> package are already " \
+		"installed on this machine:")
+
+#define	MSG_GETINST_PROMPT2				gettext( \
+		"Enter the identifier for the instance that you want to " \
+		"overwrite")
+
+#define	MSG_GETINST_HELP2				gettext( \
+		"The package you are attempting to install already exists on " \
+		"this machine.  You may choose to overwrite one of the " \
+		"versions which is already installed by selecting the " \
+		"appropriate entry from the menu.")
+
+/*
+ * I18N: MSG_GZONLY_FILE_HEADER must NOT be translated!
+ * ----- This message is placed at the beginning of an internal (private)
+ * ----- database file. The contents of the message is a warning telling
+ * ----- anyone who examines the contents of the database to not modify the
+ * ----- database manually (by hand).
+ * ----- Do NOT change or translate this text!
+ */
+
+#define	MSG_GZONLY_FILE_HEADER		\
+"# DO NOT EDIT THIS FILE BY HAND. This file is not a public interface.\n" \
+"# The format and contents of this file are subject to change.\n" \
+"# Any user modification to this file may result in the incorrect\n" \
+"# operation of the package and patch tools.\n" \
+"# Last modified by <%s> to <%s> package <%s>\n# %s"
+
+/* END CSTYLED */
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif	/* _MESSAGES_H */
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/usr/src/cmd/svr4pkg/installf/Makefile	Fri Jun 05 10:28:40 2009 -0400
@@ -0,0 +1,49 @@
+#
+# 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
+# or http://www.opensolaris.org/os/licensing.
+# 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 2009 Sun Microsystems, Inc.  All rights reserved.
+# Use is subject to license terms.
+#
+
+PROG=		installf
+
+OBJS=		installf.o	\
+		main.o		\
+		removef.o	\
+		dofinal.o
+
+include $(SRC)/cmd/svr4pkg/Makefile.svr4pkg
+
+ROOTUSRSBINLINK  = $(ROOTUSRSBIN)/removef
+
+LDLIBS	+=	-lpkg -linstzones -ladm
+LDLIBS	+=	-lnsl -lsocket
+
+.KEEP_STATE:
+
+all:		$(PROG)
+install:        all $(ROOTUSRSBINPROG) $(ROOTUSRSBINLINK)
+
+$(ROOTUSRSBINLINK): $(ROOTUSRSBINPROG)
+	$(RM) $@; $(LN) $(ROOTUSRSBINPROG) $@
+
+include $(SRC)/cmd/svr4pkg/Makefile.svr4pkg.targ
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/usr/src/cmd/svr4pkg/installf/dofinal.c	Fri Jun 05 10:28:40 2009 -0400
@@ -0,0 +1,236 @@
+/*
+ * 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
+ * or http://www.opensolaris.org/os/licensing.
+ * 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 2006 Sun Microsystems, Inc.  All rights reserved.
+ * Use is subject to license terms.
+ */
+
+/* Copyright (c) 1984, 1986, 1987, 1988, 1989 AT&T */
+/* All Rights Reserved */
+
+
+#include <stdio.h>
+#include <errno.h>
+#include <string.h>
+#include <limits.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <sys/types.h>
+#include <pkgstrct.h>
+#include <locale.h>
+#include <libintl.h>
+#include <pkglib.h>
+#include "install.h"
+#include "libinst.h"
+#include "libadm.h"
+
+extern struct cfextra **extlist;
+extern struct cfent **eptlist;
+
+extern char	*pkginst;
+
+#define	ERR_WRITE	"write of intermediate contents file failed"
+
+static char *check_db_entry(VFP_T *, struct cfextra *, int, char *, int *);
+
+/*ARGSUSED*/
+int
+dofinal(VFP_T *vfp, VFP_T *vfpo, int rmflag, char *myclass, char *prog)
+{
+	struct cfextra entry;
+	int	n, indx, dbchg;
+	char	*save_path = NULL;
+
+	entry.cf_ent.pinfo = NULL;
+	entry.fsys_value = BADFSYS;
+	entry.fsys_base = BADFSYS;
+	indx = 0;
+
+	while (extlist && extlist[indx] && (extlist[indx]->cf_ent.ftype == 'i'))
+		indx++;
+
+	dbchg = 0;
+
+	while (n = srchcfile(&(entry.cf_ent), "*", vfp, vfpo)) {
+		if (n < 0) {
+			char	*errstr = getErrstr();
+			progerr(gettext
+				("bad entry read in contents file"));
+				logerr(gettext("pathname=%s"),
+				(entry.cf_ent.path &&
+				*(entry.cf_ent.path)) ?
+				entry.cf_ent.path : "Unknown");
+			logerr(gettext("problem=%s"),
+				(errstr && *errstr) ? errstr :
+				"Unknown");
+			quit(99);
+		}
+		save_path = check_db_entry(
+				vfpo, &entry, rmflag, myclass, &dbchg);
+
+		/* Restore original server-relative path, if needed */
+		if (save_path != NULL) {
+			entry.cf_ent.path = save_path;
+			save_path = NULL;
+		}
+	}
+
+	return (dbchg);
+}
+
+static char *
+check_db_entry(VFP_T *vfpo, struct cfextra *entry, int rmflag, char *myclass,
+		int *dbchg)
+{
+	struct pinfo *pinfo;
+	int	fs_entry;
+	char	*save_path = NULL;
+	char	*tp;
+
+	/* write this entry to the contents file */
+
+	if (myclass && strcmp(myclass, entry->cf_ent.pkg_class)) {
+		if (putcvfpfile(&entry->cf_ent, vfpo)) {
+			progerr(gettext(ERR_WRITE));
+			quit(99);
+		}
+		return (NULL);
+	}
+
+	/*
+	 * Now scan each package instance holding this file or
+	 * directory and see if it matches the package we are
+	 * updating here.
+	 */
+	pinfo = entry->cf_ent.pinfo;
+	while (pinfo) {
+		if (strcmp(pkginst, pinfo->pkg) == 0)
+			break;
+		pinfo = pinfo->next;
+	}
+
+	/*
+	 * If pinfo == NULL at this point, then this file or
+	 * directory isn't part of the package of interest.
+	 * So the loop below executes only on files in the package
+	 * of interest.
+	 */
+
+	save_path = NULL;
+
+	if (pinfo) {
+		if (rmflag && (pinfo->status == RM_RDY)) {
+			*dbchg = 1;
+
+			(void) eptstat(&(entry->cf_ent), pkginst, '@');
+
+			if (entry->cf_ent.npkgs) {
+				if (putcvfpfile(&(entry->cf_ent), vfpo)) {
+					progerr(gettext(ERR_WRITE));
+					quit(99);
+				}
+			}
+			return (NULL);
+
+		} else if (!rmflag && (pinfo->status == INST_RDY)) {
+			*dbchg = 1;
+
+			/* tp is the server-relative path */
+			tp = fixpath(entry->cf_ent.path);
+			/* save_path is the cmd line path */
+			save_path = entry->cf_ent.path;
+			/* entry has the server-relative path */
+			entry->cf_ent.path = tp;
+
+			/*
+			 * The next if statement figures out how
+			 * the contents file entry should be
+			 * annotated.
+			 *
+			 * Don't install or verify objects for
+			 * remote, read-only filesystems.  We
+			 * need only verify their presence and
+			 * flag them appropriately from some
+			 * server. Otherwise, ok to do final
+			 * check.
+			 */
+			fs_entry = fsys(entry->cf_ent.path);
+
+			if (is_remote_fs_n(fs_entry) &&
+				!is_fs_writeable_n(fs_entry)) {
+				/*
+				 * Mark it shared whether it's present
+				 * or not. life's too funny for me
+				 * to explain.
+				 */
+				pinfo->status = SERVED_FILE;
+
+				/*
+				 * restore for now. This may
+				 * chg soon.
+				 */
+				entry->cf_ent.path = save_path;
+			} else {
+				/*
+				 * If the object is accessible, check
+				 * the new entry for existence and
+				 * attributes. If there's a problem,
+				 * mark it NOT_FND; otherwise,
+				 * ENTRY_OK.
+				 */
+				if (is_mounted_n(fs_entry)) {
+					int	n;
+
+					n = finalck((&entry->cf_ent), 1, 1,
+							B_FALSE);
+
+					pinfo->status = ENTRY_OK;
+					if (n != 0) {
+						pinfo->status = NOT_FND;
+					}
+				}
+
+				/*
+				 * It's not remote, read-only but it
+				 * may look that way to the client.
+				 * If it does, overwrite the above
+				 * result - mark it shared.
+				 */
+				if (is_served_n(fs_entry))
+					pinfo->status = SERVED_FILE;
+
+				/* restore original path */
+				entry->cf_ent.path = save_path;
+				/*   and clear save_path */
+				save_path = NULL;
+			}
+		}
+	}
+
+	/* Output entry to contents file. */
+	if (putcvfpfile(&(entry->cf_ent), vfpo)) {
+		progerr(gettext(ERR_WRITE));
+		quit(99);
+	}
+
+	return (save_path);
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/usr/src/cmd/svr4pkg/installf/installf.c	Fri Jun 05 10:28:40 2009 -0400
@@ -0,0 +1,308 @@
+/*
+ * 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
+ * or http://www.opensolaris.org/os/licensing.
+ * 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 2006 Sun Microsystems, Inc.  All rights reserved.
+ * Use is subject to license terms.
+ */
+
+/* Copyright (c) 1984, 1986, 1987, 1988, 1989 AT&T */
+/* All Rights Reserved */
+
+
+#include <stdio.h>
+#include <errno.h>
+#include <string.h>
+#include <limits.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <sys/types.h>
+#include <pkgstrct.h>
+#include <locale.h>
+#include <libintl.h>
+#include <pkglib.h>
+#include <install.h>
+#include <libinst.h>
+#include <libadm.h>
+#include "installf.h"
+
+#define	LSIZE	1024
+#define	MALSIZ	164
+
+#define	ERR_MAJOR 	"invalid major number <%s> specified for <%s>"
+#define	ERR_MINOR 	"invalid minor number <%s> specified for <%s>"
+#define	ERR_MODE	"invalid mode <%s> specified for <%s>"
+#define	ERR_RELPATH 	"relative pathname <%s> not permitted"
+#define	ERR_NULLPATH 	"NULL or garbled pathname"
+#define	ERR_LINK	"invalid link specification <%s>"
+#define	ERR_LINKFTYPE	"ftype <%c> does not match link specification <%s>"
+#define	ERR_LINKARGS	"extra arguments in link specification <%s>"
+#define	ERR_LINKREL	"relative pathname in link specification <%s>"
+#define	ERR_FTYPE	"invalid ftype <%c> for <%s>"
+#define	ERR_ARGC 	"invalid number of arguments for <%s>"
+#define	ERR_SPECALL	"ftype <%c> requires all fields to be specified"
+
+static int validate(struct cfextra *ext, int argc, char *argv[]);
+static void checkPaths(char *argv[]);
+
+int
+installf(int argc, char *argv[])
+{
+	struct cfextra *new;
+	char	line[LSIZE];
+	char	*largv[8];
+	int	myerror;
+
+	if (strcmp(argv[0], "-") != 0) {
+		if (argc < 1)
+			usage(); /* at least pathname is required */
+		extlist = calloc(2, sizeof (struct cfextra *));
+		extlist[0] = new = calloc(1, sizeof (struct cfextra));
+		eptnum = 1;
+
+		/* There is only one filename on the command line. */
+		checkPaths(argv);
+		if (validate(new, argc, argv))
+			quit(1);
+		return (0);
+	}
+
+	/* Read stdin to obtain entries, which need to be sorted. */
+	eptnum = 0;
+	myerror = 0;
+	extlist = calloc(MALSIZ, sizeof (struct cfextra *));
+	while (fgets(line, LSIZE, stdin) != NULL) {
+		argc = 0;
+		argv = largv;
+		argv[argc++] = strtok(line, " \t\n");
+		while (argv[argc] = strtok(NULL, " \t\n"))
+			argc++;
+
+		if (argc < 1)
+			usage(); /* at least pathname is required */
+
+		new = calloc(1, sizeof (struct cfextra));
+		if (new == NULL) {
+			progerr(strerror(errno));
+			quit(99);
+		}
+
+		checkPaths(argv);
+
+		if (validate(new, argc, argv))
+			myerror++;
+
+		extlist[eptnum] = new;
+		if ((++eptnum % MALSIZ) == 0) {
+			extlist = realloc(extlist,
+			    (sizeof (struct cfextra *) * (eptnum+MALSIZ)));
+			if (!extlist) {
+				progerr(strerror(errno));
+				quit(99);
+			}
+		}
+	}
+	extlist[eptnum] = (struct cfextra *)NULL;
+	qsort((char *)extlist, (unsigned)eptnum, sizeof (struct cfextra *),
+	    cfentcmp);
+	return (myerror);
+}
+
+static int
+validate(struct cfextra *ext, int argc, char *argv[])
+{
+	char	*ret, *pt;
+	int	n, allspec, is_a_link;
+	struct	cfent *ept;
+
+	ept = &(ext->cf_ent);
+
+	/* initialize cfent structure */
+	ept->pinfo = NULL;
+	(void) gpkgmapvfp(ept, (VFP_T *)NULL);	/* This just clears stuff. */
+
+	n = allspec = 0;
+	if (classname)
+		(void) strncpy(ept->pkg_class, classname, CLSSIZ);
+
+	if (argv[n] == NULL || *(argv[n]) == '\000') {
+		progerr(gettext(ERR_NULLPATH));
+		return (1);
+	}
+
+	/*
+	 * It would be a good idea to figure out how to get much of
+	 * this done using facilities in procmap.c - JST
+	 */
+	if (pt = strchr(argv[n], '=')) {
+		*pt = '\0';	/* cut off pathname at the = sign */
+		is_a_link = 1;
+	} else
+		is_a_link = 0;
+
+	if (RELATIVE(argv[n])) {
+		progerr(gettext(ERR_RELPATH),
+		    (argv[n] == NULL) ? "unknown" : argv[n]);
+		return (1);
+	}
+
+	/* get the pathnames */
+	if (eval_path(&(ext->server_path), &(ext->client_path),
+	    &(ext->map_path), argv[n++]) == 0)
+		return (1);
+
+	ept->path = ext->client_path;
+
+	/* This isn't likely to happen; but, better safe than sorry. */
+	if (RELATIVE(ept->path)) {
+		progerr(gettext(ERR_RELPATH), ept->path);
+		return (1);
+	}
+
+	if (is_a_link) {
+		/* links specifications should be handled right here */
+		ept->ftype = ((n >= argc) ? 'l' : argv[n++][0]);
+
+		/* If nothing follows the '=', it's invalid */
+		if (!pt[1]) {
+			progerr(gettext(ERR_LINK), ept->path);
+			return (1);
+		}
+
+		/* Test for an argument after the link. */
+		if (argc != n) {
+			progerr(gettext(ERR_LINKARGS), ept->path);
+			return (1);
+		}
+
+		/*
+		 * If it's a link but it's neither hard nor symbolic then
+		 * it's bad.
+		 */
+		if (!strchr("sl", ept->ftype)) {
+			progerr(gettext(ERR_LINKFTYPE), ept->ftype, ept->path);
+			return (1);
+		}
+
+		ext->server_local = pathdup(pt+1);
+		ext->client_local = ext->server_local;
+
+		ept->ainfo.local = ext->client_local;
+
+		return (0);
+	} else if (n >= argc) {
+		/* we are expecting to change object's contents */
+		return (0);
+	}
+
+	ept->ftype = argv[n++][0];
+	if (strchr("sl", ept->ftype)) {
+		progerr(gettext(ERR_LINK), ept->path);
+		return (1);
+	} else if (!strchr("?fvedxcbp", ept->ftype)) {
+		progerr(gettext(ERR_FTYPE), ept->ftype, ept->path);
+		return (1);
+	}
+
+	if (ept->ftype == 'b' || ept->ftype == 'c') {
+		if (n < argc) {
+			ept->ainfo.major = strtol(argv[n++], &ret, 0);
+			if (ret && *ret) {
+				progerr(gettext(ERR_MAJOR), argv[n-1],
+				    ept->path);
+				return (1);
+			}
+		}
+		if (n < argc) {
+			ept->ainfo.minor = strtol(argv[n++], &ret, 0);
+			if (ret && *ret) {
+				progerr(gettext(ERR_MINOR), argv[n-1],
+				    ept->path);
+				return (1);
+			}
+			allspec++;
+		}
+	}
+
+	allspec = 0;
+	if (n < argc) {
+		ept->ainfo.mode = strtol(argv[n++], &ret, 8);
+		if (ret && *ret) {
+			progerr(gettext(ERR_MODE), argv[n-1], ept->path);
+			return (1);
+		}
+	}
+	if (n < argc)
+		(void) strncpy(ept->ainfo.owner, argv[n++], ATRSIZ);
+	if (n < argc) {
+		(void) strncpy(ept->ainfo.group, argv[n++], ATRSIZ);
+		allspec++;
+	}
+	if (strchr("dxbcp", ept->ftype) && !allspec) {
+		progerr(gettext(ERR_ARGC), ept->path);
+		progerr(gettext(ERR_SPECALL), ept->ftype);
+		return (1);
+	}
+	if (n < argc) {
+		progerr(gettext(ERR_ARGC), ept->path);
+		return (1);
+	}
+	return (0);
+}
+
+int
+cfentcmp(const void *p1, const void *p2)
+{
+	struct cfextra *ext1 = *((struct cfextra **)p1);
+	struct cfextra *ext2 = *((struct cfextra **)p2);
+
+	return (strcmp(ext1->cf_ent.path, ext2->cf_ent.path));
+}
+
+/*
+ * If the path at argv[0] has the value of
+ * PKG_INSTALL_ROOT prepended, remove it
+ */
+static void
+checkPaths(char *argv[])
+{
+	char *root;
+	int rootLen;
+
+	/*
+	 * Note- No local copy of argv is needed since this
+	 * function is guaranteed to replace argv with a subset of
+	 * the original argv.
+	 */
+
+	/* We only want to canonize the path if it contains multiple '/'s */
+
+	canonize_slashes(argv[0]);
+
+	if ((root = get_inst_root()) == NULL)
+		return;
+	if (strcmp(root, "/") != 0) {
+		rootLen = strlen(root);
+		if (strncmp(argv[0], root, rootLen) == 0) {
+			argv[0] += rootLen;
+		}
+	}
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/usr/src/cmd/svr4pkg/installf/installf.h	Fri Jun 05 10:28:40 2009 -0400
@@ -0,0 +1,57 @@
+/*
+ * 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
+ * or http://www.opensolaris.org/os/licensing.
+ * 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 2006 Sun Microsystems, Inc.  All rights reserved.
+ * Use is subject to license terms.
+ */
+
+#ifndef _INSTALLF_H
+#define	_INSTALLF_H
+
+
+/*
+ * Block comment that describes the contents of this file.
+ */
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#include <cfext.h>
+
+extern struct cfextra **extlist;
+extern int eptnum;
+extern int warnflag;
+extern char *classname;
+
+extern int	cfentcmp(const void *, const void *);
+extern void	quit(int);
+extern void	usage(void);
+extern void	removef(int, char *[]);
+extern int	installf(int, char *[]);
+extern int	dofinal(VFP_T *, VFP_T *, int, char *, char *);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* _INSTALLF_H */
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/usr/src/cmd/svr4pkg/installf/main.c	Fri Jun 05 10:28:40 2009 -0400
@@ -0,0 +1,545 @@
+/*
+ * 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
+ * or http://www.opensolaris.org/os/licensing.
+ * 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 2009 Sun Microsystems, Inc.  All rights reserved.
+ * Use is subject to license terms.
+ */
+
+/* Copyright (c) 1984, 1986, 1987, 1988, 1989 AT&T */
+/* All Rights Reserved */
+
+
+#include <stdio.h>
+#include <fcntl.h>
+#include <ctype.h>
+#include <errno.h>
+#include <string.h>
+#include <signal.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <pkginfo.h>
+#include <pkgstrct.h>
+#include <pkglocs.h>
+#include <locale.h>
+#include <libintl.h>
+#include <instzones_api.h>
+#include <pkglib.h>
+#include <install.h>
+#include <libadm.h>
+#include <libinst.h>
+#include "installf.h"
+
+#define	BASEDIR	"/BASEDIR/"
+
+#define	INSTALF	(*prog == 'i')
+#define	REMOVEF	(*prog == 'r')
+
+#define	MSG_MANMOUNT	"Assuming mounts were provided."
+
+#define	ERR_PKGNAME_TOO_LONG	\
+"The package name specified on the command line\n" \
+"exceeds the maximum package name length: a package name may contain a\n" \
+"maximum of <%d> characters; however, the package name specified on\n" \
+"the command line contains <%d> characters, which exceeds the maximum\n" \
+"package name length by <%d> characters. Please specify a package name\n" \
+"that contains no more than <%d> characters."
+
+#define	ERR_DB_GET "unable to retrieve entries from the database."
+#define	ERR_DB_PUT "unable to update the package database."
+#define	ERR_ROOT_SET	"Could not set install root from the environment."
+#define	ERR_ROOT_CMD	"Command line install root contends with environment."
+#define	ERR_CLASSLONG	"classname argument too long"
+#define	ERR_CLASSCHAR	"bad character in classname"
+#define	ERR_INVAL	"package instance <%s> is invalid"
+#define	ERR_NOTINST	"package instance <%s> is not installed"
+#define	ERR_MERG	"unable to merge contents file"
+#define	ERR_SORT	"unable to sort contents file"
+#define	ERR_I_FAIL	"installf did not complete successfully"
+#define	ERR_R_FAIL	"removef did not complete successfully"
+#define	ERR_NOTROOT	"You must be \"root\" for %s to execute properly."
+#define	ERR_USAGE0	"usage:\n" \
+	"\t%s [[-M|-A] -R host_path] [-V ...] pkginst path " \
+	"[path ...]\n" \
+	"\t%s [[-M|-A] -R host_path] [-V ...] pkginst path\n"
+
+#define	ERR_USAGE1	"usage:\n" \
+	"\t%s [[-M] -R host_path] [-V ...] [-c class] <pkginst> " \
+	"<path>\n" \
+	"\t%s [[-M] -R host_path] [-V ...] [-c class] <pkginst> " \
+	"<path> <specs>\n" \
+	"\t   where <specs> may be defined as:\n" \
+	"\t\tf <mode> <owner> <group>\n" \
+	"\t\tv <mode> <owner> <group>\n" \
+	"\t\te <mode> <owner> <group>\n" \
+	"\t\td <mode> <owner> <group>\n" \
+	"\t\tx <mode> <owner> <group>\n" \
+	"\t\tp <mode> <owner> <group>\n" \
+	"\t\tc <major> <minor> <mode> <owner> <group>\n" \
+	"\t\tb <major> <minor> <mode> <owner> <group>\n" \
+	"\t\ts <path>=<srcpath>\n" \
+	"\t\tl <path>=<srcpath>\n" \
+	"\t%s [[-M] -R host_path] [-V ...] [-c class] -f pkginst\n"
+
+#define	CMD_SORT	"sort +0 -1"
+
+#define	LINK	1
+
+extern char	dbst; 			/* libinst/pkgdbmerg.c */
+
+struct cfextra **extlist;
+struct pinfo **eptlist;
+
+char	*classname = NULL;
+char	*pkginst;
+char	*uniTmp;
+char 	*abi_sym_ptr;
+char 	*ulim;
+char 	*script;
+
+int	eptnum;
+int	sortflag;
+int	nosetuid;
+int	nocnflct;
+int	warnflag = 0;
+
+/* libadm/pkgparam.c */
+extern void	set_PKGADM(char *newpath);
+extern void	set_PKGLOC(char *newpath);
+
+extern void set_limit(void);
+
+int
+main(int argc, char **argv)
+{
+	FILE		*pp;
+	VFP_T		*cfTmpVfp;
+	VFP_T		*cfVfp;
+	char		*cmd;
+	char		*tp;
+	char		*prog;
+	char		*pt;
+	char		*vfstab_file = NULL;
+	char		line[1024];
+	char		outbuf[PATH_MAX];
+	int		c;
+	int		dbchg;
+	int		err;
+	int		fflag = 0;
+	int		map_client = 1;
+	int		n;
+	int		pkgrmremote = 0;	/* don't remove remote files */
+	struct cfent	*ept;
+
+	/* hookup signals */
+
+	(void) signal(SIGHUP, exit);
+	(void) signal(SIGINT, exit);
+	(void) signal(SIGQUIT, exit);
+
+	/* initialize locale mechanism */
+
+	(void) setlocale(LC_ALL, "");
+
+#if	!defined(TEXT_DOMAIN)	/* Should be defined by cc -D */
+#define	TEXT_DOMAIN "SYS_TEST"
+#endif	/* !defined(TEXT_DOMAIN) */
+
+	(void) textdomain(TEXT_DOMAIN);
+
+	/* determine program name */
+
+	prog = set_prog_name(argv[0]);
+
+	/* tell instzones interface how to access package output functions */
+
+	z_set_output_functions(echo, echoDebug, progerr);
+
+	/* only allow root to run this program */
+
+	if (getuid() != 0) {
+		progerr(gettext(ERR_NOTROOT), prog);
+		exit(1);
+	}
+
+	ulim = getenv("PKG_ULIMIT");
+	script = getenv("PKG_PROC_SCRIPT");
+
+	if (ulim && script) {
+		set_limit();
+		clr_ulimit();
+	}
+
+	/* bug id 4244631, not ABI compliant */
+	abi_sym_ptr = getenv("PKG_NONABI_SYMLINKS");
+	if (abi_sym_ptr && strncasecmp(abi_sym_ptr, "TRUE", 4) == 0)
+		set_nonABI_symlinks();
+
+	/* bugId 4012147 */
+	if ((uniTmp = getenv("PKG_NO_UNIFIED")) != NULL)
+		map_client = 0;
+	if (!set_inst_root(getenv("PKG_INSTALL_ROOT"))) {
+		progerr(gettext(ERR_ROOT_SET));
+		exit(1);
+	}
+
+	while ((c = getopt(argc, argv, "c:V:fAMR:?")) != EOF) {
+		switch (c) {
+			case 'f':
+			fflag++;
+			break;
+
+			case 'c':
+			classname = optarg;
+			/* validate that classname is acceptable */
+			if (strlen(classname) > (size_t)CLSSIZ) {
+				progerr(gettext(ERR_CLASSLONG));
+				exit(1);
+			}
+			for (pt = classname; *pt; pt++) {
+				if (!isalpha(*pt) && !isdigit(*pt)) {
+					progerr(gettext(ERR_CLASSCHAR));
+					exit(1);
+				}
+			}
+			break;
+
+		/*
+		 * Don't map the client filesystem onto the server's. Assume
+		 * the mounts have been made for us.
+		 */
+			case 'M':
+			map_client = 0;
+			break;
+
+		/*
+		 * Allow admin to establish the client filesystem using a
+		 * vfstab-like file of stable format.
+		 */
+			case 'V':
+			vfstab_file = flex_device(optarg, 2);
+			map_client = 1;
+			break;
+
+			case 'A':
+			pkgrmremote++;
+			break;
+
+			case 'R':	/* added for newroot option */
+			if (!set_inst_root(optarg)) {
+				progerr(gettext(ERR_ROOT_CMD));
+				exit(1);
+			}
+			break;
+
+			default:
+			usage();
+			/*NOTREACHED*/
+			/*
+			 * Although usage() calls a noreturn function,
+			 * needed to add return (1);  so that main() would
+			 * pass compilation checks. The statement below
+			 * should never be executed.
+			 */
+			return (1);
+		}
+	}
+
+	if (pkgrmremote && (!is_an_inst_root() || fflag || INSTALF)) {
+		usage();
+		/*NOTREACHED*/
+	}
+
+	/*
+	 * Get the mount table info and store internally.
+	 */
+	if (get_mntinfo(map_client, vfstab_file))
+		exit(1);
+
+	/*
+	 * This function defines the standard /var/... directories used later
+	 * to construct the paths to the various databases.
+	 */
+	(void) set_PKGpaths(get_inst_root());
+
+	/*
+	 * If this is being installed on a client whose /var filesystem is
+	 * mounted in some odd way, remap the administrative paths to the
+	 * real filesystem. This could be avoided by simply mounting up the
+	 * client now; but we aren't yet to the point in the process where
+	 * modification of the filesystem is permitted.
+	 */
+	if (is_an_inst_root()) {
+		int fsys_value;
+
+		fsys_value = fsys(get_PKGLOC());
+		if (use_srvr_map_n(fsys_value))
+			set_PKGLOC(server_map(get_PKGLOC(), fsys_value));
+
+		fsys_value = fsys(get_PKGADM());
+		if (use_srvr_map_n(fsys_value))
+			set_PKGADM(server_map(get_PKGADM(), fsys_value));
+	}
+
+	sortflag = 0;
+
+	/*
+	 * get the package name and verify length is not too long
+	 */
+
+	pkginst = argv[optind++];
+	if (pkginst == NULL) {
+		usage();
+		/*NOTREACHED*/
+
+	}
+
+	n = strlen(pkginst);
+	if (n > PKGSIZ) {
+		progerr(gettext(ERR_PKGNAME_TOO_LONG), PKGSIZ, n, n-PKGSIZ,
+		    PKGSIZ);
+		usage();
+		/*NOTREACHED*/
+	}
+
+	/*
+	 * The following is used to setup the environment. Note that the
+	 * variable 'BASEDIR' is only meaningful for this utility if there
+	 * is an install root, recorded in PKG_INSTALL_ROOT. Otherwise, this
+	 * utility can create a file or directory anywhere unfettered by
+	 * the basedir associated with the package instance.
+	 */
+	if ((err = set_basedirs(0, NULL, pkginst, 1)) != 0)
+		exit(err);
+
+	if (INSTALF)
+		mkbasedir(0, get_basedir());
+
+	if (fflag) {
+		/* installf and removef must only have pkginst */
+		if (optind != argc) {
+			usage();
+			/*NOTREACHED*/
+		}
+	} else {
+		/*
+		 * installf and removef must have at minimum
+		 * pkginst & pathname specified on command line
+		 */
+		if (optind >= argc) {
+			usage();
+			/*NOTREACHED*/
+		}
+	}
+	if (REMOVEF) {
+		if (classname) {
+			usage();
+		}
+	}
+	if (pkgnmchk(pkginst, "all", 0)) {
+		progerr(gettext(ERR_INVAL), pkginst);
+		exit(1);
+	}
+	if (fpkginst(pkginst, NULL, NULL) == NULL) {
+		progerr(gettext(ERR_NOTINST), pkginst);
+		exit(1);
+	}
+
+#ifdef	ALLOW_EXCEPTION_PKG_LIST
+	/*
+	 * *********************************************************************
+	 * this feature is removed starting with Solaris 10 - there is no built
+	 * in list of packages that should be run "the old way"
+	 * *********************************************************************
+	 */
+	/* Until 2.9, set it from the execption list */
+	if (pkginst && exception_pkg(pkginst, LINK))
+		set_nonABI_symlinks();
+#endif
+	/*
+	 * This maps the client filesystems into the server's space.
+	 */
+	if (map_client && !mount_client())
+		logerr(gettext(MSG_MANMOUNT));
+
+	/* open the package database (contents) file */
+
+	if (!ocfile(&cfVfp, &cfTmpVfp, 0L)) {
+		quit(1);
+	}
+
+	if (fflag) {
+		dbchg = dofinal(cfVfp, cfTmpVfp, REMOVEF, classname, prog);
+	} else {
+		if (INSTALF) {
+			dbst = INST_RDY;
+			if (installf(argc-optind, &argv[optind]))
+				quit(1);
+		} else {
+			dbst = RM_RDY;
+			removef(argc-optind, &argv[optind]);
+		}
+
+		dbchg = pkgdbmerg(cfVfp, cfTmpVfp, extlist, 0);
+		if (dbchg < 0) {
+			progerr(gettext(ERR_MERG));
+			quit(99);
+		}
+	}
+
+	if (dbchg) {
+		if ((n = swapcfile(&cfVfp, &cfTmpVfp, pkginst, 1))
+			== RESULT_WRN) {
+		    warnflag++;
+		} else if (n == RESULT_ERR) {
+		    quit(99);
+		}
+	}
+
+	relslock();
+
+	if (REMOVEF && !fflag) {
+		for (n = 0; extlist[n]; n++) {
+			ept = &(extlist[n]->cf_ent);
+
+			/* Skip duplicated paths */
+			if ((n > 0) && (strncmp(ept->path,
+			    extlist[n-1]->cf_ent.path, PATH_MAX) == 0)) {
+				continue;
+			}
+
+			if (!extlist[n]->mstat.shared) {
+				/*
+				 * Only output paths that can be deleted.
+				 * so need to skip if the object is owned
+				 * by a remote server and removal is not
+				 * being forced.
+				 */
+				if (ept->pinfo &&
+				    (ept->pinfo->status == SERVED_FILE) &&
+				    !pkgrmremote)
+					continue;
+
+				c = 0;
+				if (is_a_cl_basedir() && !is_an_inst_root()) {
+					c = strlen(get_client_basedir());
+					(void) snprintf(outbuf, sizeof (outbuf),
+						"%s/%s\n", get_basedir(),
+						&(ept->path[c]));
+				} else if (is_an_inst_root()) {
+					(void) snprintf(outbuf, sizeof (outbuf),
+						"%s/%s\n", get_inst_root(),
+						&(ept->path[c]));
+				} else {
+					(void) snprintf(outbuf, sizeof (outbuf),
+						"%s\n", &(ept->path[c]));
+				}
+				canonize(outbuf);
+				(void) printf("%s", outbuf);
+			}
+		}
+	} else if (INSTALF && !fflag) {
+		for (n = 0; extlist[n]; n++) {
+			ept = &(extlist[n]->cf_ent);
+
+			if (strchr("dxcbp", ept->ftype)) {
+				tp = fixpath(ept->path);
+				(void) averify(1, &ept->ftype,
+					tp, &ept->ainfo);
+			}
+		}
+	}
+
+	/* Sort the contents files if needed */
+	if (sortflag) {
+		int n;
+
+		warnflag += (ocfile(&cfVfp, &cfTmpVfp, 0L)) ? 0 : 1;
+		if (!warnflag) {
+			size_t	len;
+
+			len = strlen(CMD_SORT) + strlen(get_PKGADM()) +
+				strlen("/contents") + 5;
+			cmd = (char *)malloc(len);
+			(void) snprintf(cmd, len, "%s %s/contents",
+				CMD_SORT, get_PKGADM());
+			pp = popen(cmd, "r");
+			if (pp == NULL) {
+				(void) vfpClose(&cfVfp);
+				(void) vfpClose(&cfTmpVfp);
+				free(cmd);
+				progerr(gettext(ERR_SORT));
+				quit(1);
+			}
+			while (fgets(line, 1024, pp) != NULL) {
+				if (line[0] != DUP_ENTRY) {
+					vfpPuts(cfTmpVfp, line);
+				}
+			}
+			free(cmd);
+			(void) pclose(pp);
+			n = swapcfile(&cfVfp, &cfTmpVfp, pkginst, 1);
+			if (n == RESULT_WRN) {
+				warnflag++;
+			} else if (n == RESULT_ERR) {
+				quit(99);
+			}
+
+			relslock();	/* Unlock the database. */
+		}
+	}
+
+	z_destroyMountTable();
+
+	quit(warnflag ? 1 : 0);
+	/* LINTED: no return */
+}
+
+void
+quit(int n)
+{
+	char *prog = get_prog_name();
+
+	unmount_client();
+
+	if (ulim && script) {
+		if (REMOVEF) {
+			set_ulimit(script, gettext(ERR_R_FAIL));
+		} else {
+			set_ulimit(script, gettext(ERR_I_FAIL));
+		}
+	}
+
+	exit(n);
+}
+
+void
+usage(void)
+{
+	char *prog = get_prog_name();
+
+	if (REMOVEF) {
+		(void) fprintf(stderr, gettext(ERR_USAGE0), prog, prog);
+	} else {
+		(void) fprintf(stderr, gettext(ERR_USAGE1), prog, prog, prog);
+	}
+	exit(1);
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/usr/src/cmd/svr4pkg/installf/removef.c	Fri Jun 05 10:28:40 2009 -0400
@@ -0,0 +1,127 @@
+/*
+ * 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
+ * or http://www.opensolaris.org/os/licensing.
+ * 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.
+ */
+
+/* Copyright (c) 1984, 1986, 1987, 1988, 1989 AT&T */
+/* All Rights Reserved */
+
+#include <stdio.h>
+#include <errno.h>
+#include <string.h>
+#include <limits.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <sys/types.h>
+#include <locale.h>
+#include <libintl.h>
+#include <pkglib.h>
+#include <install.h>
+#include <libinst.h>
+#include <libadm.h>
+#include "installf.h"
+
+void
+removef(int argc, char *argv[])
+{
+	struct cfextra *new;
+	char	buf[PATH_MAX];
+	char	*path;
+	int	flag;
+	int	len;
+	int	max_eptnum;
+
+	flag = strcmp(argv[0], "-") == 0;
+
+	eptnum = 0;
+	max_eptnum = 64;	/* starting size of array */
+	extlist = malloc(max_eptnum * sizeof (struct cfextra *));
+
+	for (;;) {
+		if (flag) {
+			if (fgets(buf, PATH_MAX, stdin) == NULL)
+				break;
+
+			/* strip trailing new line */
+			len = strlen(buf);
+			if (buf[len - 1] == '\n')
+				buf[len - 1] = '\0';
+
+			path = buf;
+		} else {
+			if (argc-- <= 0)
+				break;
+			path = argv[argc];
+		}
+
+		/*
+		 * This strips the install root from the path using
+		 * a questionable algorithm. This should go away as
+		 * we define more precisely the command line syntax
+		 * with our '-R' option. - JST
+		 */
+		path = orig_path_ptr(path);
+
+		if (path == NULL) {
+			logerr(gettext("ERROR: no pathname was provided"));
+			warnflag++;
+			continue;
+		}
+
+		if (*path != '/') {
+			logerr(gettext(
+			    "WARNING: relative pathname <%s> ignored"), path);
+			warnflag++;
+			continue;
+		}
+
+		new = calloc(1, sizeof (struct cfextra));
+		if (new == NULL) {
+			progerr(strerror(errno));
+			quit(99);
+		}
+		new->cf_ent.ftype = '-';
+
+		(void) eval_path(&(new->server_path), &(new->client_path),
+		    &(new->map_path), path);
+
+		new->cf_ent.path = new->client_path;
+
+		extlist[eptnum++] = new;
+		if (eptnum >= max_eptnum) {
+			/* array size grows exponentially */
+			max_eptnum <<= 1;
+			extlist = realloc(extlist,
+			    max_eptnum * sizeof (struct cfextra *));
+			if (extlist == NULL) {
+				progerr(strerror(errno));
+				quit(99);
+			}
+		}
+	}
+	extlist[eptnum] = (struct cfextra *)NULL;
+
+	qsort((char *)extlist,
+	    (unsigned)eptnum, sizeof (struct cfextra *), cfentcmp);
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/usr/src/cmd/svr4pkg/libinst/Makefile	Fri Jun 05 10:28:40 2009 -0400
@@ -0,0 +1,81 @@
+#
+# 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
+# or http://www.opensolaris.org/os/licensing.
+# 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 2009 Sun Microsystems, Inc.  All rights reserved.
+# Use is subject to license terms.
+#
+
+PROG= libinst.a
+
+OBJS=	copyf.o         dockdeps.o      echo.o          eptstat.o       \
+	finalck.o       findscripts.o   fixpath.o       flex_dev.o      \
+	isreloc.o       lockinst.o      mntinfo.o       nblk.o          \
+	ocfile.o        pathdup.o       pkgdbmerg.o     procmap.o       \
+	pkgobjmap.o     psvr4ck.o       ptext.o         putparam.o      \
+	qreason.o       qstrdup.o       setadmin.o      setlist.o       \
+	srcpath.o       scriptvfy.o     stub.o          doulimit.o      \
+	dryrun.o        listmgr.o       is_local_host.o cvtpath.o       \
+	depchk.o        pkgops.o        sml.o           log.o           \
+	setup_temporary_directory.o     open_package_datastream.o       \
+	unpack_package_from_stream.o
+SRCS = $(OBJS:.o=.c)
+
+include	$(SRC)/cmd/Makefile.cmd
+
+#
+# For messaging catalog
+POFILE = libinst.po
+MSGFILES=$(OBJS:%.o=%.i) 
+
+CPPFLAGS +=	-I$(SRC)/cmd/svr4pkg/hdrs \
+		-I$(SRC)/lib/libpkg/common \
+		-I$(SRC)/lib/libinstzones/common \
+		-D_FILE_OFFSET_BITS=64
+
+# Lint flags 
+#
+LINTFLAGS += -un
+
+.KEEP_STATE:
+
+all: $(PROG)
+
+$(PROG): $(OBJS)
+	$(RM) $@
+	$(AR) $(ARFLAGS) $@ $(OBJS)
+	$(POST_PROCESS_A)
+
+install: all
+	@echo "$(PROG) is a static library and will not be installed."
+
+$(POFILE): $(MSGFILES)
+	$(BUILDPO.msgfiles)
+
+_msg: $(MSGDOMAINPOFILE)
+
+clean:
+	$(RM) $(OBJS) $(MSGFILES)
+
+clobber: clean
+	$(RM) $(PROG) $(POFILE)
+
+include	$(SRC)/Makefile.msg.targ
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/usr/src/cmd/svr4pkg/libinst/copyf.c	Fri Jun 05 10:28:40 2009 -0400
@@ -0,0 +1,512 @@
+/*
+ * 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
+ * or http://www.opensolaris.org/os/licensing.
+ * 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 2005 Sun Microsystems, Inc.  All rights reserved.
+ * Use is subject to license terms.
+ */
+
+/* Copyright (c) 1984, 1986, 1987, 1988, 1989 AT&T */
+/* All Rights Reserved */
+
+
+
+#include <stdio.h>
+#include <string.h>
+#include <errno.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <libgen.h>
+#include <fcntl.h>
+#include <sys/types.h>
+#include <utime.h>
+#include <sys/stat.h>
+#include <locale.h>
+#include <libintl.h>
+#include <sys/mman.h>
+
+/*
+ * consolidation pkg command library includes
+ */
+
+#include "pkglib.h"
+
+/*
+ * local pkg command library includes
+ */
+#include "libinst.h"
+#include "libadm.h"
+#include "messages.h"
+
+/*
+ * MAXMAPSIZE controls the largest mapping to use at a time; please refer
+ * to mmap(2) for details of how this size is incremented and rounded; briefly
+ * each mapping request has an additional 16Kb added to it - mappings over
+ * 4Mb will be rounded to a 4Mb boundary - thus if there were 8mb, adding
+ * in the 16Kb overhead the mapping would use another 4Mb-16kb - that is
+ * why there is 16Kb subtracted from the total
+ */
+
+#define	MAXMAPSIZE	(1024*1024*8)-(1024*16)	/* map at most 8MB */
+#define	SMALLFILESIZE	(32*1024)	/* dont mmap files less than 32kb */
+
+/*
+ * Name:	copyF
+ * Description:	fast copy of file - use mmap()/write() loop if possible
+ * Arguments:	char *srcPath - name of source file to copy from
+ *		char *dstPath - name of target file to copy to
+ *		time_t a_mytime: control setting of access/modification times:
+ *			== 0 - replicate source file access/modification times
+ *			!= 0 - use specified time for access/modification times
+ * Returns:	int
+ *		== 0 - successful
+ *		!= 0 - failure
+ */
+
+int
+copyf(char *a_srcPath, char *a_dstPath, time_t a_mytime)
+{
+	struct stat	srcStatbuf;
+	struct utimbuf	times;
+	int		srcFd;
+	int		dstFd;
+	int		status;
+	char		*pt;
+
+	/* open source file for reading */
+
+	srcFd = open(a_srcPath, O_RDONLY, 0);
+	if (srcFd < 0) {
+		progerr(ERR_OPEN_READ, a_srcPath, errno, strerror(errno));
+		return (-1);
+	}
+
+	/* obtain file status of source file */
+
+	if (fstat(srcFd, &srcStatbuf) != 0) {
+		progerr(ERR_FSTAT, srcFd, a_srcPath, errno, strerror(errno));
+		(void) close(srcFd);
+		return (-1);
+	}
+
+	/* open target file for writing */
+
+	dstFd = open(a_dstPath, O_WRONLY | O_TRUNC | O_CREAT,
+		srcStatbuf.st_mode);
+	if (dstFd < 0) {
+		/* create directory structure if missing */
+		pt = a_dstPath;
+		while (pt = strchr(pt+1, '/')) {
+			*pt = '\0';
+			if (isdir(a_dstPath)) {
+				if (mkdir(a_dstPath, 0755)) {
+					progerr(ERR_NODIR, a_dstPath,
+						errno, strerror(errno));
+					*pt = '/';
+					(void) close(srcFd);
+					return (-1);
+				}
+			}
+			*pt = '/';
+		}
+
+		/* attempt to create target file again */
+		dstFd = open(a_dstPath, O_WRONLY | O_TRUNC | O_CREAT,
+				srcStatbuf.st_mode);
+		if (dstFd < 0) {
+			progerr(ERR_OPEN_WRITE, a_dstPath, errno,
+							strerror(errno));
+			(void) close(srcFd);
+			return (-1);
+		}
+	}
+
+	/*
+	 * source and target files are open: copy data
+	 */
+
+	status = copyFile(srcFd, dstFd, a_srcPath, a_dstPath, &srcStatbuf, 0);
+
+	(void) close(srcFd);
+	(void) close(dstFd);
+
+	/*
+	 * determine how to set access/modification times for target:
+	 * -- a_mytime == 0: replicate source file access/modification times
+	 * -- otherwise: use a_mytime for file access/modification times
+	 */
+
+	if (a_mytime == 0) {
+		times.actime = srcStatbuf.st_atime;
+		times.modtime = srcStatbuf.st_mtime;
+	} else {
+		times.actime = a_mytime;
+		times.modtime = a_mytime;
+	}
+
+	/* set access/modification times for target */
+
+	if (utime(a_dstPath, &times) != 0) {
+		progerr(ERR_MODTIM, a_dstPath, errno, strerror(errno));
+		return (-1);
+	}
+
+	/* return error if copy failed */
+
+	if (status != 0) {
+		progerr(ERR_READ, a_srcPath, errno, strerror(errno));
+		return (-1);
+	}
+
+	/* success! */
+
+	return (0);
+}
+
+/*
+ * Name:	copyFile
+ * Description:	fast copy of file - use mmap()/write() loop if possible
+ * Arguments:	int srcFd - file descriptor open on source file
+ *		int dstFd - file descriptor open on target file
+ *		char *srcPath - name of source file (for error messages)
+ *		char *dstPath - name of target file (for error messages)
+ *		struct stat *a_srcStatbuf - stat structure for source file
+ *		long a_iosize - preferred io size for read/write loop
+ * Returns:	int
+ *		== 0 - successful
+ *		!= 0 - failure
+ */
+
+int
+copyFile(int a_srcFd, int a_dstFd, char *a_srcPath, char *a_dstPath,
+	struct stat *a_srcStatbuf, long a_iosize)
+{
+	caddr_t	cp;
+	off_t	filesize = a_srcStatbuf->st_size;
+	size_t	mapsize = 0;
+	size_t	munmapsize = 0;
+	off_t	offset = 0;
+
+	echoDebug(DBG_COPY_FILE, a_srcPath, a_dstPath);
+
+	/*
+	 * if the source is a regular file and is not "too small", then cause
+	 * the file to be mapped into memory
+	 */
+
+	if (S_ISREG(a_srcStatbuf->st_mode) && (filesize > SMALLFILESIZE)) {
+		/*
+		 * Determine size of initial mapping.  This will determine the
+		 * size of the address space chunk we work with.  This initial
+		 * mapping size will be used to perform munmap() in the future.
+		 */
+
+		mapsize = MAXMAPSIZE;
+		if (filesize < mapsize) {
+			mapsize = filesize;
+		}
+
+		/*
+		 * remember size of mapping to "unmap" - if the source file
+		 * exceeds MAXMAPSIZE bytes, then the final mapping of the
+		 * source file will be less than MAXMAPSIZE, and we need to
+		 * make sure that the entire mapping is unmapped when done.
+		 */
+
+		munmapsize = mapsize;
+
+		/* map the first segment of the source into memory */
+
+		cp = mmap((caddr_t)NULL, mapsize, PROT_READ,
+			(MAP_SHARED|MAP_ALIGN), a_srcFd, (off_t)0);
+		if (cp == MAP_FAILED) {
+			mapsize = 0;   /* can't mmap today */
+		}
+	}
+
+	/*
+	 * if the source was not mapped into memory, copy via read/write loop
+	 */
+
+	if (mapsize == 0) {
+		char	*buf = (char *)NULL;
+		size_t	blocksize;
+		int	pagesize = getpagesize();
+
+		/* set blocksize for copy */
+
+		blocksize = a_iosize;
+		if ((blocksize == 0) || (blocksize > SMALLFILESIZE)) {
+			blocksize = SMALLFILESIZE;
+		} else if (blocksize < pagesize) {
+			blocksize = pagesize;
+		}
+
+		/* allocate i/o transfer buffer */
+
+		buf = memalign((size_t)pagesize, blocksize);
+		if (buf == (char *)NULL) {
+			progerr(ERR_COPY_MEMORY, a_srcPath, errno,
+				strerror(errno));
+			return (1);
+		}
+
+		/* copy the file contents */
+
+		for (;;) {
+			ssize_t	n;
+
+			/* read next block of data */
+
+			n = read(a_srcFd, buf, blocksize);
+			if (n == 0) {
+				/* end of file - return success */
+				(void) free(buf);
+				return (0);
+			} else if (n < 0) {
+				/* read error - return error */
+				progerr(ERR_READ, a_srcPath,
+						errno, strerror(errno));
+				(void) free(buf);
+				return (1);
+			}
+
+			/* write out block of data just read in */
+
+			if (vfpSafeWrite(a_dstFd, buf, (size_t)n) != n) {
+				/* short write/write error - return error */
+				progerr(ERR_WRITE, a_dstPath,
+						errno, strerror(errno));
+				(void) free(buf);
+				return (1);
+			}
+		}
+	}
+
+	/*
+	 * the source has been mapped into memory, copy via mappings
+	 */
+
+	for (;;) {
+		ssize_t	nbytes;
+
+		/* write first mappings worth of data */
+
+		nbytes = write(a_dstFd, cp, mapsize);
+
+		/*
+		 * if we write less than the mmaped size it's due to a
+		 * media error on the input file or out of space on
+		 * the output file.  So, try again, and look for errno.
+		 */
+
+		if ((nbytes >= 0) && (nbytes != (ssize_t)mapsize)) {
+			size_t	remains;
+
+			remains = mapsize - nbytes;
+			while (remains > 0) {
+				nbytes = write(a_dstFd,
+					(cp + mapsize - remains), remains);
+				if (nbytes >= 0) {
+					remains -= nbytes;
+					if (remains == 0) {
+						nbytes = mapsize;
+					}
+					continue;
+				}
+
+				/* i/o error - report and exit */
+
+				if (errno == ENOSPC) {
+					progerr(ERR_WRITE, a_dstPath,
+						errno, strerror(errno));
+				} else {
+					progerr(ERR_READ, a_srcPath,
+						errno, strerror(errno));
+				}
+
+				/* unmap source file mapping */
+				(void) munmap(cp, munmapsize);
+				return (1);
+			}
+		}
+
+		/*
+		 * although the write manual page doesn't specify this
+		 * as a possible errno, it is set when the nfs read
+		 * via the mmap'ed file is accessed, so report the
+		 * problem as a source access problem, not a target file
+		 * problem
+		 */
+
+		if (nbytes < 0) {
+			if (errno == EACCES) {
+				progerr(ERR_READ, a_srcPath,
+					errno, strerror(errno));
+			} else {
+				progerr(ERR_WRITE, a_dstPath,
+					errno, strerror(errno));
+			}
+
+			/* unmap source file mapping */
+			(void) munmap(cp, munmapsize);
+			return (1);
+		}
+
+		filesize -= nbytes;
+		if (filesize == 0) {
+			break;
+		}
+
+		offset += nbytes;
+		if (filesize < mapsize) {
+			mapsize = filesize;
+		}
+
+		/* map next segment of file on top of existing mapping */
+
+		cp = mmap(cp, mapsize, PROT_READ, (MAP_SHARED|MAP_FIXED),
+			a_srcFd, offset);
+
+		if (cp == MAP_FAILED) {
+			progerr(ERR_MAPFAILED, a_srcPath, errno,
+						strerror(errno));
+			/* unmap source file mapping */
+			(void) munmap(cp, munmapsize);
+			return (1);
+		}
+	}
+
+	/* unmap source file mapping */
+
+	(void) munmap(cp, munmapsize);
+
+	return (0);
+}
+
+/*
+ * Name:	openLocal
+ * Description:	open a file and assure that the descriptor returned is open on
+ *		a file that is local to the current system - if the file is not
+ *		local to this system, copy the file to a temporary file first,
+ *		and then pass a handle back opened on the temporary file
+ * Arguments:	a_path - [RO, *RO] - (char *)
+ *			Pointer to string representing the path to the file
+ *			to open
+ *		a_oflag - [RO, *RO] - (int)
+ *			Integer representing the "mode" bits for an open(2) call
+ *		a_tmpdir - [RO, *RO] - (char *)
+ *			Pointer to string representing the path to a directory
+ *			where a temporary copy of the file can be placed if
+ *			the source file is not local to this system. If this is
+ *			NULL or does not exist, P_tmpdir is used.
+ * Returns:	int
+ *			>= 0 - file descriptor opened on the file
+ *			== -1 - failed to open - errno contains error code
+ * NOTE:	If the file is not local and is copied locally, the file is
+ *		setup in such a way that it will be removed when the last
+ *		file descriptor opened on the file is closed - there is no need
+ *		to know the path to the temporary file or to remove it
+ *		when done.
+ */
+
+int
+openLocal(char *a_path, int a_oflag, char *a_tmpdir)
+{
+	char		*bn;
+	char		template[PATH_MAX];
+	int		fd;
+	int		lerrno;
+	int		n;
+	int		tmpFd;
+	struct stat	statbuf;
+
+	/* open source file */
+
+	fd = open(a_path, a_oflag);
+	if (fd < 0) {
+		return (fd);
+	}
+
+	/* return open fd if the source file is not remote */
+
+	if (!isFdRemote(fd)) {
+		return (fd);
+	}
+
+	/*
+	 * source file is remote - must make a local copy
+	 */
+
+	/* get the source file's status */
+
+	n = fstat(fd, &statbuf);
+	if (n < 0) {
+		lerrno = errno;
+		(void) close(fd);
+		errno = lerrno;
+		return (-1);
+	}
+
+	/* generate unique temporary file name */
+
+	if ((a_tmpdir == (char *)NULL) || (*a_tmpdir == '\0') ||
+		(isdir(a_tmpdir) != 0)) {
+		a_tmpdir = P_tmpdir;
+	}
+	bn = basename(a_path);
+	n = strlen(a_tmpdir);
+	n = snprintf(template, sizeof (template), "%s%s%sXXXXXX",
+		a_tmpdir, a_tmpdir[n-1] == '/' ? "" : "/", bn);
+	if (n > sizeof (template)) {
+		(void) close(fd);
+		return (EINVAL);
+	}
+
+	/* create the temporary file and open it */
+
+	tmpFd = mkstemp(template);
+	if (tmpFd < 0) {
+		lerrno = errno;
+		(void) close(fd);
+		errno = lerrno;
+		return (tmpFd);
+	}
+
+	/* unlink the file so when it is closed it is automatically deleted */
+
+	(void) unlink(template);
+
+	/* copy the source file to the temporary file */
+
+	n = copyFile(fd, tmpFd, a_path, template, &statbuf, 0L);
+	lerrno = errno;
+	(void) close(fd);
+	if (n != 0) {
+		(void) close(tmpFd);
+		errno = lerrno;
+		return (-1);
+	}
+
+	/* return handle to temporary file created */
+
+	return (tmpFd);
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/usr/src/cmd/svr4pkg/libinst/cvtpath.c	Fri Jun 05 10:28:40 2009 -0400
@@ -0,0 +1,55 @@
+/*
+ * 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
+ * or http://www.opensolaris.org/os/licensing.
+ * 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 1993 Sun Microsystems, Inc.  All rights reserved.
+ * Use is subject to license terms.
+ */
+
+
+
+#include <string.h>
+
+extern char *root, *basedir; 		/* WHERE? */
+
+void
+cvtpath(char *path, char *copy)
+{
+	*copy++ = '/';
+	if (root || (basedir && (*path != '/'))) {
+		if (root && ((basedir == NULL) || (path[0] == '/') ||
+		    (basedir[0] != '/'))) {
+			/* look in root */
+			(void) strcpy(copy, root + (*root == '/' ? 1 : 0));
+			copy += strlen(copy);
+			if (copy[-1] != '/')
+				*copy++ = '/';
+		}
+		if (basedir && (*path != '/')) {
+			(void) strcpy(copy,
+			    basedir + (*basedir == '/' ? 1 : 0));
+			copy += strlen(copy);
+			if (copy[-1] != '/')
+				*copy++ = '/';
+		}
+	}
+	(void) strcpy(copy, path + (*path == '/' ? 1 : 0));
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/usr/src/cmd/svr4pkg/libinst/depchk.c	Fri Jun 05 10:28:40 2009 -0400
@@ -0,0 +1,349 @@
+/*
+ * 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
+ * or http://www.opensolaris.org/os/licensing.
+ * 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 2004 Sun Microsystems, Inc.  All rights reserved.
+ * Use is subject to license terms.
+ */
+
+
+/*
+ * System includes
+ */
+
+#include <stdio.h>
+#include <limits.h>
+#include <string.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <errno.h>
+#include <sys/types.h>
+#include <locale.h>
+#include <libintl.h>
+#include <assert.h>
+
+/*
+ * local pkg command library includes
+ */
+
+#include "libinst.h"
+#include "messages.h"
+
+/*
+ * forward declarations
+ */
+
+static int
+collectError(int *r_numZones, char **r_zoneNames, char *a_packageName,
+	depckl_t *a_dck, int a_depIndex, depckErrorRecord_t *a_eir,
+	int a_errIndex);
+
+/*
+ * *****************************************************************************
+ * global external (public) functions
+ * *****************************************************************************
+ */
+
+int
+depchkReportErrors(depckl_t *a_dck)
+{
+	char	*packageName;
+	char	*zonenames;
+	char	msgbuf[4096];
+	int	err;
+	int	i;
+	int	numzones = 0;
+
+	/* entry assertions */
+
+	assert(a_dck != (depckl_t *)NULL);
+
+	/* entry debugging info */
+
+	echoDebug(DBG_DEPCHK_ENTRY);
+
+	zonenames = (char *)NULL;
+
+	/* go through dependency table, collect, collapse, report errors */
+
+	for (i = 0; a_dck[i].name != (char *)NULL; i++) {
+		int	j;
+		depckError_t	*erc;
+
+		if (zonenames != (char *)NULL) {
+			free(zonenames);
+			zonenames = (char *)NULL;
+		}
+
+		erc = a_dck[i].record;
+		if (erc->er_numEntries == 0) {
+			continue;
+		}
+
+		for (j = 0; j < erc->er_numEntries; j++) {
+			int	k;
+			depckErrorRecord_t *eir;
+
+			if (zonenames != (char *)NULL) {
+				free(zonenames);
+				zonenames = (char *)NULL;
+			}
+
+			eir = &erc->er_theEntries[j];
+			packageName = eir->ier_packageName;
+			for (k = 0; k < eir->ier_numZones; k++) {
+				int err;
+
+				err = collectError(&numzones, &zonenames,
+					packageName, a_dck, i, eir, k);
+				if (err != 0) {
+					if (zonenames != (char *)NULL) {
+						free(zonenames);
+						zonenames = (char *)NULL;
+					}
+					return (err);
+				}
+			}
+
+			if (a_dck[i].ignore_values == (char *)NULL) {
+				continue;
+			}
+
+			if (a_dck[i].err_msg == (char **)NULL) {
+				(void) snprintf(msgbuf, sizeof (msgbuf),
+					ERR_DEPENDENCY_IGNORED, a_dck[i].name,
+					packageName,
+					numzones == 1 ? "zone" : "zones",
+					zonenames ? zonenames : "?");
+			} else {
+				/* LINTED variable format specifier to ... */
+				(void) snprintf(msgbuf, sizeof (msgbuf),
+					*a_dck[i].err_msg, "package",
+					packageName,
+					numzones == 1 ? "zone" : "zones",
+					zonenames ? zonenames : "??");
+			}
+
+			if (a_dck[i].depcklFunc != NULL) {
+				/* call check function */
+				err = (a_dck[i].depcklFunc)(msgbuf,
+					packageName);
+				echoDebug(DBG_DEPCHK_REPORT_ERROR,
+					a_dck[i].ignore_values, err,
+					packageName, msgbuf);
+				if (err != 0) {
+					if (zonenames != (char *)NULL) {
+						free(zonenames);
+						zonenames = (char *)NULL;
+					}
+					return (err);
+				}
+			} else {
+				/* no check function - just report message */
+				echoDebug(DBG_DEPCHK_IGNORE_ERROR,
+					a_dck[i].ignore_values, packageName,
+					msgbuf);
+				ptext(stderr, "\\n%s", msgbuf);
+			}
+		}
+	}
+
+	if (zonenames != (char *)NULL) {
+		free(zonenames);
+		zonenames = (char *)NULL;
+	}
+
+	return (0);
+}
+
+void
+depchkRecordError(depckError_t *a_erc, char *a_pkginst,
+	char *a_zoneName, char *a_value)
+{
+	depckErrorRecord_t *erc;
+	int		i;
+
+	/*
+	 * create new error record and entry if first entry
+	 * record will look like this:
+	 * err->er_#entry=1
+	 * err->entry[0]->record->ier_numZones=1
+	 * err->entry[0]->record->ier_packageName=a_pkginst
+	 * err->entry[0]->record->ier_zones[0]=a_zoneName
+	 * err->entry[0]->record->ier_values[0]=a_value
+	 */
+
+	if (a_erc->er_numEntries == 0) {
+		depckErrorRecord_t	*eir;
+
+		eir = (depckErrorRecord_t *)calloc(1,
+					sizeof (depckErrorRecord_t));
+		eir->ier_packageName = strdup(a_pkginst);
+		eir->ier_numZones = 1;
+		eir->ier_zones = (char **)calloc(1, sizeof (char **));
+		(eir->ier_zones)[eir->ier_numZones-1] = strdup(a_zoneName);
+		eir->ier_values = (char **)calloc(1, sizeof (char *));
+		(eir->ier_values)[eir->ier_numZones-1] = strdup(a_value);
+
+		a_erc->er_numEntries = 1;
+		a_erc->er_theEntries = eir;
+
+		echoDebug(DBG_DEPCHK_RECORD_ERROR, (long)a_erc, a_pkginst,
+					a_zoneName, a_value);
+
+		return;
+	}
+
+	/* see if this package already has an entry if so add zone to list */
+
+	for (i = 0; i < a_erc->er_numEntries; i++) {
+		erc = &a_erc->er_theEntries[i];
+
+		if (strcmp(erc->ier_packageName, a_pkginst) != 0) {
+			continue;
+		}
+
+		echoDebug(DBG_DEPCHK_RECORD_ZERROR, (long)a_erc, a_zoneName,
+			a_value, erc->ier_packageName, erc->ier_numZones,
+			erc->ier_zones[0]);
+
+		/*
+		 * this package already has an entry - add zone to
+		 * existing package entry the modified records will
+		 * look like this:
+		 * err->er_#entry++;
+		 * err->entry[0]->...
+		 * err->entry[i]->
+		 * -------------->record->
+		 * ---------------------->ier_numZones++;
+		 * ---------------------->ier_packageName=a_pkginst
+		 * ---------------------->ier_zones[0]=...
+		 * ---------------------->ier_zones[...]=...
+		 * ---------------------->ier_zones[ier_numZones-1]=a_zoneName
+		 * ---------------------->ier_values[0]=...
+		 * ---------------------->ier_values[...]=...
+		 * ---------------------->ier_values[ier_numZones-1]=a_value
+		 * err->entry[i+1]->...
+		 */
+		erc->ier_numZones++;
+		erc->ier_zones = (char **)realloc(erc->ier_zones,
+					sizeof (char **)*erc->ier_numZones);
+		(erc->ier_zones)[erc->ier_numZones-1] = strdup(a_zoneName);
+		erc->ier_values = (char **)realloc(erc->ier_values,
+					sizeof (char **)*erc->ier_numZones);
+		(erc->ier_values)[erc->ier_numZones-1] = strdup(a_value);
+		return;
+	}
+
+	/*
+	 * this packages does not have an entry - add new package
+	 * entry for this zone the modified records will look like this:
+	 * err->er_#entry++;
+	 * err->entry[0]->record->ier_numZones=...
+	 * err->entry[0]->record->ier_packageName=...
+	 * err->entry[0]->record->ier_zones[0]=...
+	 * err->entry[0]->record->ier_values[0]=...
+	 * err->entry[er_#entry-1]->record->ier_numZones=1
+	 * err->entry[er_#entry-1]->record->ier_packageName=a_pkginst
+	 * err->entry[er_#entry-1]->record->ier_zones[0]=a_zoneName
+	 * err->entry[er_#entry-1]->record->ier_values[0]=a_value
+	 */
+
+	echoDebug(DBG_DEPCHK_RECORD_PERROR, (long)a_erc,
+			a_erc->er_numEntries, a_pkginst, a_zoneName, a_value);
+
+	a_erc->er_numEntries++;
+
+	a_erc->er_theEntries = realloc(a_erc->er_theEntries,
+			sizeof (depckErrorRecord_t)*a_erc->er_numEntries);
+
+	erc = &a_erc->er_theEntries[a_erc->er_numEntries-1];
+
+	erc->ier_packageName = strdup(a_pkginst);
+	erc->ier_numZones = 1;
+	erc->ier_zones = (char **)calloc(1, sizeof (char *));
+	(erc->ier_zones)[erc->ier_numZones-1] = strdup(a_zoneName);
+	erc->ier_values = (char **)calloc(1, sizeof (char *));
+	(erc->ier_values)[erc->ier_numZones-1] = strdup(a_value);
+}
+
+/*
+ * *****************************************************************************
+ * static internal (private) functions
+ * *****************************************************************************
+ */
+
+static int
+collectError(int *r_numZones, char **r_zoneNames, char *a_packageName,
+	depckl_t *a_dck, int a_depIndex, depckErrorRecord_t *a_eir,
+	int a_errIndex)
+{
+	char	msgbuf[4096];
+	char	*zn = *r_zoneNames;
+
+	if (a_dck[a_depIndex].ignore_values == (char *)NULL) {
+		if (a_dck[a_depIndex].err_msg == (char **)NULL) {
+			(void) snprintf(msgbuf, sizeof (msgbuf),
+			ERR_DEPENDENCY_REPORT, a_eir->ier_values[a_errIndex],
+			"package", a_packageName,
+			"zone", a_eir->ier_zones[a_errIndex]);
+		} else {
+			/* LINTED variable format specifier to snprintf(); */
+			(void) snprintf(msgbuf, sizeof (msgbuf),
+			*a_dck[a_depIndex].err_msg,
+			a_eir->ier_values[a_errIndex],
+			"package", a_packageName,
+			"zone", a_eir->ier_zones[a_errIndex]);
+		}
+		if (a_dck[a_depIndex].depcklFunc != NULL) {
+			int	err;
+
+			err = (a_dck[a_depIndex].depcklFunc)(msgbuf,
+							a_packageName);
+			echoDebug(DBG_DEPCHK_COLLECT_ERROR, err, a_packageName,
+					msgbuf);
+			if (err != 0) {
+				return (err);
+			}
+		} else {
+			echoDebug(DBG_DEPCHK_COLLECT_IGNORE, a_packageName,
+					msgbuf);
+			ptext(stderr, "\\n%s", msgbuf);
+		}
+		return (0);
+	}
+
+	*r_numZones = (*r_numZones)+1;
+	if (zn == (char *)NULL) {
+		zn = strdup(a_eir->ier_zones[a_errIndex]);
+	} else {
+		char *p;
+		int len = strlen(zn)+strlen(a_eir->ier_zones[a_errIndex])+3;
+		p = calloc(1, len);
+		(void) snprintf(p, len, "%s, %s", zn,
+			a_eir->ier_zones[a_errIndex]);
+		free(zn);
+		zn = p;
+
+	}
+	*r_zoneNames = zn;
+	return (0);
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/usr/src/cmd/svr4pkg/libinst/dockdeps.c	Fri Jun 05 10:28:40 2009 -0400
@@ -0,0 +1,452 @@
+/*
+ * 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
+ * or http://www.opensolaris.org/os/licensing.
+ * 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 2006 Sun Microsystems, Inc.  All rights reserved.
+ * Use is subject to license terms.
+ */
+
+/* Copyright (c) 1984, 1986, 1987, 1988, 1989 AT&T */
+/* All Rights Reserved */
+
+
+
+#include <stdio.h>
+#include <errno.h>
+#include <limits.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <string.h>
+#include <ctype.h>
+#include <dirent.h>
+#include <sys/types.h>
+#include <pkgstrct.h>
+#include <pkginfo.h>
+#include <locale.h>
+#include <libintl.h>
+#include <pkglib.h>
+#include "libinst.h"
+#include "libadm.h"
+#include "messages.h"
+
+#define	LSIZE	256
+#define	NVERS	50
+
+/*
+ * internal global variables
+ */
+
+static struct pkginfo info;
+
+static char	type;
+static char	*alist[NVERS];
+static char	*rmpkginst;
+static char	*vlist[NVERS];
+static char	file[128];
+static char	name[128];
+static char	rmpkg[PKGSIZ+1];
+static char	wabbrev[128];
+
+static int	errflg = 0;
+static int	nlist;
+static int	pkgexist;
+static int	pkgokay;
+static int	is_update;
+static int	is_patch_update;
+
+/*
+ * IMPORTANT NOTE: THE SIZE OF 'abbrev' IS HARD CODED INTO THE CHARACTER
+ * ARRAY SSCANF_FORMAT -- YOU MUST UPDATE BOTH VALUES AT THE SAME TIME!!
+ */
+
+static char	abbrev[128+1];
+static char	*SSCANF_FORMAT = "%c %128s %[^\n]";
+
+/*
+ * forward declarations
+ */
+
+static void	ckrdeps(boolean_t a_preinstallCheck);
+static void	ckpreq(FILE *fp, char *dname, boolean_t a_preinstallCheck);
+static void	deponme(char *pkginst, char *pkgname,
+				boolean_t a_preinstallCheck);
+static void	prereq(char *pkginst, char *pkgname,
+				boolean_t a_preinstallCheck);
+static void	incompat(char *pkginst, char *pkgname,
+				boolean_t a_preinstallCheck);
+static int	getline(FILE *fp);
+
+/*
+ * *****************************************************************************
+ * global external (public) functions
+ * *****************************************************************************
+ */
+
+int
+dockdeps(char *a_depfile, int a_removeFlag, boolean_t a_preinstallCheck)
+{
+	FILE	*fp;
+	int	i;
+	char	*inst;
+
+	if (a_removeFlag) {
+		/* check removal dependencies */
+		rmpkginst = a_depfile;
+		(void) strncpy(rmpkg, rmpkginst, PKGSIZ);
+		(void) strtok(rmpkg, ".");
+		(void) snprintf(file, sizeof (file),
+				"%s/%s/%s", pkgdir, rmpkginst, DEPEND_FILE);
+		if ((fp = fopen(file, "r")) == NULL)
+			goto done;
+	} else {
+		if ((fp = fopen(a_depfile, "r")) == NULL) {
+			progerr(ERR_CANNOT_OPEN_DEPEND_FILE, a_depfile,
+					strerror(errno));
+			quit(99);
+		}
+	}
+
+	while (getline(fp)) {
+		switch (type) {
+		    case 'I':
+		    case 'P':
+			if (a_removeFlag) {
+				continue;
+			}
+			break;
+
+		    case 'R':
+			if (!a_removeFlag) {
+				continue;
+			}
+			break;
+
+		    default:
+			errflg++;
+			progerr(ERR_UNKNOWN_DEPENDENCY, type);
+			break;
+		}
+
+		/* check to see if any versions listed are installed */
+		pkgexist = pkgokay = 0;
+		i = 0;
+		if (strchr(abbrev, '.')) {
+			progerr(ERR_PKGABRV, abbrev);
+		}
+		(void) snprintf(wabbrev, sizeof (wabbrev), "%s.*", abbrev);
+
+		do {
+			inst = fpkginst(wabbrev, alist[i], vlist[i]);
+			if (inst && (pkginfo(&info, inst, NULL, NULL) == 0)) {
+				pkgexist++;
+				if ((info.status == PI_INSTALLED) ||
+				    (info.status == PI_PRESVR4))
+					pkgokay++;
+			}
+		} while (++i < nlist);
+		(void) fpkginst(NULL); 	/* force closing/rewind of files */
+
+		if (!info.name) {
+			info.name = name;
+		}
+
+		switch (type) {
+		    case 'I':
+			incompat(abbrev, info.name, a_preinstallCheck);
+			break;
+
+		    case 'P':
+			prereq(abbrev, name, a_preinstallCheck);
+			break;
+
+		    case 'R':
+			deponme(abbrev, info.name, a_preinstallCheck);
+		}
+	}
+	(void) fclose(fp);
+
+done:
+	if (a_removeFlag) {
+		ckrdeps(a_preinstallCheck);
+	}
+
+	return (errflg);
+}
+
+void
+setPatchUpdate(void)
+{
+	is_patch_update = 1;
+}
+
+int
+isPatchUpdate(void)
+{
+	return ((is_patch_update) ? 1 : 0);
+}
+
+void
+setUpdate(void)
+{
+	is_update = 1;
+}
+
+int
+isUpdate(void)
+{
+	return ((is_update) ? 1 : 0);
+}
+
+/*
+ * *****************************************************************************
+ * static internal (private) functions
+ * *****************************************************************************
+ */
+
+static void
+incompat(char *pkginst, char *pkgname, boolean_t a_preinstallCheck)
+{
+	char buf[512];
+
+	if (!pkgexist)
+		return;
+
+	errflg++;
+	if (a_preinstallCheck == B_TRUE) {
+		(void) fprintf(stdout, "incompat=%s\n", pkginst);
+		return;
+	}
+
+	logerr(ERR_WARNING);
+	(void) snprintf(buf, sizeof (buf), ERR_INCOMP_VERS, pkginst, pkgname);
+	puttext(stderr, buf, 4, 0);
+	(void) putc('\n', stderr);
+}
+
+static void
+prereq(char *pkginst, char *pkgname, boolean_t a_preinstallCheck)
+{
+	register int i;
+	char buf[512];
+
+	if (pkgokay) {
+		return;
+	}
+
+	errflg++;
+
+	if (a_preinstallCheck == B_TRUE) {
+		if (pkgexist) {
+			(void) fprintf(stdout,
+				"prerequisite-incomplete=%s\n", pkginst);
+		} else {
+			(void) fprintf(stdout,
+				"prerequisite-installed=%s\n", pkginst);
+		}
+		return;
+	}
+
+	logerr(ERR_WARNING);
+	if (pkgexist) {
+		(void) snprintf(buf, sizeof (buf), ERR_PRENCI, pkginst,
+					pkgname);
+		puttext(stderr, buf, 4, 0);
+		(void) putc('\n', stderr);
+	} else {
+		(void) snprintf(buf, sizeof (buf), ERR_PREREQ, pkginst,
+					pkgname);
+		if (nlist) {
+			(void) strcat(buf, ERR_VALINST);
+		}
+		puttext(stderr, buf, 4, 0);
+		(void) putc('\n', stderr);
+		for (i = 0; i < nlist; i++) {
+			(void) printf("          ");
+			if (alist[i])
+				(void) printf("(%s) ", alist[i]);
+			if (vlist[i])
+				(void) printf("%s", vlist[i]);
+			(void) printf("\n");
+		}
+	}
+}
+
+static void
+deponme(char *pkginst, char *pkgname, boolean_t a_preinstallCheck)
+{
+	char buf[512];
+
+	if (!pkgexist)
+		return;
+
+	errflg++;
+
+	if (a_preinstallCheck == B_TRUE) {
+		if (!pkgname || !pkgname[0]) {
+			(void) snprintf(buf, sizeof (buf),
+					"dependonme=%s", pkginst);
+		} else {
+			(void) snprintf(buf, sizeof (buf),
+				"dependsonme=%s:%s", pkginst, pkgname);
+		}
+		(void) fprintf(stdout, "%s\n", buf);
+		return;
+	}
+
+	logerr(ERR_WARNING);
+	if (!pkgname || !pkgname[0]) {
+		(void) snprintf(buf, sizeof (buf), ERR_DEPONME, pkginst);
+	} else {
+		(void) snprintf(buf, sizeof (buf), ERR_DEPNAM, pkginst,
+				pkgname);
+	}
+	puttext(stderr, buf, 4, 0);
+	(void) putc('\n', stderr);
+}
+
+static int
+getline(FILE *fp)
+{
+	register int i, c, found;
+	char *pt, *new, line[LSIZE];
+
+	abbrev[0] = name[0] = type = '\0';
+
+	for (i = 0; i < nlist; i++) {
+		if (alist[i]) {
+			free(alist[i]);
+			alist[i] = NULL;
+		}
+		if (vlist[i]) {
+			free(vlist[i]);
+			vlist[i] = NULL;
+		}
+	}
+	alist[0] = vlist[0] = NULL;
+
+	found = (-1);
+	nlist = 0;
+	while ((c = getc(fp)) != EOF) {
+		(void) ungetc(c, fp);
+		if ((found >= 0) && !isspace(c))
+			return (1);
+
+		if (!fgets(line, LSIZE, fp))
+			break;
+
+		for (pt = line; isspace(*pt); /* void */)
+			pt++;
+		if (!*pt || (*pt == '#'))
+			continue;
+
+		if (pt == line) {
+			/* begin new definition */
+			/* LINTED variable format specifier to sscanf(): */
+			(void) sscanf(line, SSCANF_FORMAT, &type, abbrev, name);
+			found++;
+			continue;
+		}
+		if (found < 0)
+			return (0);
+
+		if (*pt == '(') {
+			/* architecture is specified */
+			if (new = strchr(pt, ')'))
+				*new++ = '\0';
+			else
+				return (-1); /* bad specification */
+			alist[found] = qstrdup(pt+1);
+			pt = new;
+		}
+		while (isspace(*pt))
+			pt++;
+		if (*pt) {
+			vlist[found] = qstrdup(pt);
+			if (pt = strchr(vlist[found], '\n'))
+				*pt = '\0';
+		}
+		found++;
+		nlist++;
+	}
+	return ((found >= 0) ? 1 : 0);
+}
+
+static void
+ckrdeps(boolean_t a_preinstallCheck)
+{
+	struct dirent *drp;
+	DIR	*dirfp;
+	FILE	*fp;
+	char	depfile[PATH_MAX+1];
+
+	if ((dirfp = opendir(pkgdir)) == NULL)
+		return;
+
+	while ((drp = readdir(dirfp)) != NULL) {
+		if (drp->d_name[0] == '.')
+			continue;
+
+		if (strcmp(drp->d_name, rmpkginst) == 0)
+			continue; /* others don't include me */
+		(void) snprintf(depfile, sizeof (depfile),
+				"%s/%s/%s", pkgdir, drp->d_name, DEPEND_FILE);
+		if ((fp = fopen(depfile, "r")) == NULL)
+			continue;
+
+		ckpreq(fp, drp->d_name, a_preinstallCheck);
+	}
+	(void) closedir(dirfp);
+}
+
+static void
+ckpreq(FILE *fp, char *dname, boolean_t a_preinstallCheck)
+{
+	register int i;
+	char	*inst;
+
+	while (getline(fp)) {
+		if (type != 'P')
+			continue;
+
+		if (strcmp(abbrev, rmpkg))
+			continue;
+
+		/* see if package is installed */
+		i = 0;
+		if (strchr(abbrev, '.') == 0) {
+			(void) strcat(abbrev, ".*");
+		}
+		pkgexist = 1;
+
+		do {
+			if (inst = fpkginst(abbrev, alist[i], vlist[i])) {
+				if (strcmp(inst, rmpkginst) == 0) {
+					deponme(dname, "", a_preinstallCheck);
+					(void) fclose(fp);
+					(void) fpkginst(NULL);
+					return;
+				}
+			}
+		} while (++i < nlist);
+		(void) fpkginst(NULL);
+	}
+	(void) fclose(fp);
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/usr/src/cmd/svr4pkg/libinst/doulimit.c	Fri Jun 05 10:28:40 2009 -0400
@@ -0,0 +1,164 @@
+/*
+ * 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
+ * or http://www.opensolaris.org/os/licensing.
+ * 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 2006 Sun Microsystems, Inc.  All rights reserved.
+ * Use is subject to license terms.
+ */
+
+
+
+#include <stdio.h>
+#include <sys/types.h>
+#include <sys/resource.h>
+#include <string.h>
+#include <signal.h>
+#include <limits.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <time.h>
+#include <locale.h>
+#include <libintl.h>
+#include <ctype.h>
+#include <pkglib.h>
+#include <libinst.h>
+
+#define	ERR_SET_ULIMIT	"unable to set ulimit to <%ld> blocks"
+#define	ERR_DO_ULIMIT	"An attempt was made to create a file larger than " \
+			    "ULIMIT. Source of fault is unknown."
+#define	ERR_SCRULIMIT	"Script <%s> attempted to create a file exceeding " \
+			    "ULIMIT."
+
+static char *script_name = NULL, *scr_error = NULL;
+static struct rlimit ulimit = {RLIM_INFINITY, RLIM_INFINITY};
+static struct rlimit dblimit = {RLIM_INFINITY, RLIM_INFINITY};
+static int limit_is_set = 0, fail_return = 0;
+
+void ulimit_quit();	/* XFSZ controlled signal handler. */
+int clr_ulimit();	/* Clear the user supplied file size limit. */
+void set_limit();	/* Called from installf to undo ulimit */
+int set_ulimit(char *script, char *err_msg);
+int assign_ulimit(char *fslimit);
+
+extern int	warnflag;
+
+void
+set_limit()
+{
+	limit_is_set = 1;
+}
+
+int
+clr_ulimit()
+{
+	if (limit_is_set) {
+		if (script_name)
+			free(script_name);
+		script_name = NULL;
+		if (scr_error)
+			free(scr_error);
+		scr_error = NULL;
+		fail_return = 99;
+
+		/* Clear out the limit to infinity. */
+		return (setrlimit(RLIMIT_FSIZE, &dblimit));
+	} else
+		return (0);
+}
+
+/*
+ * This sets up the ULIMIT facility for the signal retrieval. This sets up
+ * the static pointers to the message constants for indicating where the
+ * error occurred.
+ */
+int
+set_ulimit(char *script, char *err_msg)
+{
+	int n;
+
+	if (limit_is_set) {
+		(void) signal(SIGXFSZ, ulimit_quit);
+		if (script_name)
+			free(script_name);
+		script_name = strdup(script);
+		if (scr_error)
+			free(scr_error);
+		scr_error = strdup(err_msg);
+		fail_return = 99;
+
+		n = setrlimit(RLIMIT_FSIZE, &ulimit);
+
+		return (n);
+	} else
+		return (0);
+
+}
+
+/* Validate ULIMIT and set accordingly. */
+int
+assign_ulimit(char *fslimit)
+{
+	rlim_t limit;
+	int cnt = 0;
+
+	if (fslimit && *fslimit) {
+		/* fslimit must be a simple unsigned integer. */
+		do {
+			if (!isdigit(fslimit[cnt]))
+				return (-1);
+		} while (fslimit[++cnt]);
+
+		limit = atol(fslimit);
+
+		ulimit.rlim_cur = (limit * 512); /* fslimit is in blocks */
+
+		limit_is_set = 1;
+
+		return (0);
+	} else
+		return (-1);
+}
+
+/*
+ * This is the signal handler for ULIMIT.
+ */
+void
+ulimit_quit(int n)
+{
+#ifdef lint
+	int i = n;
+	n = i;
+#endif	/* lint */
+
+	setrlimit(RLIMIT_FSIZE, &dblimit);
+	signal(SIGXFSZ, SIG_IGN);
+
+	if (script_name) {
+		progerr(gettext(ERR_SCRULIMIT), script_name);
+		if (scr_error)
+			progerr("%s", scr_error);
+	} else
+		progerr(gettext(ERR_DO_ULIMIT));
+
+	quit(fail_return);
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/usr/src/cmd/svr4pkg/libinst/dryrun.c	Fri Jun 05 10:28:40 2009 -0400
@@ -0,0 +1,926 @@
+/*
+ * 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
+ * or http://www.opensolaris.org/os/licensing.
+ * 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 2007 Sun Microsystems, Inc.  All rights reserved.
+ * Use is subject to license terms.
+ */
+
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/types.h>
+#include <sys/wait.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+#include <pkgstrct.h>
+#include <unistd.h>
+#include <pkglib.h>
+#include <libintl.h>
+#include "libadm.h"
+#include "libinst.h"
+#include "dryrun.h"
+
+#define	HDR_FSUSAGE	"#name remote_name writeable bfree bused ifree iused"
+
+#define	ERR_NOCREAT	"cannot create %s."
+#define	ERR_NOOPEN	"cannot open %s."
+#define	ERR_NOWRITE	"cannot write to %s."
+#define	ERR_NOREAD	"cannot read from %s."
+#define	ERR_FSFAIL	"cannot construct filesystem table entry."
+#define	ERR_BADTYPE	"cannot record %s dryrun from %s continuation file."
+#define	ERR_NOCONT	"cannot install from continue file due to error " \
+			    "stacking."
+
+#define	ISUMASC_SUFFIX	".isum.asc"
+#define	FSASC_SUFFIX	".fs.asc"
+#define	IPOASC_SUFFIX	".ipo.asc"
+#define	IBIN_SUFFIX	".inst.bin"
+
+#define	MALCOUNT	5	/* package entries to allocate in a block */
+#define	PKGNAMESIZE	32	/* package entries to allocate in a block */
+
+extern struct cfextra **extlist;
+extern char *pkginst;
+
+static struct cfextra **extptr;
+static int	dryrun_mode = 0;
+static int	continue_mode = 0;
+static int	this_exitcode = 0;
+
+/* The dryrun and continuation filenames */
+static char *dryrun_sumasc;
+static char *dryrun_fsasc;
+static char *dryrun_poasc;
+static char *dryrun_bin;
+static char *continue_bin;
+
+/* These tell us if the actual files are initialized yet. */
+static int dryrun_initialized = 0;
+static int continue_initialized = 0;
+
+static int this_type;		/* type of transaction from main.c */
+
+static int pkg_handle = -1;
+static int tot_pkgs;
+
+/* Their associated file pointers */
+static FILE *fp_dra;
+static int fd_drb;
+static int fd_cnb;
+
+struct dr_pkg_entry {
+	char pkginst[PKGNAMESIZE + 2];
+	struct dr_pkg_entry *next;
+};
+
+static struct drinfo {
+	unsigned partial_set:1;	/* 1 if a partial installation was detected. */
+	unsigned partial:1;	/* 1 if a partial installation was detected. */
+	unsigned runlevel_set:1;
+	unsigned runlevel:1;	/* 1 if runlevel test returned an error. */
+	unsigned pkgfiles_set:1;
+	unsigned pkgfiles:1;
+	unsigned depend_set:1;
+	unsigned depend:1;
+	unsigned space_set:1;
+	unsigned space:1;
+	unsigned conflict_set:1;
+	unsigned conflict:1;
+	unsigned setuid_set:1;
+	unsigned setuid:1;
+	unsigned priv_set:1;
+	unsigned priv:1;
+	unsigned pkgdirs_set:1;
+	unsigned pkgdirs:1;
+	unsigned reqexit_set:1;
+	unsigned checkexit_set:1;
+
+	int	type;		/* type of operation */
+	int	reqexit;	/* request script exit code */
+	int	checkexit;	/* checkinstall script exit code */
+	int	exitcode;	/* overall program exit code. */
+
+	struct dr_pkg_entry *packages;	/* pointer to the list of packages */
+
+	int total_ext_recs;	/* total extlist entries */
+	int total_fs_recs;	/* total fs_tab entries */
+	int total_pkgs;		/* total number of dryrun packages */
+	int do_not_continue;	/* error stacking is likely */
+} dr_info;
+
+static char	*exitmsg;	/* the last meaningful message printed */
+
+/*
+ * In the event that live continue (continue from a dryrun source only)
+ * becomes a feature, it will be necessary to keep track of those events such
+ * as multiply edited files and files dependent upon multiple class action
+ * scripts that will lead to "tolerance stacking". Calling this function
+ * states that we've lost the level of precision necessary for a live
+ * continue.
+ */
+void
+set_continue_not_ok(void)
+{
+	dr_info.do_not_continue = 1;
+}
+
+int
+continue_is_ok(void)
+{
+	return (!dr_info.do_not_continue);
+}
+
+static void
+wr_OK(FILE *fp, char *parameter, int set, int value)
+{
+	(void) fprintf(fp, "%s=%s\n", parameter,
+		(set ? (value ? "OK" : "NOT_OK") : "NOT_TESTED"));
+}
+
+static void
+add_pkg_to_list(char *pkgname)
+{
+	struct dr_pkg_entry **pkg_entry;
+
+	if (pkg_handle == -1) {
+		if ((pkg_handle = bl_create(MALCOUNT,
+		    sizeof (struct dr_pkg_entry), "package dryrun")) == -1)
+			return;
+	}
+
+	pkg_entry = &(dr_info.packages);
+
+	while (*pkg_entry != NULL)
+		pkg_entry = &((*pkg_entry)->next);
+
+	/* LINTED pointer cast may result in improper alignment */
+	*pkg_entry = (struct dr_pkg_entry *)bl_next_avail(pkg_handle);
+	dr_info.total_pkgs++;
+
+	(void) snprintf((*pkg_entry)->pkginst, PKGNAMESIZE, "%s%s",
+		(pkgname ? pkgname : ""), ((this_exitcode == 0) ? "" : "-"));
+}
+
+static void
+write_pkglist_ascii(void)
+{
+	struct dr_pkg_entry *pkg_entry;
+
+	(void) fprintf(fp_dra, "PKG_LIST=\"");
+
+	pkg_entry = dr_info.packages;
+	while (pkg_entry) {
+		(void) fprintf(fp_dra, " %s", pkg_entry->pkginst);
+		pkg_entry = pkg_entry->next;
+	}
+
+	(void) fprintf(fp_dra, "\"\n");
+}
+
+static int
+write_string(int fd, char *string)
+{
+	int string_size;
+
+	if (string)
+		string_size = strlen(string) + 1;
+	else
+		string_size = 0;
+
+	if (write(fd, &string_size, sizeof (string_size)) == -1) {
+		progerr(gettext(ERR_NOWRITE), dryrun_bin);
+		return (0);
+	}
+
+	if (string_size > 0) {
+		if (write(fd, string, string_size) == -1) {
+			progerr(gettext(ERR_NOWRITE), dryrun_bin);
+			return (0);
+		}
+	}
+
+	return (1);
+}
+
+static char *
+read_string(int fd, char *buffer)
+{
+	size_t string_size;
+
+	if (read(fd, &(string_size), sizeof (string_size)) == -1) {
+		progerr(gettext(ERR_NOREAD), continue_bin);
+		return (NULL);
+	}
+
+	if (string_size != 0) {
+		if (read(fd, buffer, string_size) == -1) {
+			progerr(gettext(ERR_NOREAD), continue_bin);
+			return (NULL);
+		}
+	} else {
+		return (NULL);
+	}
+
+	return (buffer);
+}
+
+static void
+write_dryrun_ascii()
+{
+	int n;
+	char *fs_mntpt, *src_name;
+
+	if ((fp_dra = fopen(dryrun_sumasc, "wb")) == NULL) {
+		progerr(gettext(ERR_NOOPEN), dryrun_sumasc);
+		return;
+	}
+
+	(void) fprintf(fp_dra, "DR_TYPE=%s\n", (dr_info.type == REMOVE_TYPE ?
+	    "REMOVE" : "INSTALL"));
+
+	(void) fprintf(fp_dra, "PKG_INSTALL_ROOT=%s\n", (((get_inst_root()) &&
+	    (strcmp(get_inst_root(), "/") != 0)) ?
+	    get_inst_root() : ""));
+
+	write_pkglist_ascii();
+
+	wr_OK(fp_dra, "CONTINUE", 1, !(dr_info.do_not_continue));
+
+	wr_OK(fp_dra, "PARTIAL", dr_info.partial_set, dr_info.partial);
+
+	wr_OK(fp_dra, "RUNLEVEL", dr_info.runlevel_set, dr_info.runlevel);
+
+	(void) fprintf(fp_dra, "REQUESTEXITCODE=%d\n", dr_info.reqexit);
+
+	(void) fprintf(fp_dra, "CHECKINSTALLEXITCODE=%d\n", dr_info.checkexit);
+
+	wr_OK(fp_dra, "PKGFILES", dr_info.pkgfiles_set, dr_info.pkgfiles);
+
+	wr_OK(fp_dra, "DEPEND", dr_info.depend_set, dr_info.depend);
+
+	wr_OK(fp_dra, "SPACE", dr_info.space_set, dr_info.space);
+
+	wr_OK(fp_dra, "CONFLICT", dr_info.conflict_set, dr_info.conflict);
+
+	wr_OK(fp_dra, "SETUID", dr_info.setuid_set, dr_info.setuid);
+
+	wr_OK(fp_dra, "PRIV", dr_info.priv_set, dr_info.priv);
+
+	wr_OK(fp_dra, "PKGDIRS", dr_info.pkgdirs_set, dr_info.pkgdirs);
+
+	(void) fprintf(fp_dra, "EXITCODE=%d\n", dr_info.exitcode);
+
+	(void) fprintf(fp_dra, "ERRORMSG=%s\n", (exitmsg ? exitmsg : "NONE"));
+
+	(void) fclose(fp_dra);
+
+	if ((fp_dra = fopen(dryrun_fsasc, "wb")) == NULL) {
+		progerr(gettext(ERR_NOOPEN), dryrun_fsasc);
+		return;
+	}
+
+	(void) fprintf(fp_dra, "%s\nFSUSAGE=\\\n\"\\\n", HDR_FSUSAGE);
+
+	for (n = 0; fs_mntpt = get_fs_name_n(n); n++) {
+		int bfree, bused;
+		bfree = get_blk_free_n(n);
+		bused = get_blk_used_n(n);
+
+		if (bfree || bused) {
+			(void) fprintf(fp_dra, "%s %s %s %d %d %lu %lu \\\n",
+			    fs_mntpt,
+			    ((src_name = get_source_name_n(n)) ?
+			    src_name : "none?"),
+			    (is_fs_writeable_n(n) ? "TRUE" : "FALSE"),
+			    bfree,
+			    bused,
+			    get_inode_free_n(n),
+			    get_inode_used_n(n));
+		}
+	}
+
+	dr_info.total_fs_recs = n;
+
+	(void) fprintf(fp_dra, "\"\n");
+
+	(void) fclose(fp_dra);
+
+	if ((fp_dra = fopen(dryrun_poasc, "wb")) == NULL) {
+		progerr(gettext(ERR_NOOPEN), dryrun_poasc);
+		return;
+	}
+
+	dr_info.total_ext_recs = 0;
+
+	(void) fprintf(fp_dra, "WOULD_INSTALL=\\\n\"\\\n");
+
+	for (n = 0; extptr && extptr[n]; n++) {
+		/*
+		 * Write it out if it's a successful change or it is from the
+		 * prior dryrun file (meaning it was a change back then).
+		 */
+		if ((this_exitcode == 0 &&
+		    (extptr[n]->mstat.contchg || extptr[n]->mstat.attrchg)) ||
+		    extptr[n]->mstat.preloaded) {
+			(void) fprintf(fp_dra, "%c %s \\\n",
+				extptr[n]->cf_ent.ftype,
+				extptr[n]->client_path);
+
+			/* Count it, if it's going into the dryrun file. */
+			if (extptr[n]->cf_ent.ftype != 'i')
+				dr_info.total_ext_recs++;
+		}
+	}
+
+	(void) fprintf(fp_dra, "\"\n");
+
+	(void) fclose(fp_dra);
+}
+
+/*
+ * This writes out a dryrun file.
+ */
+static void
+write_dryrun_bin()
+{
+	struct fstable *fs_entry;
+	struct pinfo *pkginfo;
+	struct dr_pkg_entry *pkg_entry;
+	int n;
+	int fsentry_size = sizeof (struct fstable);
+	int extentry_size = sizeof (struct cfextra);
+	int pinfoentry_size = sizeof (struct pinfo);
+
+	if ((fd_drb = open(dryrun_bin,
+	    O_RDWR | O_APPEND | O_TRUNC)) == -1) {
+		progerr(gettext(ERR_NOOPEN), dryrun_bin);
+		return;
+	}
+
+	/* Write the dryrun info table. */
+	if (write(fd_drb, &dr_info, sizeof (struct drinfo)) == -1) {
+		progerr(gettext(ERR_NOWRITE), dryrun_bin);
+		return;
+	}
+
+	/* Write out the package instance list. */
+	pkg_entry = dr_info.packages;
+	while (pkg_entry) {
+		if (write(fd_drb, pkg_entry->pkginst, PKGNAMESIZE) == -1) {
+			progerr(gettext(ERR_NOWRITE), dryrun_bin);
+			return;
+		}
+		pkg_entry = pkg_entry->next;
+	}
+
+	/* Write out the fstable records. */
+	for (n = 0; n < dr_info.total_fs_recs; n++) {
+		fs_entry = get_fs_entry(n);
+
+		if (write(fd_drb, fs_entry, fsentry_size) == -1) {
+			progerr(gettext(ERR_NOWRITE), dryrun_bin);
+			return;
+		}
+
+		if (!write_string(fd_drb, fs_entry->name))
+			return;
+
+		if (!write_string(fd_drb, fs_entry->fstype))
+			return;
+
+		if (!write_string(fd_drb, fs_entry->remote_name))
+			return;
+	}
+
+	/* Write out the package objects and their attributes. */
+	for (n = 0; extptr && extptr[n]; n++) {
+		/* Don't save metafiles. */
+		if (extptr[n]->cf_ent.ftype == 'i')
+			continue;
+
+		/*
+		 * If it's a new package object (not left over from the
+		 * continuation file) and it indicates no changes to the
+		 * system, skip it. Only files that will change the system
+		 * are stored.
+		 */
+		if (extptr[n]->mstat.preloaded == 0 &&
+		    !(this_exitcode == 0 &&
+		    (extptr[n]->mstat.contchg || extptr[n]->mstat.attrchg)))
+			continue;
+
+		if (write(fd_drb, extptr[n], extentry_size) == -1) {
+			progerr(gettext(ERR_NOWRITE), dryrun_bin);
+			return;
+		}
+
+		if (!write_string(fd_drb, extptr[n]->cf_ent.path))
+			return;
+
+		if (!write_string(fd_drb, extptr[n]->cf_ent.ainfo.local))
+			return;
+
+		extptr[n]->cf_ent.pinfo = eptstat(&(extptr[n]->cf_ent),
+		    pkginst, CONFIRM_CONT);
+
+		/*
+		 * Now all of the entries about the various packages that own
+		 * this entry.
+		 */
+		pkginfo = extptr[n]->cf_ent.pinfo;
+
+		do {
+			if (write(fd_drb, pkginfo,
+			    pinfoentry_size) == -1) {
+				progerr(gettext(ERR_NOWRITE), dryrun_bin);
+				return;
+			}
+			pkginfo = pkginfo->next;	/* May be several */
+		} while (pkginfo);
+	}
+
+	(void) close(fd_drb);
+}
+
+static void
+init_drinfo(void) {
+
+	if (dr_info.partial != 0)
+		dr_info.partial_set = 0;
+
+	if (dr_info.runlevel != 0)
+		dr_info.runlevel_set = 0;
+
+	if (dr_info.pkgfiles != 0)
+		dr_info.pkgfiles_set = 0;
+
+	if (dr_info.depend != 0)
+		dr_info.depend_set = 0;
+
+	if (dr_info.space != 0)
+		dr_info.space_set = 0;
+
+	if (dr_info.conflict != 0)
+		dr_info.conflict_set = 0;
+
+	if (dr_info.setuid != 0)
+		dr_info.setuid_set = 0;
+
+	if (dr_info.priv != 0)
+		dr_info.priv_set = 0;
+
+	if (dr_info.pkgdirs != 0)
+		dr_info.pkgdirs_set = 0;
+
+	if (dr_info.reqexit == 0)
+		dr_info.reqexit_set = 0;
+
+	if (dr_info.checkexit == 0)
+		dr_info.checkexit_set = 0;
+
+	dr_info.packages = NULL;
+	tot_pkgs = dr_info.total_pkgs;
+	dr_info.total_pkgs = 0;
+}
+
+/*
+ * This function reads in the various continuation file data in order to seed
+ * the internal data structures.
+ */
+static boolean_t
+read_continue_bin(void)
+{
+	int n;
+	int fsentry_size = sizeof (struct fstable);
+	int extentry_size = sizeof (struct cfextra);
+	int pinfoentry_size = sizeof (struct pinfo);
+
+	pkgobjinit();
+	if (!init_pkgobjspace())
+		return (B_FALSE);
+
+	if ((fd_cnb = open(continue_bin, O_RDONLY)) == -1) {
+		progerr(gettext(ERR_NOOPEN), continue_bin);
+		return (B_FALSE);
+	}
+
+	/* Read the dryrun info structure. */
+	if (read(fd_cnb, &dr_info, sizeof (struct drinfo)) == -1) {
+		progerr(gettext(ERR_NOREAD), continue_bin);
+		return (B_FALSE);
+	}
+
+	init_drinfo();
+
+	if (this_type != dr_info.type) {
+		progerr(gettext(ERR_BADTYPE),
+		    (this_type == REMOVE_TYPE) ?
+		    "a remove" : "an install",
+		    (dr_info.type == REMOVE_TYPE) ?
+		    "a remove" : "an install");
+		return (B_FALSE);
+	}
+
+	/* Read in the dryrun package records. */
+	for (n = 0; n < tot_pkgs; n++) {
+		char pkg_name[PKGNAMESIZE];
+
+		if (read(fd_cnb, &pkg_name, PKGNAMESIZE) == -1) {
+			progerr(gettext(ERR_NOREAD), continue_bin);
+			return (B_FALSE);
+		}
+
+		add_pkg_to_list(pkg_name);
+	}
+
+	/* Read in the fstable records. */
+	for (n = 0; n < dr_info.total_fs_recs; n++) {
+		struct fstable fs_entry;
+		char name[PATH_MAX], remote_name[PATH_MAX];
+		char fstype[200];
+
+		if (read(fd_cnb, &fs_entry, fsentry_size) == -1) {
+			progerr(gettext(ERR_NOREAD), continue_bin);
+			return (B_FALSE);
+		}
+
+		if (read_string(fd_cnb, &name[0]) == NULL)
+			return (B_FALSE);
+
+		if (read_string(fd_cnb, &fstype[0]) == NULL)
+			return (B_FALSE);
+
+		if (read_string(fd_cnb, &remote_name[0]) == NULL)
+			return (B_FALSE);
+
+		if (load_fsentry(&fs_entry, name, fstype, remote_name)) {
+			progerr(gettext(ERR_FSFAIL));
+			return (B_FALSE);
+		}
+	}
+
+	/* Read in the package objects and their attributes. */
+	for (n = 0; n < dr_info.total_ext_recs; n++) {
+		struct cfextra ext_entry;
+		struct pinfo pinfo_area, *pinfo_ptr;
+		char path[PATH_MAX], local[PATH_MAX], *local_ptr;
+
+		if (read(fd_cnb, &ext_entry, extentry_size) == -1) {
+			progerr(gettext(ERR_NOREAD), continue_bin);
+			return (B_FALSE);
+		}
+
+		/*
+		 * If the previous dryrun replaced a directory with a
+		 * non-directory and we're going into *another* dryrun, we're
+		 * stacking errors and continuation should not be permitted.
+		 */
+		if (ext_entry.mstat.dir2nondir && dryrun_mode)
+			dr_info.do_not_continue = 1;
+
+		/*
+		 * Since we just read this from a continuation file; it is,
+		 * by definition, preloaded.
+		 */
+		ext_entry.mstat.preloaded = 1;
+
+		if (read_string(fd_cnb, &path[0]) == NULL)
+			return (B_FALSE);
+
+		local_ptr = read_string(fd_cnb, &local[0]);
+
+		ext_entry.cf_ent.pinfo = NULL;
+
+		/*
+		 * Now all of the entries about the various packages that own
+		 * this entry.
+		 */
+		do {
+			if (read(fd_cnb, &pinfo_area, pinfoentry_size) == -1) {
+				progerr(gettext(ERR_NOREAD), continue_bin);
+				return (B_FALSE);
+
+			}
+
+			pinfo_ptr = eptstat(&(ext_entry.cf_ent),
+			    pinfo_area.pkg, CONFIRM_CONT);
+
+			if (pinfo_ptr->next) {
+				pinfo_ptr = pinfo_ptr->next;
+			} else {
+				pinfo_ptr = NULL;
+			}
+
+		} while (pinfo_ptr);
+
+		seed_pkgobjmap(&ext_entry, path, local_ptr);
+	}
+
+	(void) close(fd_cnb);
+
+	/*
+	 * Return as reading is done, so pkginstall doesn't
+	 * read the same info from the system.
+	 */
+
+	return (B_TRUE);
+}
+
+int
+in_dryrun_mode(void)
+{
+	return (dryrun_mode);
+}
+
+void
+set_dryrun_mode(void)
+{
+	dryrun_mode = 1;
+}
+
+int
+in_continue_mode(void)
+{
+	return (continue_mode);
+}
+
+void
+set_continue_mode(void)
+{
+	continue_mode = 1;
+}
+
+/*
+ * Initialize a dryrun file by assigning it a name and creating it
+ * empty.
+ */
+static int
+init_drfile(char **targ_ptr, char *path)
+{
+	int n;
+	char *targ_file;
+
+	*targ_ptr = strdup(path);
+	targ_file = *targ_ptr;
+
+	if (access(targ_file, W_OK) == 0) {
+		(void) unlink(targ_file);
+	}
+
+	n = open(targ_file, O_WRONLY|O_CREAT|O_TRUNC|O_EXCL, 0644);
+	if (n < 0) {
+		progerr(gettext(ERR_NOCREAT), targ_file);
+		return (0);
+	} else {
+		(void) close(n);
+	}
+
+	return (1);
+}
+
+/*
+ * Initialize all required dryrun files and see that the target directory is
+ * present. If all goes well, we're in dryrun mode. If it doesn't, we're not.
+ */
+void
+init_dryrunfile(char *dr_dir)
+{
+	char temp_path[PATH_MAX];
+	char *dot_pos = (temp_path+strlen(dr_dir)+7);
+
+	/* First create or confirm the directory. */
+	if (isdir(dr_dir) != 0) {
+		(void) mkpath(dr_dir);
+	}
+
+	(void) snprintf(temp_path, sizeof (temp_path), "%s/dryrun", dr_dir);
+
+	(void) strcpy(dot_pos, ISUMASC_SUFFIX);
+
+	if (!init_drfile(&dryrun_sumasc, temp_path))
+		return;
+
+	(void) strcpy(dot_pos, FSASC_SUFFIX);
+
+	if (!init_drfile(&dryrun_fsasc, temp_path))
+		return;
+
+	(void) strcpy(dot_pos, IPOASC_SUFFIX);
+
+	if (!init_drfile(&dryrun_poasc, temp_path))
+		return;
+
+	(void) strcpy(dot_pos, IBIN_SUFFIX);
+
+	if (!init_drfile(&dryrun_bin, temp_path))
+		return;
+
+	dryrun_initialized = 1;
+}
+
+void
+init_contfile(char *cn_dir)
+{
+	char temp_path[PATH_MAX];
+
+	/* First confirm the directory. */
+	if (isdir(cn_dir) != 0)
+		return;		/* no continuation directory */
+
+	(void) snprintf(temp_path, sizeof (temp_path),
+				"%s/dryrun%s", cn_dir, IBIN_SUFFIX);
+	continue_bin = strdup(temp_path);
+
+	if (access(continue_bin, W_OK) != 0) {
+		free(continue_bin);
+		return;
+	}
+
+	continue_initialized = 1;
+}
+
+void
+set_dr_exitmsg(char *value)
+{
+	exitmsg = value;
+}
+
+void
+set_dr_info(int type, int value)
+{
+	switch (type) {
+	    case PARTIAL:
+		if (dr_info.partial_set == 0) {
+			dr_info.partial_set = 1;
+			dr_info.partial = (value ? 1 : 0);
+		}
+		break;
+
+	    case RUNLEVEL:
+		if (dr_info.runlevel_set == 0) {
+			dr_info.runlevel_set = 1;
+			dr_info.runlevel = (value ? 1 : 0);
+		}
+		break;
+
+	    case PKGFILES:
+		if (dr_info.pkgfiles_set == 0) {
+			dr_info.pkgfiles_set = 1;
+			dr_info.pkgfiles = (value ? 1 : 0);
+		}
+		break;
+
+	    case DEPEND:
+		if (dr_info.depend_set == 0) {
+			dr_info.depend_set = 1;
+			dr_info.depend = (value ? 1 : 0);
+		}
+		break;
+
+	    case SPACE:
+		if (dr_info.space_set == 0) {
+			dr_info.space_set = 1;
+			dr_info.space = (value ? 1 : 0);
+		}
+		break;
+
+	    case CONFLICT:
+		if (dr_info.conflict_set == 0) {
+			dr_info.conflict_set = 1;
+			dr_info.conflict = (value ? 1 : 0);
+		}
+		break;
+
+	    case SETUID:
+		if (dr_info.setuid_set == 0) {
+			dr_info.setuid_set = 1;
+			dr_info.setuid = (value ? 1 : 0);
+		}
+		break;
+
+	    case PRIV:
+		if (dr_info.priv_set == 0) {
+			dr_info.priv_set = 1;
+			dr_info.priv = (value ? 1 : 0);
+		}
+
+		break;
+
+	    case PKGDIRS:
+		if (dr_info.pkgdirs_set == 0) {
+			dr_info.pkgdirs_set = 1;
+			dr_info.pkgdirs = (value ? 1 : 0);
+		}
+
+		break;
+
+	    case REQUESTEXITCODE:
+		if (dr_info.reqexit_set == 0) {
+			dr_info.reqexit_set = 1;
+			dr_info.reqexit = value;
+		}
+
+		break;
+
+	    case CHECKEXITCODE:
+		if (dr_info.checkexit_set == 0) {
+			dr_info.checkexit_set = 1;
+			dr_info.checkexit = value;
+		}
+
+		break;
+
+	    case EXITCODE:
+		if (dr_info.exitcode == 0) {
+			dr_info.exitcode = value;
+		}
+
+		this_exitcode = value;
+
+		break;
+
+	    /* default to install if the value is kookie. */
+	    case DR_TYPE:
+		if (value == REMOVE_TYPE)
+			this_type = REMOVE_TYPE;
+		else
+			this_type = INSTALL_TYPE;
+
+		break;
+	}
+}
+
+void
+write_dryrun_file(struct cfextra **extlist)
+{
+	extptr = extlist;
+
+	if (dryrun_initialized) {
+		dr_info.type = this_type;
+
+		add_pkg_to_list(pkginst);
+		write_dryrun_ascii();
+		write_dryrun_bin();
+
+		if (dryrun_mode) {
+			free(dryrun_sumasc);
+			free(dryrun_fsasc);
+			free(dryrun_poasc);
+			free(dryrun_bin);
+		}
+	}
+}
+
+/*
+ * Name:		read_continuation
+ * Description:		If continuation is initialised, reads the
+ *			continuation binary file. The path for the
+ *			same is freed, if set,  as this is the last
+ *			chance to do so.
+ * Sets:		Error condition, through the pointer passed
+ *			if read failed.
+ * Returns:		B_TRUE - if the continuation binary file
+ *			from previous dryrun is read successfully.
+ *			B_FALSE - if either continuation is not initialised
+ *			or read was not successful.
+ */
+boolean_t
+read_continuation(int *error)
+{
+	boolean_t ret = B_FALSE;
+	*error = 0;
+	if (continue_initialized) {
+		if (!read_continue_bin()) {
+			continue_mode = 0;
+			free(continue_bin);
+			*error = -1;
+			return (ret);
+		}
+
+		if (continue_mode) {
+			free(continue_bin);
+		}
+		ret = B_TRUE;
+	}
+	return (ret);
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/usr/src/cmd/svr4pkg/libinst/echo.c	Fri Jun 05 10:28:40 2009 -0400
@@ -0,0 +1,187 @@
+/*
+ * 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
+ * or http://www.opensolaris.org/os/licensing.
+ * 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 2004 Sun Microsystems, Inc.  All rights reserved.
+ * Use is subject to license terms.
+ */
+
+/* Copyright (c) 1984, 1986, 1987, 1988, 1989 AT&T */
+/* All Rights Reserved */
+
+
+
+/*
+ * System includes
+ */
+
+#include <stdio.h>
+#include <stdarg.h>
+#include <unistd.h>
+#include <fcntl.h>
+#include <sys/types.h>
+#include <zone.h>
+
+/*
+ * consolidation pkg command library includes
+ */
+
+#include "pkglib.h"
+
+/*
+ * internal global variables
+ */
+
+static boolean_t	debugFlag = B_FALSE;	/* debug messages enabled? */
+static boolean_t	echoFlag = B_TRUE;	/* interactive msgs enabled? */
+
+/*
+ * *****************************************************************************
+ * global external (public) functions
+ * *****************************************************************************
+ */
+
+/*
+ * Name:	echo
+ * Synopsis:	Output an interactive message if interaction is enabled
+ * Description:	Main method for outputting an interactive message; call to
+ *		output interactive message if interation has not been disabled
+ *		by a previous call to echoSetFlag(0).
+ * Arguments:	format - [RO, RO*] (char *)
+ *			printf-style format for debugging message to be output
+ *		VARG_LIST - [RO] (?)
+ *			arguments as appropriate to 'format' specified
+ * Returns:	void
+ */
+
+/*PRINTFLIKE1*/
+void
+echo(char *fmt, ...)
+{
+	va_list ap;
+
+	/* output message if echoing is enabled */
+
+	if (echoFlag == B_TRUE) {
+		va_start(ap, fmt);
+
+		(void) vfprintf(stderr, fmt, ap);
+
+		va_end(ap);
+
+		(void) putc('\n', stderr);
+	}
+}
+
+/*
+ * Name:	echoDebug
+ * Synopsis:	Output a debugging message if debugging is enabled
+ * Description:	Main method for outputting a debugging message; call to
+ *		output debugging message if debugging has been enabled
+ *		by a previous call to echoDebugSetFlag(1).
+ * Arguments:	format - [RO, RO*] (char *)
+ *			printf-style format for debugging message to be output
+ *		VARG_LIST - [RO] (?)
+ *			arguments as appropriate to 'format' specified
+ * Returns:	void
+ * NOTE:	format of message will be:
+ *			# [ aaa bbb ccc ] message
+ *		where:	aaa - process i.d.
+ *			bbb - zone i.d.
+ *			ccc - name of program
+ * 		for example:
+ *			# [ 25685   0 pkgadd     ] unable to get package list
+ */
+
+/*PRINTFLIKE1*/
+void
+echoDebug(char *a_fmt, ...)
+{
+	va_list ap;
+
+	/* output debugging message if debugging is enabled */
+
+	if (debugFlag == B_TRUE) {
+		char	*p = get_prog_name();
+
+		(void) fprintf(stderr, "# [%6d %3d", getpid(), getzoneid());
+
+		if ((p != (char *)NULL) && (*p != '\0')) {
+			fprintf(stderr, " %-11s", p);
+		}
+
+		(void) fprintf(stderr, "] ");
+
+		va_start(ap, a_fmt);
+
+		(void) vfprintf(stderr, a_fmt, ap);
+
+		va_end(ap);
+
+		(void) putc('\n', stderr);
+	}
+}
+
+/*
+ * get the "interactive message enabled" flag
+ */
+
+boolean_t
+echoGetFlag(void) {
+	return (echoFlag);
+}
+
+/*
+ * set the "interactive message enabled" flag
+ */
+
+boolean_t
+echoSetFlag(boolean_t a_echoFlag)
+{
+	boolean_t	oldvalue;
+
+	oldvalue = echoFlag;
+	echoFlag = a_echoFlag;
+	return (oldvalue);
+}
+
+/*
+ * get the "debugging message enabled" flag
+ */
+
+boolean_t
+echoDebugGetFlag(void) {
+	return (debugFlag);
+}
+
+/*
+ * set the "debugging message enabled" flag
+ */
+
+boolean_t
+echoDebugSetFlag(boolean_t a_debugFlag)
+{
+	boolean_t	oldvalue;
+
+	oldvalue = debugFlag;
+	debugFlag = a_debugFlag;
+	return (oldvalue);
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/usr/src/cmd/svr4pkg/libinst/eptstat.c	Fri Jun 05 10:28:40 2009 -0400
@@ -0,0 +1,154 @@
+/*
+ * 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
+ * or http://www.opensolaris.org/os/licensing.
+ * 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 2006 Sun Microsystems, Inc.  All rights reserved.
+ * Use is subject to license terms.
+ */
+
+/* Copyright (c) 1984, 1986, 1987, 1988, 1989 AT&T */
+/* All Rights Reserved */
+
+
+#include <stdio.h>
+#include <errno.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <string.h>
+#include <malloc.h>
+#include <assert.h>
+#include <sys/stat.h>
+#include <pkgstrct.h>
+#include <pkginfo.h>
+#include <locale.h>
+#include <libintl.h>
+#include <pkglib.h>
+#include <install.h>
+#include <libinst.h>
+#include <libadm.h>
+
+#define	PINFOALLOC	200
+
+#define	ERR_MEMORY	"memory allocation failure, errno=%d"
+
+int	otherstoo;
+char	*useclass;
+
+static int pinfo_handle = -1;
+
+/* Free all allocated package info structures. */
+void
+pinfo_free(void)
+{
+	bl_free(pinfo_handle);
+}
+
+/*
+ * This function manipulates the pinfo entry corresponding to the package
+ * indicated on the command line.
+ */
+struct pinfo *
+eptstat(struct cfent *entry, char *pkg, char c)
+{
+	struct pinfo *pinfo, *last, *me, *myparent;
+
+	otherstoo = 0;
+	useclass = entry->pkg_class;
+
+	me = myparent = last = (struct pinfo *)0;
+
+	if (pinfo_handle == -1) {
+		pinfo_handle = bl_create(PINFOALLOC, sizeof (struct pinfo),
+		    "package data");
+	}
+
+	for (pinfo = entry->pinfo; pinfo; pinfo = pinfo->next) {
+		if (strcmp(pkg, pinfo->pkg) == 0) {
+			if (*pinfo->aclass)
+				useclass = pinfo->aclass;
+			myparent = last;
+			me = pinfo;
+		} else
+			otherstoo++;
+		last = pinfo;
+	}
+
+	if (c) {
+		/*
+		 * use a delete/add strategy to keep package list
+		 * ordered by modification time
+		 */
+		if (me) {
+			/* remove from list first */
+			if (myparent)
+				myparent->next = me->next;
+			else
+				entry->pinfo = me->next;
+			if (me == last)
+				last = myparent;
+			entry->npkgs--;
+			/* leave 'me' around until later! */
+		}
+		if ((c != STAT_NEXT) && (me || (c != RM_RDY))) {
+			/* need to add onto end */
+			entry->npkgs++;
+			if (me == NULL) {
+				/* LINTED pointer cast may result in impro... */
+				me = (struct pinfo *)
+				    bl_next_avail(pinfo_handle);
+				if (me == NULL) {
+					progerr(gettext(ERR_MEMORY), errno);
+					quit(99);
+				}
+			} else {
+				me->next = (struct pinfo *)NULL;
+				if (entry->npkgs == 1) {
+					if (me->aclass[0])
+						(void) strcpy(entry->pkg_class,
+							me->aclass);
+					useclass = entry->pkg_class;
+				} else
+					useclass = me->aclass;
+			}
+			(void) strncpy(me->pkg, pkg, PKGSIZ);
+
+			/*
+			 * Only change status for local objects.  Need
+			 * to maintain "shared" status for objects that
+			 * are provided from a server.
+			 */
+			if (me->status != SERVED_FILE)
+				me->status = ((c == DUP_ENTRY) ? '\0' : c);
+
+			if (last)
+				last->next = me; /* add to end */
+			else
+				entry->pinfo = me; /* only item */
+		} else {
+			/* just wanted to remove this package from list */
+			if (me) {
+				free(me);
+				me = (struct pinfo *)0;
+			}
+		}
+	}
+	return (me);
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/usr/src/cmd/svr4pkg/libinst/finalck.c	Fri Jun 05 10:28:40 2009 -0400
@@ -0,0 +1,205 @@
+/*
+ * 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
+ * or http://www.opensolaris.org/os/licensing.
+ * 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 2006 Sun Microsystems, Inc.  All rights reserved.
+ * Use is subject to license terms.
+ */
+
+/* Copyright (c) 1984, 1986, 1987, 1988, 1989 AT&T */
+/* All Rights Reserved */
+
+
+
+#include <stdio.h>
+#include <string.h>
+#include <limits.h>
+#include <sys/types.h>
+#include <pkgstrct.h>
+#include <locale.h>
+#include <libintl.h>
+
+/*
+ * consolidation pkg command library includes
+ */
+
+#include <pkglib.h>
+
+/*
+ * local pkg command library includes
+ */
+
+#include "install.h"
+#include "libinst.h"
+#include "libadm.h"
+#include "messages.h"
+
+extern int	warnflag;
+
+/*
+ * forward declarations
+ */
+
+static int	finalck_warning(struct cfent *ept, int attrchg, int contchg);
+static int	finalck_error(struct cfent *ept, int attrchg, int contchg);
+
+int
+finalck(struct cfent *ept, int attrchg, int contchg, boolean_t a_warning)
+{
+	int	errflg;
+
+	/*
+	 * invoke the correct finalck based on whether warnings or errors
+	 * should be generated
+	 */
+
+	if (a_warning) {
+		errflg = finalck_warning(ept, attrchg, contchg);
+	} else {
+		errflg = finalck_error(ept, attrchg, contchg);
+	}
+
+	/* exit debug output */
+
+	echoDebug(DBG_FINALCK_EXIT, errflg, ept->ftype,
+		ept->path ? ept->path : "");
+
+	/* return results of the finalck_xxx call */
+
+	return (errflg);
+}
+
+/*
+ * this finalck generates errors on failure
+ */
+
+static int
+finalck_error(struct cfent *ept, int attrchg, int contchg)
+{
+	int	errflg = 0;
+
+	/* entry debug info */
+
+	echoDebug(DBG_FINALCK_ERROR, attrchg, contchg, ept->ftype,
+		ept->path ? ept->path : "");
+
+	/* on attribute or content change, verify attributes */
+
+	if (attrchg || contchg) {
+		int	n;
+
+		/* verify change, or fix if possible */
+		n = averify(1, &ept->ftype, ept->path, &ept->ainfo);
+		echoDebug(DBG_FINALCK_ERROR_AVERIFY, n);
+		if (n != 0) {
+			logerr(ERR_FINALCK_ATTR, ept->path);
+			logerr(getErrbufAddr());
+			errflg++;
+			warnflag++;
+			if (n == VE_EXIST)
+				return (1); /* no need to check contents */
+		}
+	}
+
+	/* on content change of "f/e/v" type, verify contents */
+
+	if (contchg && strchr("fev", ept->ftype)) {
+		int	n;
+
+		/* verify change was executed properly */
+
+		if (contchg < 0) {
+			ept->cinfo.modtime = BADCONT;
+			ept->cinfo.size = BADCONT;
+			ept->cinfo.cksum = BADCONT;
+		}
+
+		n = cverify(1, &ept->ftype, ept->path, &ept->cinfo, 1);
+		echoDebug(DBG_FINALCK_ERROR_CVERIFY, n);
+		if (n != 0) {
+			logerr(ERR_FINALCK_CONT, ept->path);
+			logerr(getErrbufAddr());
+			errflg++;
+			warnflag++;
+		}
+	}
+
+	return (errflg);
+}
+
+/*
+ * this finalck generates warnings on failure
+ */
+
+static int
+finalck_warning(struct cfent *ept, int attrchg, int contchg)
+{
+	int	errflg = 0;
+
+	/* entry debug info */
+
+	echoDebug(DBG_FINALCK_WARNING, attrchg, contchg, ept->ftype,
+		ept->path ? ept->path : "");
+
+
+	/* on attribute or content change, verify attributes */
+
+	if (attrchg || contchg) {
+		int	n;
+
+		/* verify change, or fix if possible */
+
+		n = averify(1, &ept->ftype, ept->path, &ept->ainfo);
+		echoDebug(DBG_FINALCK_WARNING_AVERIFY, n);
+		if (n != 0) {
+			logerr(WRN_FINALCK_ATTR, ept->path);
+			logerr(getErrbufAddr());
+			errflg++;
+			if (n == VE_EXIST) {
+				return (1); /* no need to check contents */
+			}
+		}
+	}
+
+	/* on content change of "f/e/v" type, verify contents */
+
+	if (contchg && strchr("fev", ept->ftype)) {
+		int	n;
+
+		/* verify change was executed properly */
+
+		if (contchg < 0) {
+			ept->cinfo.modtime = BADCONT;
+			ept->cinfo.size = BADCONT;
+			ept->cinfo.cksum = BADCONT;
+		}
+
+		n = cverify(1, &ept->ftype, ept->path, &ept->cinfo, 1);
+		echoDebug(DBG_FINALCK_WARNING_CVERIFY, n);
+		if (n != 0) {
+			logerr(WRN_FINALCK_CONT, ept->path);
+			logerr(getErrbufAddr());
+		}
+		errflg++;
+	}
+
+	return (errflg);
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/usr/src/cmd/svr4pkg/libinst/findscripts.c	Fri Jun 05 10:28:40 2009 -0400
@@ -0,0 +1,197 @@
+/*
+ * 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
+ * or http://www.opensolaris.org/os/licensing.
+ * 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 1995 Sun Microsystems, Inc.  All rights reserved.
+ * Use is subject to license terms.
+ */
+
+
+
+#include <stdio.h>
+#include <errno.h>
+#include <string.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <locale.h>
+#include <libintl.h>
+#include <pkglocs.h>
+#include <pkglib.h>
+#include "libinst.h"
+
+#define	ERR_INVALID_CAS	"%d is an invalid class action script type."
+#define	ERR_NO_NONE	"Cannot find the default archive install script."
+#define	ERR_NO_PATH	"No paths for finding class action scripts."
+
+/* setlist.c */
+extern struct cl_attr **cl_Classes;
+extern int cl_NClasses;
+extern char *cl_nam(int idx);
+
+static int pkg_has_arch;
+
+/* Return the install class action script associated with this class index */
+char *
+cl_iscript(int idx)
+{
+	if (cl_Classes && idx >= 0 && idx < cl_NClasses)
+		return (cl_Classes[idx]->inst_script);
+	return (NULL);
+}
+
+/*
+ * This resets an input class action script pointer and the various
+ * codes that are associated with special treatment available to a class
+ * action script. It returns 1 if there was a script there in the first
+ * place and 0 if there wasn't.
+ */
+int
+cl_deliscript(int idx)
+{
+	if (cl_Classes && idx >= 0 && idx < cl_NClasses)
+		if (cl_Classes[idx]->inst_script) {
+			free(cl_Classes[idx]->inst_script);
+			cl_Classes[idx]->inst_script = NULL;
+			cl_Classes[idx]->src_verify = DEFAULT;
+			cl_Classes[idx]->dst_verify = DEFAULT;
+			cl_Classes[idx]->relpath_2_CAS = DEFAULT;
+
+		} else
+			return (0);
+	return (1);
+}
+
+/* Return the remove class action script associated with this class index */
+char *
+cl_rscript(int idx)
+{
+	if (cl_Classes && idx >= 0 && idx < cl_NClasses)
+		return (cl_Classes[idx]->rem_script);
+	return (NULL);
+}
+
+/*
+ * This scans the admin directories for the class acton scripts associated
+ * with the classes to be installed. It will look for install or remove
+ * scripts and place appropriate pointers into the cl_Classes list. There's
+ * no reason why it couldn't look for both except that I haven't seen a
+ * need for it yet.
+ */
+void
+find_CAS(int CAS_type, char *pkgbin, char *instdir)
+{
+	int i;
+	char path[PATH_MAX];
+
+	if (instdir == NULL || pkgbin == NULL) {
+		progerr(gettext(ERR_NO_PATH));
+		quit(99);
+	}
+
+	if (CAS_type == I_ONLY) {
+		for (i = 0; i < cl_NClasses; i++) {
+			/*
+			 * Locate appropriate installation class action
+			 * script, if any; look on media for script,
+			 * since it might be on the system due to a
+			 * previous installation.
+			 */
+			(void) sprintf(path, "%s/install/i.%s", instdir,
+			    cl_nam(i));
+			if (access(path, R_OK) == 0) {
+				(void) sprintf(path, "%s/i.%s", pkgbin,
+				    cl_nam(i));
+				cl_Classes[i]->inst_script = qstrdup(path);
+				continue;
+			}
+
+			(void) sprintf(path, "%s/i.%s", PKGSCR, cl_nam(i));
+			if (access(path, R_OK) == 0) {
+				cl_Classes[i]->inst_script = qstrdup(path);
+				continue;
+			}
+
+			/*
+			 * Provide CAS to uncompress and distribute a
+			 * compressed cpio archive for those older packages
+			 * that don't include their own. This is the first
+			 * point at which we know, it's an old package
+			 * without all the various pkginfo items set.
+			 * The default script is provided for all classes
+			 * in an old package which do not have their own
+			 * class action script. These are the criteria used
+			 * by the script that packs the archives.
+			 */
+			(void) sprintf(path, "%s/%s", PKGSCR, DEF_NONE_SCR);
+			if (pkg_has_arch &&
+			    cl_Classes[i]->inst_script == NULL) {
+
+				cl_Classes[i]->src_verify = NOVERIFY;
+				cl_Classes[i]->dst_verify = QKVERIFY;
+				cl_Classes[i]->relpath_2_CAS = REL_2_CAS;
+
+				if (access(path, R_OK) == 0) {
+					cl_Classes[i]->inst_script =
+					    qstrdup(path);
+					continue;
+				} else {
+					progerr(gettext(ERR_NO_NONE));
+					quit(99);
+				}
+
+			}
+		}
+	} else if (CAS_type == R_ONLY) {
+		for (i = 0; i < cl_NClasses; i++) {
+			(void) sprintf(path, "%s/install/r.%s", instdir,
+			    cl_nam(i));
+			if (access(path, R_OK) == 0) {
+				(void) sprintf(path, "%s/r.%s", pkgbin,
+				    cl_nam(i));
+				cl_Classes[i]->rem_script = qstrdup(path);
+				continue;
+			}
+
+			(void) sprintf(path, "%s/r.%s", PKGSCR, cl_nam(i));
+			if (access(path, R_OK) == 0) {
+				cl_Classes[i]->rem_script = qstrdup(path);
+				continue;
+			}
+		}
+	} else {
+		progerr(gettext(ERR_INVALID_CAS), CAS_type);
+		quit(99);
+	}
+}
+
+/*
+ * This function deals with the special case of an old WOS package
+ * with a compressed cpio'd file set but no class action script.
+ * We find out it doesn't have a CAS later in find_CAS() and deal
+ * with it then. The only reason for this variable is to let
+ * findscripts() know to get the default script if it can't find it in
+ * the usual places.
+ */
+void
+is_WOS_arch(void)
+{
+	pkg_has_arch++;
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/usr/src/cmd/svr4pkg/libinst/fixpath.c	Fri Jun 05 10:28:40 2009 -0400
@@ -0,0 +1,983 @@
+/*
+ * 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
+ * or http://www.opensolaris.org/os/licensing.
+ * 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 2009 Sun Microsystems, Inc.  All rights reserved.
+ * Use is subject to license terms.
+ */
+
+
+/*
+ * This module contains all the code necessary to establish the key base
+ * directories to which the actual components of the package will be
+ * installed or removed. -- JST
+ */
+
+#include <stdio.h>
+#include <string.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <sys/stat.h>	/* mkdir() declaration */
+#include <libintl.h>
+#include <pkglib.h>
+#include <install.h>
+#include <libadm.h>
+#include <libinst.h>
+
+static char *install_root = NULL;
+static int install_root_exists = 0;	/* An install root was specified */
+static int install_root_len;		/* strlen(install_root) */
+static char *orig_basedir = NULL;	/* The unadjusted basedir */
+static char *basedir = NULL;		/* basedir (cmb w/ inst rt if req) */
+static int basedir_exists = 0;		/* There are relocatable paths */
+static char *client_basedir = NULL;
+static int client_basedir_exists = 0;	/* Installing from a host */
+static char *env_cl_bdir = NULL;	/* CLIENT_BASEDIR from environment */
+static int ir_accessed = 0;		/* install_root has been used */
+static int relocatable;			/* set_basedir() assumed this */
+static int partial_inst = 0; /* Installing pkg from partial spool directory */
+static boolean_t depend_pkginfo_DB = B_FALSE; /* Only update depend/pkginfoDB */
+static int partial_spool_create = 0; /* Create partial spool dir */
+
+static int	ask_basedir(char *path, int nointeract);
+static char	*expand_path(char *path);
+static int	set_client_basedir(void);
+static char 	*fixpath_dup(char *path);
+static int	orig_offset_rel;
+
+/*
+ * base_sepr and rel_fmt support construction of absolute paths from
+ * relative paths.
+ */
+static int	base_sepr = 1;	/* separator length btwn basedir & path */
+static char	*rel_fmt[] = { "%s%s", "%s/%s" };
+
+static int	eval_valid = 0;	/* everything set up to do an eval_path() */
+
+/* libpkg/gpkgmap.c */
+extern int	getmapmode();
+
+#define	MSG_IR_REPL	"Replacing current install root with %s."
+#define	ERR_IRSET	"Install_root has already been set to <%s> and used."
+#define	ERR_IRNOTABS	"Install_root (-R option) requires an absolute " \
+			"pathname: <%s>"
+#define	ERR_ALLOCFAILED	"insufficient memory in %s"
+#define	ERR_ADMIN_INVAL	"Invalid basedir entry in admin file."
+#define	ERR_PATHNAME 	"Path name is invalid"
+#define	ERR_RELINABS	"Relative path <%s> found in absolute package."
+#define	ERR_CL_MIS	"Constructed CLIENT_BASEDIR <%s> and " \
+			"environment CLIENT_BASEDIR <%s> do not match."
+#define	ERR_ASKBD	"%s is already installed at %s. Cannot create a " \
+			    "duplicate installation at %s."
+#define	ERR_NO_CL_BD	"Cannot resolve CLIENT_BASEDIR conflicts."
+#define	ERR_AMBDIRS	"Cannot evaluate path due to ambiguous " \
+			"base directories."
+#define	ERR_NODELETE	"unable to delete <%s>."
+#define	ERR_MKBASE	"unable to make directory <%s>."
+#define	MSG_REQBASEDIR	"Installation of this package requires a base " \
+			"directory."
+
+#define	MSG_MUSTEXIST	"\\nThe selected base directory <%s> must exist " \
+			"before installation is attempted."
+#define	MSG_YORNPRMPT	"Do you want this directory created now"
+
+#define	MSG_ISAFILE	"\\nThe selected base directory <%s> must exist " \
+			"before installation is attempted, but a file " \
+			"already exists in it's place."
+#define	MSG_YORNFILE	"Do you want the file deleted and the directory " \
+			"created now"
+
+#define	MSG_PROMPT	"Enter path to package base directory"
+
+#define	MSG_HELP	"Installation of this package requires that a UNIX " \
+			"directory be available for installation of " \
+			"appropriate software.  This directory may be part " \
+			"of any mounted filesystem, or may itself be a " \
+			"mount point.  In general, it is unwise to select a " \
+			"base directory which already contains other files " \
+			"and/or directories."
+
+/*
+ * Set the install root (-R option).
+ */
+
+int
+set_inst_root(char *path)
+{
+	static	char	tmp_path[PATH_MAX];
+
+	/*
+	 * If we've already set the install_root but no one has used it
+	 * yet, we'll complain and allow the change. If it's been used
+	 * then we'll deny the switch & return failed.
+	 */
+	if (install_root_exists)
+		/* If the two install_roots are different - problem */
+		if (strcmp(install_root, path))
+			/* We are trying to *change* the install_root */
+			if (ir_accessed) {
+				ptext(stderr, gettext(ERR_IRSET), path);
+				return (0);
+			} else { /* !ir_accessed */
+				ptext(stderr, gettext(MSG_IR_REPL), path);
+				install_root_exists = 0;	/* reset */
+				install_root = NULL;
+			}
+
+	if (path && *path) {
+		if (*path != '/') {
+			ptext(stderr, gettext(ERR_IRNOTABS), path);
+			return (0);
+		}
+
+		(void) strlcpy(tmp_path, path, sizeof (tmp_path));
+
+		canonize(tmp_path);
+
+		install_root = tmp_path;
+
+		install_root_exists = 1;
+
+		install_root_len = strlen(install_root);
+
+		/* If install_root is '/' then it's trivial. */
+		if (install_root_len == 1)
+			install_root_len = 0;
+		else
+			z_set_zone_root(install_root);
+	} else
+		install_root_exists = 0;
+
+	return (1);
+}
+
+/*
+ * This routine returns a path with the correct install_root prepended.
+ * if the install_root has been set. NOTE : this allocates memory
+ * which will need to be freed at some point.
+ */
+char *
+fixpath(char *path)
+{
+	register char *npath_ptr, *ir_ptr;
+	char *npath = NULL;
+
+	if (path && *path) {
+		if (install_root_exists) {
+			if ((npath =
+			    calloc(1, strlen(path) + install_root_len +
+			    1)) == NULL) {
+				progerr(gettext(ERR_ALLOCFAILED), "fixpath()");
+				quit(99);
+			}
+
+			npath_ptr = npath;
+			ir_ptr = get_inst_root();
+
+			while (*ir_ptr)	/* for every char in install_root */
+				*npath_ptr++ = *ir_ptr++;	/* copy it */
+
+			/*
+			 * If install_root == "/", a concatenation will
+			 * result in a return value of "//...", same goes
+			 * for an install_root ending in '/'. So we back
+			 * over a trailing '/' if it's there.
+			 */
+			if (*(npath_ptr - 1) == '/')
+				npath_ptr--;
+
+			if (strcmp(path, "/"))
+				(void) strcpy(npath_ptr, path);
+		} else
+			/*
+			 * If there's no install root & no client_basedir,
+			 * then return the path
+			 */
+			npath = strdup(path);
+	} else
+		/*
+		 * If there's no path specified, return the install root
+		 * since no matter what happens, this is where the
+		 * path will have to start.
+		 */
+		if (install_root_exists)
+			npath = strdup(get_inst_root());
+
+	return (npath);
+}
+
+/*
+ * This routine does what fixpath() does except it's for high-volume
+ * stuff restricted to the instvol() function. By using
+ * pathdup() and pathalloc() memory fragmentation is reduced. Also, the
+ * memory allocated by pathdup() and pathalloc() gets freed at the end
+ * of each volume installed.
+ */
+char *
+fixpath_dup(char *path)
+{
+	register char *npath_ptr, *ir_ptr;
+	char *npath = NULL;
+
+	if (path && *path) {
+		if (install_root_exists) {
+			npath = pathalloc(strlen(path) + install_root_len + 1);
+
+			npath_ptr = npath;
+			ir_ptr = get_inst_root();
+
+			while (*ir_ptr)	/* for every char in install_root */
+				*npath_ptr++ = *ir_ptr++;	/* copy it */
+
+			/*
+			 * If install_root == "/", a concatenation will
+			 * result in a return value of "//...", same goes
+			 * for an install_root ending in '/'. So we back
+			 * over a trailing '/' if it's there.
+			 */
+			if (*(npath_ptr - 1) == '/')
+				npath_ptr--;
+
+			if (strcmp(path, "/"))
+				(void) strcpy(npath_ptr, path);
+		} else
+			/*
+			 * If there's no install root & no client_basedir,
+			 * then return the path
+			 */
+			npath = pathdup(path);
+	} else
+		/*
+		 * If there's no path specified, return the install root
+		 * since no matter what happens, this is where the
+		 * path will have to start.
+		 */
+		if (install_root_exists)
+			npath = pathdup(get_inst_root());
+
+	return (npath);
+}
+
+/*
+ * This returns a pointer to a static name. This could be abused.
+ * -- JST (1993-07-21)
+ */
+char *
+get_inst_root(void)
+{
+	ir_accessed = 1;	/* we can't change it now */
+	return (install_root);
+}
+
+/*
+ * This routine takes path and removes install_root from the path
+ * if it has already been prepended. If install_root is not prepended to
+ * path or install_root is '/' or path == NULL then path is returned
+ * as is. If the resulting path is somehow relative, a corrupt
+ * package name error is raised and the program quits. NOTE : This
+ * function usually returns a pointer into the original path
+ * argument. It doesn't allocate new memory. This is possible,
+ * of course, because the path being returned is guaranteed to
+ * be a subset of the original argument unless basedir = '/' in
+ * which case a pointer to a static "/" is returned. See
+ * orig_path() below if you want to be handed a new copy of the
+ * return value.
+ */
+char *
+orig_path_ptr(char *path)
+{
+	char *retv = NULL;
+
+	if (path && *path) {	/* as long as we got an argument */
+		if (!install_root_exists)	/* if no install_root */
+			retv = path;		/*   path unchanged */
+
+		/*
+		 * Otherwise, if install_root is really prepended to the path
+		 * then remove it dealing appropriately with special cases.
+		 */
+		else if (strncmp(path, install_root, install_root_len) == 0) {
+			retv = path + install_root_len;
+			if (*retv == NULL)
+				retv = "/";
+
+			/*
+			 * The result will be relative if install_root = '/'.
+			 * If the basedir path was built legally, then moving
+			 * the pointer back one character will make it
+			 * absolute. If that fails then the path we got was
+			 * incorrectly constructed in the first place.
+			 */
+			else if (*retv != '/') {
+				retv--;
+				if (*retv != '/') {
+					progerr(gettext(ERR_PATHNAME));
+					quit(99);
+				}
+			}
+		} else
+			retv = path;	/* All else failing, return path. */
+
+		canonize(retv);
+	}
+
+	return (retv);
+}
+
+/*
+ * This function does the same as orig_path_ptr() except that it mallocs
+ * new space and provides a new copy of the original basedir path which
+ * needs to be free()'d one way or another later.
+ */
+char *
+orig_path(char *path)
+{
+	char *retv;
+
+	retv = orig_path_ptr(path);
+
+	return ((retv == NULL) ? retv : strdup(retv));
+}
+
+/*
+ * This function lets us hold onto the environment's version of
+ * CLIENT_BASEDIR for later review by set_client_basedir().
+ */
+void
+set_env_cbdir()
+{
+	register char *cb_ptr;
+
+	cb_ptr = getenv("CLIENT_BASEDIR");
+
+	if (cb_ptr && *cb_ptr) {
+		env_cl_bdir = strdup(cb_ptr);
+		canonize(env_cl_bdir);
+	}
+}
+
+/* ask for the basedir */
+static int
+ask_basedir(char *path, int nointeract)
+{
+	int n;
+
+	if (nointeract) {
+		progerr(gettext(MSG_REQBASEDIR));
+		return (5);
+	} else {
+		path[0] = '\0';
+		if (n = ckpath(path, P_ABSOLUTE|P_DIR|P_WRITE,
+		    basedir, NULL, gettext(MSG_HELP),
+		    gettext(MSG_PROMPT)))
+			return (n);	/* FAIL */
+		orig_basedir =
+		    expand_path(path);
+	}
+	return (0);
+}
+
+/*
+ * Set the basedir and client_basedir based on install root and config
+ * files. It returns 0 if all OK otherwise returns the error code base
+ * appropriate to the problem.
+ */
+int
+set_basedirs(int reloc, char *adm_basedir, char *pkginst, int nointeract)
+{
+	char	path[PATH_MAX];
+	int	n;
+
+	relocatable = reloc;
+
+	/*
+	 * If there are no relocatable files basedir is probably meaningless
+	 * so we skip ahead to the simple tests. Otherwise we do the twisted
+	 * stuff below. The BASEDIR is set based on the following heirarchy :
+	 *	1. The entry in the admin file
+	 *	2. The entry in the pkginfo file delivered on the medium
+	 *	3. The entry in the already installed pkginfo file
+	 *	4. ask
+	 * If it's not a relocatable package, we go with whatever seems
+	 * reasonable; if it's relocatable and we've exhausted our
+	 * options, we ask.
+	 */
+	if (reloc) {
+		int is_adm_basedir = (adm_basedir && *adm_basedir);
+		int is_update = 0;
+		int is_ask = 0;
+
+		if (is_adm_basedir) {
+			if (strcmp(adm_basedir, "update") == 0) {
+				is_update = 1;
+				is_ask = 1;
+			} else if (strcmp(adm_basedir, "ask") == 0)
+				is_ask = 1;
+		}
+
+		/*
+		 * If there's a BASEDIR in the admin file & it's a valid
+		 * absolute pathname, use it.
+		 */
+		if (is_adm_basedir && strchr("/$", *adm_basedir))
+			orig_basedir = expand_path(adm_basedir);
+
+		/* If admin says 'ask regardless', ask and continue */
+		else if (is_adm_basedir && is_ask) {
+			if (n = ask_basedir(path, nointeract))
+				return (n);
+			if (is_update &&
+			    strcmp(orig_basedir,
+			    (basedir = getenv("BASEDIR"))) != 0) {
+				progerr(gettext(ERR_ASKBD),
+				    getenv("PKG"), basedir, orig_basedir);
+				quit(4);
+			}
+		}
+		/*
+		 * If it isn't the only other valid option,
+		 * namely 'default', quit FAIL.
+		 */
+		else if (is_adm_basedir &&
+		    strcmp(adm_basedir, "default") != 0) {
+			progerr(gettext(ERR_ADMIN_INVAL));
+			return (1);
+
+		/*
+		 * OK, the admin file has no preference, so we go to the
+		 * other sources.
+		 */
+		} else {
+			/*
+			 * Check to see if BASEDIR is set in the environment
+			 * (probably from the pkginfo file on the installation
+			 * medium).
+			 */
+			basedir = getenv("BASEDIR");
+			if (basedir && *basedir)
+				orig_basedir = expand_path(basedir);
+			else {
+				/*
+				 * Check to see if the package BASEDIR was
+				 * already defined during a previous
+				 * installation of this package instance. The
+				 * function below looks for an installed
+				 * pkginfo file and scans it.
+				 */
+				basedir = pkgparam(pkginst, "BASEDIR");
+				if (basedir && *basedir)
+					orig_basedir = expand_path(basedir);
+				else if (n = ask_basedir(path, nointeract))
+					return (n);
+			}
+		}
+	} else {	/* not relocatable */
+		/*
+		 * Since all paths are absolute the only reason to have a
+		 * basedir is if there's an install root meaning there's
+		 * really a basedir relative to this host or this package is
+		 * absolute only because it's sparse in which case we're
+		 * interested in the prior basedir. So we next check for a
+		 * prior basedir and then an install root.
+		 */
+		basedir = pkgparam(pkginst, "BASEDIR");
+		if (basedir && *basedir)
+			orig_basedir = expand_path(basedir);
+
+		else if (install_root_exists)
+			/*
+			 * If we have a basedir *only because*
+			 * we have an install_root, we need to
+			 * set orig_basedir to '/' to simplify
+			 * later attempts to force
+			 * client_basedir.
+			 */
+			orig_basedir = "/";
+		else {
+			eval_valid++;	/* we can run eval_path() now */
+			return (0);	/* fixpath below unnecessary */
+		}
+	}
+
+	basedir_exists = 1;
+
+	basedir = fixpath(orig_basedir);
+
+	/*
+	 * If basedir == "/" then there's no need for a "/" between
+	 * it and the rest of the path.
+	 */
+	if (strcmp(basedir, "/") == 0)
+		base_sepr = 0;
+
+	if (set_client_basedir() == 0) {
+		progerr(gettext(ERR_NO_CL_BD));
+		return (1);
+	}
+
+	eval_valid++;	/* we've confirmed the validity of everything */
+
+	return (0);
+}
+
+/*
+ * Make a directory from a path and all necessary directories above it as
+ * needed.
+ */
+int
+mkpath(char *p)
+{
+	char	*pt;
+
+	/* if entire path exists, return o.k. */
+
+	if (access(p, F_OK) == 0) {
+		return (0);
+	}
+
+	/* entire path not there - check components and create */
+
+	pt = (*p == '/') ? p+1 : p;
+	do {
+		if (pt = strchr(pt, '/')) {
+			*pt = '\0';
+		}
+		if ((access(p, F_OK) != 0) && (mkdir(p, 0755) != 0)) {
+			return (-1);
+		}
+		if (pt) {
+			*pt++ = '/';
+		}
+	} while (pt);
+
+	return (0);
+}
+
+/* This makes the required base directory if necessary */
+void
+mkbasedir(int flag, char *basedir)
+{
+	char	ans[MAX_INPUT];
+	int	n;
+
+	/*
+	 * If a base directory is called for but there's no such directory on
+	 * the system, deal with that issue.
+	 */
+	if (is_a_basedir() && isdir(basedir)) {
+		if (flag) {	/* Interaction is OK. */
+			/*
+			 * If there's a non-directory object in the way, ask.
+			 */
+			if (access(basedir, F_OK) == 0) {
+				ptext(stderr, gettext(MSG_ISAFILE), basedir);
+
+				if (n = ckyorn(ans, NULL, NULL, NULL,
+				    gettext(MSG_YORNFILE)))
+					quit(n);
+				if (strchr("yY", *ans) == NULL)
+					quit(3);
+
+				/*
+				 * It isn't a directory, so we'll just unlink
+				 * it.
+				 */
+				if (unlink(basedir) == -1) {
+					progerr(gettext(ERR_NODELETE),
+					    basedir);
+					quit(99);
+				}
+
+			} else {
+				ptext(stderr, gettext(MSG_MUSTEXIST), basedir);
+
+				if (n = ckyorn(ans, NULL, NULL, NULL,
+				    gettext(MSG_YORNPRMPT)))
+					quit(n);
+				if (strchr("yY", *ans) == NULL)
+					quit(3);
+			}
+		}
+
+		if (access(basedir, F_OK) == 0 || mkpath(basedir)) {
+			progerr(gettext(ERR_MKBASE), basedir);
+			quit(99);
+		}
+	}
+}
+
+/*
+ * Create a client_basedir if it is appropriate. If all goes well, resulting
+ * in either a valid client_basedir or a valid lack thereof, it returns 1.
+ * If there is an irreconcileable conflict, it returns 0.
+ */
+static int
+set_client_basedir(void)
+{
+	if (install_root_exists) {
+		if (basedir_exists)
+			client_basedir = strdup(orig_basedir);
+		else
+			client_basedir = "/";
+		client_basedir_exists = 1;
+	}
+
+	/*
+	 * In response to an agreement associated with bug report #1133956,
+	 * CLIENT_BASEDIR will be defined in all cases where BASEDIR is
+	 * defined until the on1094 release. For on1094 delete the else if
+	 * and associated expressions below. -- JST (6/25/1993)
+	 */
+	else if (basedir_exists) {
+		client_basedir = strdup(basedir);
+		client_basedir_exists = 1;
+	}
+
+	/*
+	 * At this point we may or may not have a client_basedir defined. Now
+	 * we need to check for one in the environment & make sure it syncs
+	 * up with prior findings. If there's no other client_basedir defined,
+	 * the environment defines it.
+	 */
+	if (env_cl_bdir && *env_cl_bdir) {
+		if (client_basedir_exists) {
+			/* If the two client basedirs mismatch, return fail */
+			if (strcmp(client_basedir, env_cl_bdir)) {
+				ptext(stderr, gettext(ERR_CL_MIS),
+				    client_basedir, env_cl_bdir);
+				return (0);
+			}
+		} else {
+			client_basedir = env_cl_bdir;
+			client_basedir_exists = 1;
+		}
+	}
+
+	return (1);
+}
+
+static char *
+expand_path(char *path)
+{
+	char	path_buf[PATH_MAX];
+
+	if (!path || !*path)
+		return (path);
+
+	(void) strlcpy(path_buf, path, sizeof (path_buf));
+	mappath(getmapmode(), path_buf);
+	canonize(path_buf);
+
+	return (qstrdup(path_buf));
+}
+
+char *
+get_basedir(void)
+{
+	return (basedir);
+}
+
+char *
+get_client_basedir(void)
+{
+	return (client_basedir);
+}
+
+/*
+ * This function returns the basedir that is appropriate for this package's
+ * pkginfo file.
+ */
+char *
+get_info_basedir(void)
+{
+	if (install_root_exists)
+		return (client_basedir);
+	else if (basedir_exists)
+		return (basedir);
+	else
+		return (NULL);
+}
+
+int
+is_an_inst_root(void)
+{
+	return (install_root_exists);
+}
+
+int
+is_a_basedir(void)
+{
+	return (basedir_exists);
+}
+
+int
+is_relocatable(void)
+{
+	return (relocatable);
+}
+
+int
+is_a_cl_basedir(void)
+{
+	return (client_basedir_exists);
+}
+
+/*
+ * Since calls to putparam() become valid long after much of the above
+ * code has run, this routine allows the insertion of these key
+ * environment variables without passing a bunch of pointers.
+ */
+void
+put_path_params(void)
+{
+	if (install_root_exists)
+		putparam("PKG_INSTALL_ROOT", get_inst_root());
+
+	if (basedir_exists)
+		putparam("BASEDIR", basedir);
+
+	if (client_basedir_exists)
+		putparam("CLIENT_BASEDIR", client_basedir);
+}
+
+/*
+ * This fills three pointers and a buffer which contains the longest
+ * possible path (with install_root and basedir prepended. The pointers
+ * are to the subpaths within the string. This was added so that the
+ * eptlist could be produced with all relevant paths defined without
+ * repeated calls and string scans. For example, given a path of
+ * haberdasher/crute we may return
+ *
+ *	server_ptr -----> /export/root/client1/opt/SUNWhab/haberdasher/crute
+ *                                            |            |
+ *	client_ptr ---------------------------             |
+ *	map_ptr -------------------------------------------
+ *
+ * We construct the new path based upon the established environment
+ * and the type of path that was passed. Here are the possibilities:
+ *
+ *   |					| relative path	| absolute path	|
+ *   |	--------------------------------|---------------|---------------|
+ *   |	is_an_inst_root			|	1	|	2	|
+ *   V	! an_inst_root && is_a_basedir	|	1	|	3	|
+ *	! an_inst_root && ! a_basedir	|	X	|	3	|
+ *
+ * METHOD
+ * 1. Prepend the basedir to the path (the basedir is guaranteed to exist
+ *	whenever there's an install_root).
+ *
+ * 2. Prepend the install_root (not the basedir) to the path
+ *
+ * 3. Return the path as unchanged.
+ *
+ * X. THIS CAN'T HAPPEN
+ */
+int
+eval_path(char **server_ptr, char **client_ptr, char **map_ptr, char *path)
+{
+	static int client_offset;
+	static int offsets_valid, retcode;
+	int path_size;
+
+	if (!offsets_valid) {
+		/*
+		 * This is the offset from the beginning of the evaluated
+		 * path to the start of the relative path. Note that we
+		 * are accounting for the '/' inserted between the
+		 * basedir and the path with the '+ 1'. If there is a
+		 * relative path, then there is always a basedir. The
+		 * only way this will come up '0' is if this is an
+		 * absolute package.
+		 */
+		orig_offset_rel = (is_a_basedir()) ? (strlen(basedir) +
+		    base_sepr) : 0;
+
+		/*
+		 * This is the position of the client-relative path
+		 * in that it points to the '/' beginning the base
+		 * directory or the absolute path. Once the basedir has
+		 * been afixed, the path is absolute. For that reason,
+		 * the client path is the same thing as the original path
+		 * if it were absolute.
+		 */
+		client_offset = (is_an_inst_root()) ? install_root_len : 0;
+
+		offsets_valid = 1;
+	}
+
+	/*
+	 * If we've evaluated the base directory and come up trumps,
+	 * then we can procede with this operation, otherwise, the
+	 * available data is too ambiguous to resolve the issue.
+	 */
+	if (eval_valid) {
+		if (RELATIVE(path)) {
+			if (relocatable) {
+				/*
+				 * Figure out how long our buffer will
+				 * have to be.
+				 */
+				path_size = orig_offset_rel + strlen(path);
+
+				(*server_ptr) = pathalloc(path_size);
+
+				*client_ptr = *server_ptr + client_offset;
+
+				if (map_ptr)
+					*map_ptr = *server_ptr +
+					    orig_offset_rel;
+
+				/* LINTED warning: variable format specifier */
+				(void) snprintf(*server_ptr, path_size+1,
+					rel_fmt[base_sepr], basedir, path);
+			} else {
+				ptext(stderr, gettext(ERR_RELINABS), path);
+				retcode = 0;
+			}
+		} else {	/* NOT RELATIVE */
+			*server_ptr = fixpath_dup(path);
+
+			if ((*client_ptr = *server_ptr + client_offset) == NULL)
+				*client_ptr = "/";
+
+			if (map_ptr)
+				*map_ptr = *client_ptr;
+		}
+
+		retcode = 1;
+	} else {
+		ptext(stderr, gettext(ERR_AMBDIRS));
+		retcode = 0;
+	}
+
+	return (retcode);
+}
+
+void
+export_client_env(char *root_path)
+{
+	char	*inst_release_path;
+	char	*key;
+	char	*value;
+	FILE	*inst_fp;
+	size_t	len;
+
+	/*
+	 * Put the variables found in a clients INST_RELEASE file into the
+	 * package environment so procedure scripts can know what
+	 * release/version/revision a client is running. Also this function
+	 * doesn't return state since the INST_RELEASE file may not exist in
+	 * some package installation environments
+	 */
+
+	len = strlen(root_path) + strlen(INST_RELEASE) + 2;
+
+	inst_release_path = (char *)malloc(len);
+
+	key = (char *)malloc(PATH_MAX);
+
+	(void) snprintf(inst_release_path, len, "%s/%s", root_path,
+				INST_RELEASE);
+
+	if ((inst_fp = fopen(inst_release_path, "r")) != NULL) {
+		while (value = fpkgparam(inst_fp, key)) {
+			if (strcmp(key, "OS") == 0) {
+				putparam("PKG_CLIENT_OS", value);
+			} else if (strcmp(key, "VERSION") == 0) {
+				putparam("PKG_CLIENT_VERSION", value);
+			} else if (strcmp(key, "REV") == 0) {
+				putparam("PKG_CLIENT_REVISION", value);
+			}
+			*key = '\0';
+		}
+		(void) fclose(inst_fp);
+	}
+	free(inst_release_path);
+	free(key);
+}
+
+/*
+ * Increment variable indicating the installation is from a partially spooled
+ * package.
+ */
+void
+set_partial_inst(void)
+{
+	partial_inst++;
+}
+
+/*
+ * Return variable indicating that the installation is from a partially spooled
+ * package.
+ * Returns:  !0 for true
+ *           0 for false
+ */
+int
+is_partial_inst(void)
+{
+	return (partial_inst);
+}
+
+/*
+ * Increment variable indicating that only the depend and pkginfo DB's are to be
+ * updated
+ */
+
+void
+set_depend_pkginfo_DB(boolean_t a_setting)
+{
+	depend_pkginfo_DB = a_setting;
+}
+
+/*
+ * Return variable indicating that the installation only updates the depend
+ * and pkginfo DB's.
+ * Returns:  !0 for true
+ *           0 for false
+ */
+
+boolean_t
+is_depend_pkginfo_DB(void)
+{
+	return (depend_pkginfo_DB);
+}
+
+/*
+ * Increment variable indicating that packages should not be spooled in
+ * var/sadm/pkg/<pkgabbrev>/save/pspool/
+ */
+void
+disable_spool_create(void)
+{
+	partial_spool_create++;
+}
+
+/*
+ * Return variable indicating whether or not the partial spool directory
+ * should be created.
+ * Returns:  1 for true
+ *           0 for false
+ */
+int
+is_spool_create(void)
+{
+	return (partial_spool_create);
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/usr/src/cmd/svr4pkg/libinst/flex_dev.c	Fri Jun 05 10:28:40 2009 -0400
@@ -0,0 +1,87 @@
+/*
+ * 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
+ * or http://www.opensolaris.org/os/licensing.
+ * 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 1993 Sun Microsystems, Inc.  All rights reserved.
+ * Use is subject to license terms.
+ */
+
+
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <limits.h>
+#include <string.h>
+#include <errno.h>
+#include <libintl.h>
+#include <pkglib.h>
+#include "libadm.h"
+
+#define	ERR_CHDIR	"unable to chdir back to <%s>, errno=%d"
+#define	ERR_GETCWD	"unable to determine the current working directory, " \
+			"errno=%d"
+
+char *
+flex_device(char *device_name, int dev_ok)
+{
+	char		new_device_name[PATH_MAX];
+	char		*np = device_name;
+	char		*cwd = NULL;
+
+	if (!device_name || !*device_name)		/* NULL or empty */
+		return (np);
+
+	if (dev_ok == 1 && listdev(np) != (char **) NULL) /* device.tab */
+		return (np);
+
+	if (!strncmp(np, "/dev", 4))			/* /dev path */
+		return (np);
+
+	if ((cwd = getcwd(NULL, PATH_MAX)) == NULL) {
+		progerr(gettext(ERR_GETCWD), errno);
+		exit(99);
+	}
+
+	if (realpath(np, new_device_name) == NULL) {	/* path */
+		if (chdir(cwd) == -1) {
+			progerr(gettext(ERR_CHDIR), cwd, errno);
+			(void) free(cwd);
+			exit(99);
+		}
+		if (*np != '/' && dev_ok == 2) {
+			(void) sprintf(new_device_name, "%s/%s", cwd, np);
+			canonize(new_device_name);
+			if ((np = strdup(new_device_name)) == NULL)
+				np = device_name;
+		}
+		(void) free(cwd);
+		return (np);
+	}
+
+	if (strcmp(np, new_device_name)) {
+		if ((np = strdup(new_device_name)) == NULL)
+			np = device_name;
+	}
+
+	(void) free(cwd);
+	return (np);
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/usr/src/cmd/svr4pkg/libinst/is_local_host.c	Fri Jun 05 10:28:40 2009 -0400
@@ -0,0 +1,151 @@
+/*
+ * 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
+ * or http://www.opensolaris.org/os/licensing.
+ * 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 2004 Sun Microsystems, Inc.  All rights reserved.
+ * Use is subject to license terms.
+ */
+
+
+
+#include <stdio.h>
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <netdb.h>
+#include <unistd.h>
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <sys/sockio.h>
+#include <net/if.h>
+#include <netinet/in.h>
+#include <arpa/inet.h>
+#include <errno.h>
+#include <stdlib.h>
+#include <string.h>
+#include <libintl.h>
+
+static int	is_local_if(struct hostent *hp);
+
+/*
+ * Given a host name, check to see if it points to the local host.
+ * If it does, return 1, else return 0.
+ *
+ * The strategy is this:  translate the host name argument to a list of
+ * addresses.  Then compare each of those addresses to the addresses of
+ * network interfaces on this host.
+ */
+int
+is_local_host(char *host)
+{
+	struct hostent	*hp;
+	int		err;
+	int		flags = AI_DEFAULT;
+
+	if (hp = getipnodebyname((const char *) host, AF_INET, flags, &err))
+		if (is_local_if(hp))
+			return (1);
+	if (hp = getipnodebyname((const char *) host, AF_INET6, flags, &err))
+		if (is_local_if(hp))
+			return (1);
+
+	return (0);
+}
+
+static int
+is_local_if(struct hostent *hp)
+{
+	char		*buf;
+	struct lifconf	lifc;
+	struct lifnum	lifn;
+	struct lifreq	lifr;
+	struct lifreq	*lifrp;
+	int		bufsiz;
+	int		nha;
+	int		nif;
+	int		s;
+
+	if ((s = socket(hp->h_addrtype, SOCK_DGRAM, 0)) == -1) {
+		perror("socket");
+		return (0);
+	}
+
+	lifn.lifn_family = hp->h_addrtype;
+	lifn.lifn_flags = LIFC_EXTERNAL_SOURCE;
+	if (ioctl(s, SIOCGLIFNUM, (char *)&lifn) == -1) {
+		perror("SIOCGLIFNUM");
+		(void) close(s);
+		return (0);
+	}
+	bufsiz = lifn.lifn_count * sizeof (struct lifreq);
+
+	if ((buf = malloc(bufsiz)) == NULL) {
+		perror("malloc");
+		(void) close(s);
+		return (0);
+	}
+
+	lifc.lifc_family = hp->h_addrtype;
+	lifc.lifc_flags = LIFC_EXTERNAL_SOURCE;
+	lifc.lifc_len = bufsiz;
+	lifc.lifc_buf = buf;
+	if (ioctl(s, SIOCGLIFCONF, (char *)&lifc) == -1) {
+		perror("SIOCGLIFCONF");
+		(void) close(s);
+		free(buf);
+		return (0);
+	}
+
+#define	lifraddrp(lifrp) ((lifrp->lifr_addr.ss_family == AF_INET6) ? \
+	(void *) &((struct sockaddr_in6 *)&lifrp->lifr_addr)->sin6_addr : \
+	(void *) &((struct sockaddr_in *)&lifrp->lifr_addr)->sin_addr)
+
+	for (lifrp = lifc.lifc_req,
+	    nif = lifc.lifc_len / sizeof (struct lifreq);
+	    nif > 0; nif--, lifrp++) {
+		if (lifrp->lifr_addr.ss_family != hp->h_addrtype) {
+			continue;
+		}
+		(void) memset(&lifr, 0, sizeof (lifr));
+		(void) strncpy(lifr.lifr_name, lifrp->lifr_name,
+		    sizeof (lifr.lifr_name));
+		if (ioctl(s, SIOCGLIFFLAGS, (caddr_t)&lifr) == -1) {
+			perror("SIOCGLIFFLAGS");
+			(void) close(s);
+			free(buf);
+			return (0);
+		}
+
+		for (nha = 0; hp->h_addr_list[nha]; nha++) {
+			if (memcmp(hp->h_addr_list[nha], lifraddrp(lifrp),
+			    hp->h_length) == 0) {
+				(void) close(s);
+				free(buf);
+				return (1);
+			}
+		}
+	}
+
+#undef	lifraddrp
+
+	(void) close(s);
+	free(buf);
+	return (0);
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/usr/src/cmd/svr4pkg/libinst/isreloc.c	Fri Jun 05 10:28:40 2009 -0400
@@ -0,0 +1,243 @@
+/*
+ * 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
+ * or http://www.opensolaris.org/os/licensing.
+ * 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 2009 Sun Microsystems, Inc.  All rights reserved.
+ * Use is subject to license terms.
+ */
+
+
+
+#include <stdio.h>
+#include <sys/types.h>
+#include <sys/param.h>
+#include <dirent.h>
+#include <limits.h>
+#include <errno.h>
+#include <ctype.h>
+#include <fcntl.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+#include <pkglib.h>
+#include <libintl.h>
+#include <libinst.h>
+#include <install.h>
+
+#define	ERR_NOPKGMAP	"Cannot open pkgmap file."
+
+#define	ENTRY_MAX (PATH_MAX + 38)
+#define	IGNORE_START	":#!"
+#define	IGNORE_TYPE	"i"
+
+static int	has_rel_path(char *entry);
+static int	is_relative(char *entry);
+
+/*
+ * This routine attempts to determine with certainty whether or not
+ * the package is relocatable or not. It first attempts to determine if
+ * there is a reloc directory by scanning pkginstdir. If that fails to
+ * provide a definite result (pkg is coming from a stream device and
+ * the directories aren't in place) it inspects the pkgmap in pkginstdir
+ * in order to determine if the package has relocatable elements. If
+ * there is a single relative pathname or $BASEDIR/... construct,
+ * this returns 1. If no relative pathnames are found it returns 0
+ * meaning absolute package and all the things that implies.
+ *
+ * This does not determine the validity of the pkgmap file. If the pkgmap
+ * is corrupted, this returns 0.
+ */
+int
+isreloc(char *pkginstdir)
+{
+	FILE	*pkg_fp;
+	struct	dirent *drp;
+	DIR	*dirfp;
+	int	retcode = 0;
+
+	/* First look in the directory */
+	if ((dirfp = opendir(pkginstdir)) != NULL) {
+		while ((drp = readdir(dirfp)) != NULL) {
+			if (drp->d_name[0] == '.')
+				continue;
+			if (strlen(drp->d_name) < (size_t)5)
+				continue;
+			if (strncmp(drp->d_name, "reloc", 5) == 0) {
+				retcode = 1;
+				break;
+			}
+		}
+		(void) closedir(dirfp);
+	}
+
+	/*
+	 * If retcode == 0, meaning we didn't find a reloc directory then we
+	 * probably don't have a complete directory structure available to
+	 * us. We'll have to determine what type of package it is by scanning
+	 * the pkgmap file.
+	 */
+	if (retcode == 0) {
+		char	path_buffer[ENTRY_MAX];
+
+		(void) snprintf(path_buffer, sizeof (path_buffer),
+						"%s/pkgmap", pkginstdir);
+
+		canonize(path_buffer);
+
+		if ((pkg_fp = fopen(path_buffer, "r")) != NULL) {
+			while (fgets(path_buffer, sizeof (path_buffer), pkg_fp))
+				if (has_rel_path(path_buffer)) {
+					retcode = 1;
+					break;
+				}
+			(void) fclose(pkg_fp);
+		} else {
+			progerr(gettext(ERR_NOPKGMAP));
+			quit(99);
+		}
+	}
+
+	return (retcode);
+}
+
+/*
+ * Test the string for the presence of a relative path. If found, return
+ * 1 otherwise return 0. If we get past the IGNORE_TYPE test, we're working
+ * with a line of the form :
+ *
+ *	dpart type classname pathname ...
+ *
+ * It's pathname we're going to test here.
+ *
+ * Yes, yes, I know about sscanf(); but, I don't need to reserve 4K of
+ * space and parse the whole string, I just need to get to two tokens.
+ * We're in a hurry.
+ */
+static int
+has_rel_path(char *entry)
+{
+	register int entry_pos = 1;
+
+	/* If the line is a comment or special directive, return 0 */
+	if (*entry == NULL || strchr(IGNORE_START, *entry))
+		return (0);
+
+	/* Skip past this data entry if it is volume number. */
+	if (isdigit(*entry)) {
+		while (*entry && !isspace(*entry)) {
+			entry++;
+		}
+	}
+
+	/* Skip past this white space */
+	while (*entry && isspace(*entry)) {
+		entry++;
+	}
+
+	/*
+	 * Now we're either pointing at the type or we're pointing at
+	 * the termination of a degenerate entry. If the line is degenerate
+	 * or the type indicates this line should be ignored, we return
+	 * as though not relative.
+	 */
+	if (*entry == NULL || strchr(IGNORE_TYPE, *entry))
+		return (0);
+
+	/* The pathname is in the third position */
+	do {
+		/* Skip past this data entry */
+		while (*entry && !isspace(*entry)) {
+			entry++;
+		}
+
+		/* Skip past this white space and call this the next entry */
+		while (*entry && isspace(*entry)) {
+			entry++;
+		}
+	} while (++entry_pos < 3 && *entry != NULL);
+
+	/*
+	 * Now we're pointing at the first character of the pathname.
+	 * If the file is corrupted, we're pointing at NULL. is_relative()
+	 * will return FALSE for NULL which will yield the correct return
+	 * value.
+	 */
+	return (is_relative(entry));
+}
+
+/*
+ * If the path doesn't begin with a variable, the first character in the
+ * path is tested for '/' to determine if it is absolute or not. If the
+ * path begins with a '$', that variable is resolved if possible. If it
+ * isn't defined yet, we exit with error code 1.
+ */
+static int
+is_relative(char *entry)
+{
+	register char *eopath = entry;	/* end of full pathname pointer */
+	register char **lasts = &entry;
+
+	/* If there is a path, test it */
+	if (entry && *entry) {
+		if (*entry == '$') {	/* it's an environment parameter */
+			entry++;	/* skip the '$' */
+
+			while (*eopath && !isspace(*eopath))
+				eopath++;
+
+			*eopath = '\0';	/* terminate the pathname */
+
+			/* isolate the variable */
+			entry = strtok_r(entry, "/", lasts);
+
+			/*
+			 * Some packages call out $BASEDIR for relative
+			 * paths in the pkgmap even though that is
+			 * redundant. This special case is actually
+			 * an indication that this is a relative
+			 * path.
+			 */
+			if (strcmp(entry, "BASEDIR") == 0)
+				return (1);
+			/*
+			 * Since entry is pointing to a now-expendable PATH_MAX
+			 * size buffer, we can expand the path variable into it
+			 * here.
+			 */
+			entry = getenv(entry);
+		}
+
+		/*
+		 * Return type of path. If pathname was unresolvable
+		 * variable, assume relative. This looks like a strange
+		 * assumption since the resolved path may end up
+		 * absolute and pkgadd may prompt the user for a basedir
+		 * incorrectly because of this assumption. Unfortunately,
+		 * the request script MUST have a final BASEDIR in the
+		 * environment before it executes.
+		 */
+		if (entry && *entry)
+			return (RELATIVE(entry));
+		else
+			return (1);
+	} else		/* no path, so we skip it */
+		return (0);
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/usr/src/cmd/svr4pkg/libinst/listmgr.c	Fri Jun 05 10:28:40 2009 -0400
@@ -0,0 +1,564 @@
+/*
+ * 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
+ * or http://www.opensolaris.org/os/licensing.
+ * 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 1996 Sun Microsystems, Inc.  All rights reserved.
+ * Use is subject to license terms.
+ */
+
+
+#include <stdio.h>
+#include <string.h>
+#include <stdlib.h>
+#include <libintl.h>
+#include "pkglib.h"
+
+/*
+ * This is the module responsible for allocating and maintaining lists that
+ * require allocation of memory. For certain lists, large chunks are
+ * allocated once to contain a large number of entries in each chunk (bl_*
+ * for block list). The other approach involves the augmentation of linked
+ * lists, each entry of which is alloc'd individually.
+ */
+#define	ERR_CS_ALLOC	"ERROR: Cannot allocate control structure for %s array."
+#define	ERR_MEM_ALLOC	"ERROR: Cannot allocate memory for %s array."
+
+#define	MAX_ARRAYS	50
+
+#define	ARRAY_END(x)	(bl_cs_array[x]->cur_segment->avail_ptr)
+#define	REC_SIZE(x)	(bl_cs_array[x]->struct_size)
+#define	EOSEG(x)	(bl_cs_array[x]->cur_segment->eoseg_ptr)
+#define	GET_AVAIL(x)	(ARRAY_END(x) + REC_SIZE(x))
+
+struct alloc_seg {
+	char *seg_ptr;		/* ptr to the allocated block */
+	char *avail_ptr;	/* ptr to the next available list element */
+	char *eoseg_ptr;	/* last byte in the segment */
+	int full;		/* segment has no available space */
+	struct alloc_seg *next;	/* next record */
+};
+
+struct blk_list_cs {
+	int struct_size;		/* size of a single list element */
+	int count_per_block;		/* number of list elements per block */
+	int block_size;			/* just to save time - alloc size */
+	int data_handle;		/* list_handle for pointer array */
+	struct alloc_seg *alloc_segs;	/* memory pool */
+
+	struct alloc_seg *cur_segment;	/* the current allocated segment */
+	int total_elem;			/* total elements stored */
+	int contiguous;			/* use realloc to grow */
+	char *desc;			/* description of the list */
+};
+
+static struct blk_list_cs *bl_cs_array[MAX_ARRAYS];
+static int next_array_elem;
+
+/* Support functions */
+static int
+invalid_handle(int list_handle)
+{
+	if (list_handle < 0 || list_handle >= next_array_elem)
+		return (1);
+
+	return (0);
+}
+
+static int
+invalid_record(int list_handle, int recno)
+{
+	if (invalid_handle(list_handle))
+		return (1);
+
+	if (recno < 0 || recno > bl_cs_array[list_handle]->total_elem)
+		return (1);
+
+	return (0);
+}
+
+static void
+free_list(int list_handle)
+{
+	struct blk_list_cs *bl_ptr;
+	struct alloc_seg *segstr_ptr, *nextstr_ptr;
+
+	/* Make sure this wasn't free'd earlier */
+	if (bl_cs_array[list_handle] == NULL)
+		return;
+
+	bl_ptr = bl_cs_array[list_handle];
+
+	/* First free the alloc_seg list. */
+	segstr_ptr = bl_ptr->alloc_segs;
+
+	if (segstr_ptr) {
+		do {
+			nextstr_ptr = segstr_ptr->next;
+
+			/* Free the memory block. */
+			free((void *)segstr_ptr->seg_ptr);
+
+			/* Free the control structure. */
+			free((void *)segstr_ptr);
+			segstr_ptr = nextstr_ptr;
+		} while (segstr_ptr);
+	}
+
+	/* Free the block control structure. */
+	free((void *)bl_ptr->desc);
+	free((void *)bl_ptr);
+
+	bl_cs_array[list_handle] = NULL;
+}
+
+/* Allocate another alloc_seg structure. */
+static int
+alloc_next_seg(struct blk_list_cs *bl_ptr)
+{
+	struct alloc_seg *new_alloc_cs;
+
+	if (bl_ptr->contiguous) {
+		int offset_to_avail, seg_size, new_size;
+		struct alloc_seg *alloc_segment;
+
+		if (bl_ptr->alloc_segs) {
+			alloc_segment = bl_ptr->alloc_segs;
+
+			offset_to_avail = (alloc_segment->avail_ptr -
+			    alloc_segment->seg_ptr);
+			seg_size = (alloc_segment->eoseg_ptr -
+			    alloc_segment->seg_ptr);
+			new_size = (seg_size + bl_ptr->block_size);
+		} else {
+			if ((bl_ptr->alloc_segs =
+			    (struct alloc_seg *)calloc(1,
+			    sizeof (struct alloc_seg))) == NULL) {
+				logerr(gettext(ERR_CS_ALLOC), (bl_ptr->desc ?
+				    bl_ptr->desc : "an unknown"));
+				return (0);
+			}
+
+			alloc_segment = bl_ptr->alloc_segs;
+
+			offset_to_avail = 0;
+			seg_size = 0;
+			new_size = bl_ptr->block_size;
+		}
+
+		bl_ptr->cur_segment = alloc_segment;
+
+		if ((alloc_segment->seg_ptr =
+		    (char *)realloc((void *)alloc_segment->seg_ptr,
+		    (unsigned)new_size)) == NULL) {
+			logerr(gettext(ERR_MEM_ALLOC), (bl_ptr->desc ?
+			    bl_ptr->desc : "an unknown"));
+			return (0);
+		}
+
+		alloc_segment->next = NULL;
+
+		/* reset the status */
+		alloc_segment->full = 0;
+
+		/* readjust the original pointers */
+		alloc_segment->avail_ptr = alloc_segment->seg_ptr +
+		    offset_to_avail;
+		alloc_segment->eoseg_ptr = alloc_segment->seg_ptr + new_size;
+
+		(void) memset(alloc_segment->avail_ptr, '\000',
+		    bl_ptr->block_size);
+	} else {
+		/* Allocate the control structure and link it into the list. */
+		if ((new_alloc_cs = (struct alloc_seg *)malloc(
+		    sizeof (struct alloc_seg))) == NULL) {
+			logerr(gettext(ERR_CS_ALLOC), (bl_ptr->desc ?
+			    bl_ptr->desc : "an unknown"));
+			return (0);
+		}
+
+		if (bl_ptr->alloc_segs == NULL) {
+			/*
+			 * If this is the first allocation, then initialize
+			 * the head pointer and set cur_segment to this first
+			 * block of memory.
+			 */
+			bl_ptr->alloc_segs = new_alloc_cs;
+		} else {
+			/*
+			 * Otherwise, point the current cur_segment to the
+			 * next one and then point to the new one.
+			 */
+			bl_ptr->cur_segment->next = new_alloc_cs;
+		}
+
+		new_alloc_cs->next = NULL;
+		bl_ptr->cur_segment = new_alloc_cs;
+
+		new_alloc_cs->full = 0;
+
+		/* Now allocate the block of memory that this controls. */
+		if ((new_alloc_cs->seg_ptr = calloc(bl_ptr->count_per_block,
+		    bl_ptr->struct_size)) == NULL) {
+			logerr(gettext(ERR_MEM_ALLOC), (bl_ptr->desc ?
+			    bl_ptr->desc : "an unknown"));
+			return (0);
+		}
+
+		new_alloc_cs->avail_ptr = new_alloc_cs->seg_ptr;
+		new_alloc_cs->eoseg_ptr = (new_alloc_cs->seg_ptr +
+		    bl_ptr->block_size);
+	}
+
+	return (1);
+}
+
+/*
+ * These first functions (beginning with bl_*) manage simple block lists. The
+ * pointers returned, may get lost if they aren't assigned to an array or
+ * something. While individual records can be obtained by record number, the
+ * process isn't very efficient. Look to the array management section
+ * (ar_*)for an easily administrable list.
+ */
+
+/*
+ * Create a block list. Allocate memory for a block list structure and
+ * initialize that structure. This doesn't actually allocate memory for the
+ * list yet, just the controlling data structure. Returns -1 on failure and a
+ * valid block list handle otherwise.
+ *
+ * NOTE: At the time of writing, it was not seen as important to recover block
+ * pointers made available with a bl_free() (two of these at most in
+ * pkginstall). If this became important later, we could trade efficiency for
+ * speed by ignoring next_array_elem and actually scanning through the array
+ * for a NULL pointer and then return that.
+ */
+int
+bl_create(int count_per_block, int struct_size, char *desc)
+{
+	struct blk_list_cs *bl_ptr;
+	int retval;
+
+	if ((bl_cs_array[next_array_elem] =
+	    (struct blk_list_cs *)calloc(1, sizeof (struct blk_list_cs))) ==
+	    NULL) {
+		logerr(gettext(ERR_CS_ALLOC), (desc ? desc : "an unknown"));
+		return (-1);
+	}
+
+	bl_ptr = bl_cs_array[next_array_elem];
+	retval = next_array_elem++;
+
+	bl_ptr->data_handle = -1;
+	bl_ptr->struct_size = struct_size;
+	bl_ptr->count_per_block = count_per_block;
+	bl_ptr->block_size = (count_per_block * struct_size);
+	bl_ptr->desc = strdup((desc ? desc : "unknown"));
+
+	return (retval);
+}
+
+/*
+ * Get the next available entry in the list. This will allocate memory as
+ * required based on the initialization values in bl_create(). Returns a
+ * pointer to the allocated memory segment or NULL if operation was not
+ * possible.
+ */
+char *
+bl_next_avail(int list_handle)
+{
+	struct blk_list_cs *bl_ptr;
+	char *retval;
+
+	if (invalid_handle(list_handle))
+		return (NULL);
+
+	bl_ptr = bl_cs_array[list_handle];
+
+	/*
+	 * Allocate more memory if none is allocated yet or our last access
+	 * filled the allotted segment.
+	 */
+	if (bl_ptr->cur_segment == NULL || bl_ptr->cur_segment->full)
+		if (!alloc_next_seg(bl_ptr))
+			return (NULL);
+
+	/* Get the correct pointer. */
+	retval = bl_ptr->cur_segment->avail_ptr;
+
+	/* Advance it and mark if full. */
+	bl_ptr->cur_segment->avail_ptr += bl_ptr->struct_size;
+	bl_ptr->total_elem++;
+
+	if (bl_ptr->cur_segment->avail_ptr >= bl_ptr->cur_segment->eoseg_ptr)
+		bl_ptr->cur_segment->full = 1;
+
+	return (retval);
+}
+
+char *
+bl_get_record(int list_handle, int recno)
+{
+	struct blk_list_cs *bl_ptr;
+	struct alloc_seg *cur_as_ptr;
+	int cur_rec = 0;
+
+	if (invalid_record(list_handle, recno))
+		return (NULL);
+
+	bl_ptr = bl_cs_array[list_handle];
+
+	cur_as_ptr = bl_ptr->alloc_segs;
+
+	while (recno > (cur_rec + bl_ptr->count_per_block)) {
+		cur_as_ptr = cur_as_ptr->next;
+
+		if (cur_as_ptr == NULL)
+			return (NULL);
+
+		cur_rec += bl_ptr->count_per_block;
+	}
+
+	/*
+	 * Now cur_as_ptr points to the allocated segment bearing the
+	 * intended record and all we do now is move down that by the
+	 * remaining record lengths.
+	 */
+
+	return ((char *)cur_as_ptr + ((recno - cur_rec) * bl_ptr->struct_size));
+}
+
+void
+bl_free(int list_handle)
+{
+	int cur_handle;
+
+	if (list_handle == -1) {
+		for (cur_handle = 0; cur_handle < next_array_elem;
+		    cur_handle++) {
+			free_list(cur_handle);
+		}
+	} else {
+		if (invalid_handle(list_handle))
+			return;
+
+		free_list(list_handle);
+	}
+}
+
+/*
+ * These are the array management functions. They insert into (and can return
+ * a pointer to) a contiguous list of pointers to stuff. This keeps
+ * everything together in a very handy package and is very similar in
+ * appearance to the arrays created by the old AT&T code. The method for
+ * presenting the interface is entirely different, however.
+ */
+
+/*
+ * This constructs, maintains and returns pointers into a growable array of
+ * pointers to structures of the form
+ *	struct something *array[n]
+ * The last element in the array is always NULL.
+ */
+int
+ar_create(int count_per_block, int struct_size, char *desc)
+{
+	int data_handle, retval;
+	char ar_desc[60];
+	struct blk_list_cs *array_ptr;
+
+	if ((data_handle = bl_create(count_per_block, struct_size, desc)) == -1)
+		return (-1);
+
+	sprintf(ar_desc, "%s pointer", desc);
+	if ((retval = bl_create(count_per_block, sizeof (char *),
+	    ar_desc)) == -1)
+		return (-1);
+
+	array_ptr = bl_cs_array[retval];
+
+	array_ptr->contiguous = 1;
+	array_ptr->data_handle = data_handle;
+
+	return (retval);
+}
+
+/* Return a pointer to the first element in the array. */
+char **
+ar_get_head(int list_handle)
+{
+	if (invalid_handle(list_handle) ||
+	    bl_cs_array[list_handle]->alloc_segs == NULL)
+		return (NULL);
+
+	return ((char **)bl_cs_array[list_handle]->alloc_segs->seg_ptr);
+}
+
+/*
+ * Free up the entry in the array indicated by index, but hold onto it for
+ * future use.
+ */
+int
+ar_delete(int list_handle, int index)
+{
+	char **array;
+	char *deleted_rec;
+	int i;
+	struct blk_list_cs *list_ptr, *data_ptr;
+
+	if ((array = ar_get_head(list_handle)) == NULL)
+		return (0);
+
+	if (invalid_record(list_handle, index))
+		return (0);
+
+	/* Get the pointer to the array control structure. */
+	list_ptr = bl_cs_array[list_handle];
+
+	if (!(list_ptr->contiguous))
+		return (0);	/* This isn't an array. */
+
+	data_ptr = bl_cs_array[list_ptr->data_handle];
+
+	/*
+	 * Since this looks just like an array. Record the pointer being
+	 * deleted for insertion into the avail list at the end and move all
+	 * elements below it up one.
+	 */
+	deleted_rec = array[index];
+
+	for (i = index; array[i] != NULL; i++)
+		array[i] = array[i+1];
+
+	/*
+	 * Now insert the deleted entry into the avails list after the NULL
+	 * and adjust the avail_ptr to point to the NULL again.
+	 */
+	array[i] = deleted_rec;
+	list_ptr->alloc_segs->avail_ptr -= list_ptr->struct_size;
+
+	/* Adjust other entries in the control structure. */
+	list_ptr->alloc_segs->full = 0;
+	list_ptr->total_elem -= 1;
+
+	/* Clear the deleted data area. */
+	(void) memset(deleted_rec, '\000', data_ptr->struct_size);
+
+	return (1);
+}
+
+/*
+ * Return a new pointer to a structure pointer. Find an available element in
+ * the array and point it at an available element in the data pool
+ * constructed of block lists. Allocate new memory as necessary.
+ */
+char **
+ar_next_avail(int list_handle)
+{
+	struct blk_list_cs *array_ptr;
+	char *data_area, **pointer_area;
+
+	if (invalid_handle(list_handle) ||
+	    !(bl_cs_array[list_handle]->contiguous) ||
+	    invalid_handle(bl_cs_array[list_handle]->data_handle))
+		return (NULL);
+
+	array_ptr = bl_cs_array[list_handle];
+
+	/*
+	 * First see if an avail has already been allocated (it will be right
+	 * after the NULL termination of the array if it exists). Return
+	 * that, if found.
+	 */
+	if ((bl_cs_array[list_handle]->cur_segment != NULL) &&
+	    (ARRAY_END(list_handle) + REC_SIZE(list_handle) <
+	    EOSEG(list_handle)) &&
+	    (*(pointer_area = (char **) GET_AVAIL(list_handle)) != NULL)) {
+		/* We can reclaim a previous deletion. */
+		data_area = *pointer_area;
+
+		*(char **)(ARRAY_END(list_handle)) = data_area;	/* reactivate */
+		*pointer_area-- = NULL;	/* new end */
+
+		array_ptr->cur_segment->avail_ptr += array_ptr->struct_size;
+		array_ptr->total_elem++;
+	} else {
+		/*
+		 * Get the data area first. This is the record we're pointing
+		 * to from the array.
+		 */
+		data_area = bl_next_avail(array_ptr->data_handle);
+
+		/* Now get the next pointer from the pointer array. */
+		pointer_area = (char **) bl_next_avail(list_handle);
+
+		*pointer_area = data_area;
+
+		/*
+		 * The array must be NULL terminated. So, if the block list
+		 * structure is full, we have to grow it without resetting
+		 * the avail pointer. NOTE: This will only work for a
+		 * contiguous list!
+		 */
+		if (bl_cs_array[list_handle]->alloc_segs->full) {
+			char **old_list_pointer, **new_list_pointer;
+
+			/*
+			 * First grab the old numbers in case realloc() moves
+			 * everything.
+			 */
+			old_list_pointer = ar_get_head(list_handle);
+
+			/*
+			 * Now allocate additional contiguous memory, moving
+			 * the original block if necessary.
+			 */
+			if (!alloc_next_seg(array_ptr))
+				return (NULL);
+
+			/*
+			 * Now determine if everything moved and readjust the
+			 * pointer_area if required.
+			 */
+			new_list_pointer = ar_get_head(list_handle);
+
+			if (old_list_pointer != new_list_pointer) {
+				pointer_area += (new_list_pointer -
+				    old_list_pointer);
+			}
+		}
+	}
+
+	return (pointer_area);
+}
+
+/*
+ * Relinquish the array back to the memory pool. Note that there is no method
+ * provided to free *all* arrays.
+ */
+void
+ar_free(int list_handle)
+{
+	if (invalid_handle(list_handle))
+		return;
+
+	bl_free(bl_cs_array[list_handle]->data_handle);
+	bl_free(list_handle);
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/usr/src/cmd/svr4pkg/libinst/lockinst.c	Fri Jun 05 10:28:40 2009 -0400
@@ -0,0 +1,279 @@
+/*
+ * 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
+ * or http://www.opensolaris.org/os/licensing.
+ * 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 2005 Sun Microsystems, Inc.  All rights reserved.
+ * Use is subject to license terms.
+ */
+
+/* Copyright (c) 1984, 1986, 1987, 1988, 1989 AT&T */
+/* All Rights Reserved */
+
+
+#include <stdio.h>
+#include <limits.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <fcntl.h>
+#include <errno.h>
+#include <pkglocs.h>
+#include <locale.h>
+#include <libintl.h>
+#include <string.h>
+#include <signal.h>
+#include <sys/types.h>
+#include <sys/signal.h>
+#include <sys/fault.h>
+#include <sys/syscall.h>
+#include <pkglib.h>
+#include "libadm.h"
+
+extern int errno;
+
+#define	ST_QUIT	1
+#define	ST_OK	0
+
+#define	LOCKFILE	".lockfile"
+#define	LCKBUFSIZ	128
+#define	LOCKWAIT	20	/* seconds between retries */
+#define	LOCKRETRY	10	/* number of retries for a DB lock */
+#define	LF_SIZE		128	/* size of governing lock file */
+
+#define	MSG_WTING	"NOTE: Waiting for access to the package database."
+#define	MSG_XWTING	"NOTE: Waiting for exclusive access to the package " \
+			    "database."
+#define	MSG_WTFOR	"NOTE: Waiting for %s of %s to complete."
+#define	WRN_CLRLOCK	"WARNING: Stale lock installed for %s, pkg %s quit " \
+			    "in %s state."
+#define	WRN_CLRLOCK1	"Removing lock."
+#define	ERR_MKLOCK	"unable to create governing lock file <%s>."
+#define	ERR_NOLOCK	"unable to install governing lock on <%s>."
+#define	ERR_NOOPEN	"unable to open <%s>."
+#define	ERR_LCKTBL	"unable to lock <%s> - lock table full."
+#define	ERR_LCKREM	"unable to lock <%s> - remote host unavailable."
+#define	ERR_BADLCK	"unable to lock <%s> - unknown error."
+#define	ERR_DEADLCK	"unable to lock <%s> - deadlock condition."
+
+static pid_t lock_pid;
+static int lock_fd, lock_is_applied;
+static char lock_name[PKGSIZ];
+static char lock_pkg[PKGSIZ];
+static char lock_place[PKGSIZ];
+static unsigned int lock_state;
+static char lockbuf[LCKBUFSIZ];
+static char lockpath[PATH_MAX];
+
+#define	LOCK_NAME_OLD_PKG	"old version pkg command"
+#define	LOCK_PKG_UNKNOWN	"unknown package"
+#define	LOCK_PLACE_UNKNOWN	"unknown"
+
+/*
+ * This function writes the PID, effective utility name, package name,
+ * current progress of the utility and the exit state to the lockfile in
+ * support of post mortem operations.
+ */
+static int
+wrlockdata(int fd, int this_pid, char *this_name,
+    char *this_pkg, char *this_place, unsigned int this_state)
+{
+	if (this_pid < 0 || *this_name == '\000')
+		return (0);
+
+	(void) memset(lockbuf, 0, LCKBUFSIZ);
+
+	(void) snprintf(lockbuf, sizeof (lockbuf),
+			"%d %s %s %s %d\n", this_pid, this_name, this_pkg,
+			this_place, this_state);
+
+	(void) lseek(fd, 0, SEEK_SET);
+	if (write(fd, lockbuf, LF_SIZE) == LF_SIZE)
+		return (1);
+	else
+		return (0);
+}
+
+/*
+ * This function reads the lockfile to obtain the PID and name of the lock
+ * holder. Upon those rare circumstances that an older version of pkgadd
+ * created the lock, this detects that zero-length file and provides the
+ * appropriate data. Since this data is only used if an actual lock (from
+ * lockf()) is detected, a manually created .lockfile will not result in a
+ * message.
+ */
+static void
+rdlockdata(int fd)
+{
+	(void) lseek(fd, 0, SEEK_SET);
+	if (read(fd, lockbuf, LF_SIZE) != LF_SIZE) {
+		lock_pid = 0;
+		(void) strlcpy(lock_name, LOCK_NAME_OLD_PKG,
+						sizeof (lock_name));
+
+		(void) strlcpy(lock_pkg, LOCK_PKG_UNKNOWN,
+						sizeof (lock_pkg));
+
+		(void) strlcpy(lock_place, LOCK_PLACE_UNKNOWN,
+						sizeof (lock_place));
+
+		lock_state = ST_OK;
+	} else {
+		/* LINTED format argument contains unbounded string specifier */
+		(void) sscanf(lockbuf, "%ld %s %s %s %u", &lock_pid,
+			lock_name, lock_pkg, lock_place, &lock_state);
+	}
+}
+
+static void
+do_alarm(int n)
+{
+#ifdef lint
+	int i = n;
+	n = i;
+#endif	/* lint */
+	(void) signal(SIGALRM, do_alarm);
+	(void) alarm(LOCKWAIT);
+}
+
+/*
+ * This establishes a locked status file for a pkgadd, pkgrm or swmtool - any
+ * of the complex package processes. Since numerous packages currently use
+ * installf and removef in preinstall scripts, we can't enforce a contents
+ * file write lock throughout the install process. In 2.7 we will enforce the
+ * write lock and allow this lock to serve as a simple information carrier
+ * which can be used by installf and removef too.
+ * Arguments:
+ *  util_name - the name of the utility that is claiming the lock
+ *  pkg_name - the package that is being locked (or "all package")
+ *  place - a string of ascii characters that defines the initial "place" where
+ *    the current operation is - this is updated by lockupd() and is a string
+ *    is used fr post mortem operations if the utility should quit improperly.
+ * Returns (int):
+ *  == 0 - failure
+ *  != 0 - success
+ */
+
+int
+lockinst(char *util_name, char *pkg_name, char *place)
+{
+	int	fd, retry_cnt;
+
+	/* assume "initial" if no "place" during processing specified */
+
+	if ((place == (char *)NULL) || (*place == '\0')) {
+		place = "initial";
+	}
+
+	(void) snprintf(lockpath, sizeof (lockpath),
+			"%s/%s", get_PKGADM(), LOCKFILE);
+
+	/* If the exit file is not present, create it. */
+	/* LINTED O_CREAT without O_EXCL specified in call to open() */
+	if ((fd = open(lockpath, O_RDWR | O_CREAT, 0600)) == -1) {
+		progerr(gettext(ERR_MKLOCK), lockpath);
+		return (0);
+	}
+
+	lock_fd = fd;
+
+	retry_cnt = LOCKRETRY;
+	lock_is_applied = 0;
+
+	(void) signal(SIGALRM, do_alarm);
+	(void) alarm(LOCKWAIT);
+
+	/*
+	 * This tries to create the lock LOCKRETRY times waiting LOCKWAIT
+	 * seconds between retries.
+	 */
+	do {
+
+		if (lockf(fd, F_LOCK, 0)) {
+			/*
+			 * Try to read the status of the last (or current)
+			 * utility.
+			 */
+			rdlockdata(fd);
+
+			logerr(gettext(MSG_WTFOR), lock_name, lock_pkg);
+		} else {	/* This process has the lock. */
+			rdlockdata(fd);
+
+			if (lock_state != 0) {
+				logerr(gettext(WRN_CLRLOCK), lock_name,
+				    lock_pkg, lock_place);
+				logerr(gettext(WRN_CLRLOCK1));
+			}
+
+			lock_pid = getpid();
+			(void) strlcpy(lock_name, (util_name) ?
+			    util_name : gettext("unknown"), sizeof (lock_name));
+
+			(void) strlcpy(lock_pkg, (pkg_name) ?
+			    pkg_name : gettext("unknown"), sizeof (lock_pkg));
+
+			(void) wrlockdata(fd, lock_pid, lock_name,
+			    lock_pkg, place, ST_QUIT);
+			lock_is_applied = 1;
+			break;
+		}
+	} while (retry_cnt--);
+
+	(void) signal(SIGALRM, SIG_IGN);
+
+	if (!lock_is_applied) {
+		progerr(gettext(ERR_NOLOCK), lockpath);
+		return (0);
+	}
+
+	return (1);
+}
+
+/*
+ * This function updates the utility progress data in the lock file. It is
+ * used for post mortem operations if the utility should quit improperly.
+ */
+void
+lockupd(char *place)
+{
+	(void) wrlockdata(lock_fd, lock_pid, lock_name, lock_pkg, place,
+			ST_QUIT);
+}
+
+/*
+ * This clears the governing lock and closes the lock file. If this was
+ * called already, it just returns.
+ */
+void
+unlockinst(void)
+{
+	if (lock_is_applied) {
+		(void) wrlockdata(lock_fd, lock_pid, lock_name, lock_pkg,
+			"finished", ST_OK);
+
+		/*
+		 * If close() fails, we can't be sure the lock has been
+		 * removed, so we assume the worst in case this function is
+		 * called again.
+		 */
+		if (close(lock_fd) != -1)
+			lock_is_applied = 0;
+	}
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/usr/src/cmd/svr4pkg/libinst/log.c	Fri Jun 05 10:28:40 2009 -0400
@@ -0,0 +1,190 @@
+/*
+ * 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
+ * or http://www.opensolaris.org/os/licensing.
+ * 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 2009 Sun Microsystems, Inc.  All rights reserved.
+ * Use is subject to license terms.
+ */
+
+
+/* unix system includes */
+
+#include <stdio.h>
+#include <stdarg.h>
+#include <stdlib.h>
+#include <string.h>
+#include <fcntl.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <unistd.h>
+#include <locale.h>
+#include <errno.h>
+#include <sys/param.h>
+#include <instzones_api.h>
+
+/*
+ * consolidation pkg command library includes
+ */
+
+#include "pkglib.h"
+
+/*
+ * local pkg command library includes
+ */
+
+#include "install.h"
+#include "libinst.h"
+#include "libadm.h"
+#include "messages.h"
+
+/* Should be defined by cc -D */
+#if	!defined(TEXT_DOMAIN)
+#define	TEXT_DOMAIN "SYS_TEST"
+#endif
+
+/* local static data */
+
+static boolean_t	verbose = B_FALSE;
+
+/*
+ * Name:	log_msg
+ * Description:	Outputs messages to logging facility.
+ * Scope:	public
+ * Arguments:	a_type - the severity of the message
+ *		a_format - the printf format, plus its arguments
+ * Returns:	none
+ */
+
+/*PRINTFLIKE2*/
+void
+log_msg(LogMsgType a_type, const char *a_format, ...)
+{
+	FILE	*out;
+	char	*rstr = (char *)NULL;
+	char	bfr[1];
+	char	*prefix;
+	size_t	vres = 0;
+	va_list	ap;
+	char	*p = get_prog_name();
+
+	/* process message based on type */
+
+	switch (a_type) {
+	case LOG_MSG_ERR:
+	default:	/* treat unknown type as LOG_MSG_ERR */
+		out = stderr;
+		prefix = MSG_LOG_ERROR;
+		break;
+	case LOG_MSG_WRN:	/* warning message */
+		out = stderr;
+		prefix = MSG_LOG_WARNING;
+		break;
+	case LOG_MSG_INFO:	/* information message */
+		out = stdout;
+		prefix = NULL;
+		break;
+	case LOG_MSG_DEBUG:	/* debugging message */
+		if (!log_get_verbose()) {
+			/* no debug messages if not verbose mode */
+			return;
+		}
+
+		out = stderr;
+		prefix = NULL;
+
+		/* output debug prefix to match echoDebug() format */
+
+		(void) fprintf(stderr, "# [%6d %3d", getpid(), getzoneid());
+
+		if ((p != (char *)NULL) && (*p != '\0')) {
+			fprintf(stderr, " %-11s", p);
+		}
+
+		(void) fprintf(stderr, "] ");
+		break;
+	}
+
+	/* output prefix if specified */
+
+	if (prefix != NULL) {
+		(void) fprintf(out, "%s: ", prefix);
+	}
+
+	/* determine size of the message in bytes */
+
+	va_start(ap, a_format);
+	vres = vsnprintf(bfr, 1, a_format, ap);
+	va_end(ap);
+
+	/* allocate storage to hold the message */
+
+	rstr = (char *)malloc(vres+2);
+
+	/* generate the results of the printf conversion */
+
+	va_start(ap, a_format);
+	vres = vsnprintf(rstr, vres+1, a_format, ap);
+	va_end(ap);
+
+	/* output formatted message to appropriate destination */
+
+	if (fprintf(out, "%s\n", rstr) < 0) {
+		if (out != stderr) {
+			/*
+			 * nothing output, try stderr as a
+			 * last resort
+			 */
+			(void) fprintf(stderr, ERR_LOG_FAIL, a_format);
+		}
+	}
+
+	/* free temporary message storage */
+
+	free(rstr);
+}
+
+/*
+ * Name:	set_verbose
+ * Description:	Turns on verbose output
+ * Scope:	public
+ * Arguments:	verbose = B_TRUE indicates verbose mode
+ * Returns:	none
+ */
+
+void
+log_set_verbose(boolean_t setting)
+{
+	verbose = setting;
+}
+
+/*
+ * Name:	get_verbose
+ * Description:	Returns whether or not to output verbose messages
+ * Scope:	public
+ * Arguments:	none
+ * Returns:	B_TRUE - verbose messages should be output
+ */
+
+boolean_t
+log_get_verbose()
+{
+	return (verbose);
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/usr/src/cmd/svr4pkg/libinst/mntinfo.c	Fri Jun 05 10:28:40 2009 -0400
@@ -0,0 +1,1417 @@
+/*
+ * 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
+ * or http://www.opensolaris.org/os/licensing.
+ * 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.
+ */
+
+
+/*
+ * System includes
+ */
+
+#include <stdio.h>
+#include <limits.h>
+#include <errno.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <string.h>
+#include <wait.h>
+#include <signal.h>
+#include <malloc.h>
+#include <sys/types.h>
+#include <sys/mount.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+#include <sys/systeminfo.h>
+#include <pkgstrct.h>
+#include <pkginfo.h>
+#include <locale.h>
+#include <libintl.h>
+
+#include <sys/mnttab.h>
+#include <sys/mntent.h>
+#include <sys/vfstab.h>
+
+/*
+ * consolidation pkg command library includes
+ */
+
+#include <pkglib.h>
+
+/*
+ * local pkg command library includes
+ */
+
+#include "install.h"
+#include "libinst.h"
+#include "libadm.h"
+#include "messages.h"
+
+extern char **environ;
+
+static int match_mount;		/* This holds the mount of interest. */
+
+int	fs_tab_used  = 0;
+int	fs_tab_alloc = 0;
+static int	fs_list = -1;
+
+struct	fstable	**fs_tab = NULL;
+
+#define	PKGDBROOT	"/var/sadm"
+#define	MOUNT		"/sbin/mount"
+#define	UMOUNT		"/sbin/umount"
+
+#define	setmntent	fopen
+#define	endmntent	fclose
+#define	MOUNT_TABLE	MNTTAB
+
+/* returned by already_mounted() */
+#define	MNT_NOT		0
+#define	MNT_EXACT	1
+#define	MNT_AVAIL	2
+
+/* used with is_remote_src() */
+#define	NOT_REMOTE	0
+#define	REAL_REMOTE	1
+#define	SELF_SERVE	2
+
+/*
+ * Due to /etc/mnttab files containing entries for multiple nfs hosts
+ * HOST_NM_LN needs to be accommodating. The recommended value in the sysinfo
+ * man page of 257 needs to be expanded. See bugid 4076513.
+ * 1024 chars is defined in the mnttab.h header as the max size of an entry.
+ */
+
+#define	HOST_NM_LN	MNT_LINE_MAX
+
+/* These cachefs definitions should be in mntent.h. Maybe some day. */
+#define	MNTTYPE_CFS		"cachefs"
+#define	MNTOPT_BACKFSTYPE	"backfstype"
+#define	MNTTYPE_AUTO		"autofs"
+
+/*
+ * Utilities for getting filesystem information from the mount table.
+ *
+ * Note: vanilla SVr4 code (pkginstall/dockspace.c) used the output from
+ * popen() on the "/etc/mount" command.  However, we need to get more
+ * information about mounted filesystems, so we use the C interfaces to
+ * the mount table, which also happens to be much faster than running
+ * another process.  Since several of the pkg commands need access to the
+ * the code has been placed here, to be included in the libinst library.
+ */
+
+#define	ALLOC_CHUNK	30
+
+/*
+ * fs_tab_ent_comp -	compare fstable entries first by length in reverse
+ *			order, then alphabetically.
+ */
+static int
+fs_tab_ent_comp(const void *e1, const void *e2)
+{
+	struct fstable	*fs1 = *((struct fstable **)e1);
+	struct fstable	*fs2 = *((struct fstable **)e2);
+
+	if (fs1->namlen == fs2->namlen)
+		return (strcmp(fs1->name, fs2->name));
+	else
+		return (fs2->namlen - fs1->namlen);
+}
+
+/*
+ * This determines if the source of the mount is from another host. If it's
+ * from this host, then it might be writable. This returns NOT_REMOTE if it's
+ * pure local, REAL_REMOTE if it's being served from another host and
+ * SELF_SERVE if it's being served by the current host.
+ */
+static int
+is_remote_src(char *source)
+{
+	static char host_name[HOST_NM_LN];
+	char source_host[HOST_NM_LN], *src_ptr, *src_host_ptr;
+	static int hn_len;
+
+	if (hn_len == 0) {
+		/* Find out what host this is. */
+		(void) sysinfo(SI_HOSTNAME, host_name, HOST_NM_LN);
+		hn_len = strlen(host_name);
+	}
+
+	if (source[0] == '/')
+		return (NOT_REMOTE);	/* No server name, so it's local. */
+
+	if (strchr(source, ':') == NULL)
+		return (NOT_REMOTE);	/* it's a floppy disk or something */
+
+	src_ptr = source;
+	src_host_ptr = source_host;
+
+	/* Scan to the end of the hostname (find the ":"). */
+	while (*src_ptr != ':')
+		*src_host_ptr++ = *src_ptr++;
+	*src_host_ptr = '\0';
+
+	if (strncmp(source, host_name, hn_len) == 0 &&
+	    *(source+hn_len) == ':' || is_local_host(source_host))
+		return (SELF_SERVE);	/* Exporting from itself, it's local. */
+
+	return (REAL_REMOTE);
+}
+
+/*
+ * This determines if an apparently writeable filesystem is really writeable
+ * or if it's been shared over the network with root-restrictive options.
+ */
+static int
+really_write(char *mountpt)
+{
+	char testfile[PATH_MAX];
+	int fd, retval = 0;
+	struct stat status;
+
+	(void) snprintf(testfile, sizeof (testfile), "%s/testXXXXXX", mountpt);
+
+	if (mktemp(testfile) == NULL)
+		return (0);	/* may as well be read-only */
+	/* LINTED do not use creat(); use open(path,... */
+	else if ((fd = creat(testfile, 0777)) == -1)
+		return (0);	/* can't write */
+	else if (fstat(fd, &status) == -1)
+		retval = 0;	/* may as well be read-only */
+	else if (status.st_uid != 0)
+		retval = 0;	/* too many restrictions */
+	else
+		retval = 1;
+
+	(void) close(fd);
+	(void) unlink(testfile);
+
+	return (retval);
+}
+
+/* This returns the hostname portion of a remote path. */
+char *
+get_server_host(short n)
+{
+	static char hostname[HOST_NM_LN], *host_end;
+
+	if (fs_tab_used == 0) {
+		return ("unknown source");
+	}
+
+	if (n >= 0 && n < fs_tab_used) {
+		(void) strcpy(hostname, fs_tab[n]->remote_name);
+		if ((host_end = strchr(hostname, ':')) == NULL) {
+			if ((strcmp(fs_tab[n]->fstype, MNTTYPE_AUTO)) == NULL)
+				return ("automounter");
+			else
+				return (fs_tab[n]->fstype);
+		} else {
+			*host_end = '\0';
+			return (hostname);
+		}
+	}
+
+	return ("unknown source");
+}
+
+/*
+ * This pulls the path out of a hostpath which may be of the form host:path
+ * where path is an absolute path. NOTE: If path turns out to be relative,
+ * this returns NULL.
+ */
+static char *
+path_part(char *hostpath)
+{
+	char *host_end;
+
+	if ((host_end = strchr(hostpath, ':')) == NULL && hostpath[0] == '/')
+		return (hostpath);	/* It's already legit. */
+
+	if (*(host_end+1) == '/')
+		return (host_end+1);	/* Here's the path part. */
+
+	return (NULL);
+}
+
+/*
+ * This scans the filesystems already mounted to see if this remote mount is
+ * already in place on the server. This scans the fs_tab for a remote_name
+ * exactly matching the client's. It stores the current entry number
+ * corresponding to this mount in the static match_mount.
+ *
+ * Returns:
+ *	MNT_NOT		Couldn't find it.
+ *	MNT_EXACT	This has actually been manually mounted for us
+ *	MNT_AVAIL	This is mounted for the server, but needs to be
+ *			loopback mounted from the client's perspective.
+ */
+static int
+already_mounted(struct vfstab *vfs, int is_local_host, char *client_path,
+    char *host_path)
+{
+	int i;
+
+	match_mount = -1;
+
+	if (fs_tab_used == 0) {
+		return (MNT_NOT);
+	}
+
+	for (i = 0; i < fs_tab_used; i++) {
+		/*
+		 * Determine if this has been manually mounted exactly as we
+		 * require. Begin by finding a mount on our current
+		 * mountpoint.
+		 */
+		if (strcmp(fs_tab[i]->name, client_path) == 0) {
+			/*
+			 * Now see if it is really the same mount. This isn't
+			 * smart enough to find mounts on top of mounts, but
+			 * assuming there is no conspiracy to fool this
+			 * function, it will be good enough.
+			 */
+			if (is_local_host &&
+			    strcmp(fs_tab[i]->remote_name, host_path) == 0) {
+				match_mount = i;
+				return (MNT_EXACT);
+			}
+		}
+
+		/* Determine if this mount is available to the server. */
+		if (strcmp(fs_tab[i]->remote_name, vfs->vfs_special) == 0) {
+			match_mount = i;
+			return (MNT_AVAIL);
+		}
+	}
+	return (MNT_NOT);
+}
+
+/*
+ * This function unmounts all of the loopback mounts created for the client.
+ * If no client stuff is mounted, this is completely benign, it finds that
+ * nothing is mounted up and returns. It returns "1" for unmounted everything
+ * OK and "0" for failure.
+ */
+int
+unmount_client()
+{
+	int	errcode;
+	int	exit_no;
+	int	n;
+	int	retcode = 1;
+	int	status;
+	pid_t	pid;
+	pid_t	pid_return;
+
+	if (fs_tab_used == 0) {
+		return (1);
+	}
+
+	for (n = 0; n < fs_tab_used-1; n++) {
+		/* If the filesystem is mounted and this utility did it ... */
+		if (fs_tab[n]->cl_mounted && fs_tab[n]->srvr_map) {
+			char	*arg[3];
+
+			/* create arglist for umount command */
+
+			arg[0] = UMOUNT;
+			arg[1] = fs_tab[n]->name;
+			arg[2] = (char *)NULL;
+
+			/* flush standard i/o before creating new process */
+
+			(void) fflush(stderr);
+			(void) fflush(stdout);
+
+			/*
+			 * create new process to execute command in;
+			 * vfork is being used to avoid duplicating the parents
+			 * memory space - this means that the child process may
+			 * not modify any of the parents memory including the
+			 * standard i/o descriptors - all the child can do is
+			 * adjust interrupts and open files as a prelude to a
+			 * call to exec().
+			 */
+
+			pid = vfork();
+			if (pid < 0) {
+				/* fork failed! */
+
+				logerr(WRN_BAD_FORK, errno, strerror(errno));
+				retcode = 0;
+			} else if (pid > 0) {
+				/*
+				 * this is the parent process
+				 */
+
+				status = 0;
+				pid_return = waitpid(pid, &status, 0);
+
+				if (pid_return != pid) {
+					logerr(WRN_BAD_WAIT, pid, pid_return,
+						(unsigned long)status, errno,
+						strerror(errno));
+					retcode = 0;
+				}
+
+				/*
+				 * If the child was stopped or killed by a
+				 * signal or exied with any code but 0, we
+				 * assume the mount has failed.
+				 */
+
+				if (!WIFEXITED(status) ||
+				    (errcode = WEXITSTATUS(status))) {
+					retcode = 0;
+					logerr(WRN_FSTAB_UMOUNT,
+						fs_tab[n]->name, errcode);
+				} else {
+					fs_tab[n]->cl_mounted = 0;
+				}
+			} else {
+				/*
+				 * this is the child process
+				 */
+
+				int	i;
+
+				/* reset any signals to default */
+
+				for (i = 0; i < NSIG; i++) {
+					(void) sigset(i, SIG_DFL);
+				}
+
+				/*
+				 * Redirect output to /dev/null because the
+				 * umount error message may be confusing to
+				 * the user.
+				 */
+
+				i = open("/dev/null", O_WRONLY);
+				if (i >= 0) {
+					dup2(2, STDERR_FILENO);
+				}
+
+				/* close all file descriptors except stdio */
+
+				closefrom(3);
+
+				exit_no = execve(arg[0], arg, environ);
+				_exit(exit_no);
+			}
+		}
+	}
+
+	return (retcode);
+}
+
+/*
+ * This function creates the necessary loopback mounts to emulate the client
+ * configuration with respect to the server. If this is being run on a
+ * standalone or the installation is actually to the local system, this call
+ * is benign since srvr_map won't be set anywhere. It returns "1" for mounted
+ * everything OK and "0" for failure.
+ */
+int
+mount_client()
+{
+	int	errcode;
+	int	exit_no;
+	int	n;
+	int	retcode = 1;
+	int	status;
+	pid_t	pid;
+	pid_t	pid_return;
+
+	if (fs_tab_used == 0) {
+		return (1);
+	}
+
+	for (n = fs_tab_used-1; n >= 0; n--) {
+		/*
+		 * If the filesystem is mounted (meaning available) and the
+		 * apparent filesystem can be mapped to a local filesystem
+		 * AND the local filesystem is not the same as the target
+		 * filesystem, mount it.
+		 */
+		if (fs_tab[n]->mounted && fs_tab[n]->srvr_map) {
+			char	*arg[6];
+
+			/* create arglist for mount command */
+
+			arg[0] = MOUNT;
+			arg[1] = "-F";
+			arg[2] = "lofs";
+			arg[3] = fs_tab[n]->remote_name;
+			arg[4] = fs_tab[n]->name;
+			arg[5] = (char *)NULL;
+
+			/* flush standard i/o before creating new process */
+
+			(void) fflush(stderr);
+			(void) fflush(stdout);
+
+			/*
+			 * create new process to execute command in;
+			 * vfork is being used to avoid duplicating the parents
+			 * memory space - this means that the child process may
+			 * not modify any of the parents memory including the
+			 * standard i/o descriptors - all the child can do is
+			 * adjust interrupts and open files as a prelude to a
+			 * call to exec().
+			 */
+
+			pid = vfork();
+			if (pid < 0) {
+				/* fork failed! */
+
+				logerr(WRN_BAD_FORK, errno, strerror(errno));
+				retcode = 0;
+			} else if (pid > 0) {
+				/*
+				 * this is the parent process
+				 */
+
+				pid_return = waitpid(pid, &status, 0);
+
+				if (pid_return != pid) {
+					logerr(WRN_BAD_WAIT, pid, pid_return,
+						(unsigned long)status, errno,
+						strerror(errno));
+					retcode = 0;
+				}
+
+				/*
+				 * If the child was stopped or killed by a
+				 * signal or exied with any code but 0, we
+				 * assume the mount has failed.
+				 */
+
+				if (!WIFEXITED(status) ||
+				    (errcode = WEXITSTATUS(status))) {
+					retcode = 0;
+					fs_tab[n]->mnt_failed = 1;
+					logerr(WRN_FSTAB_MOUNT,
+					    fs_tab[n]->name, errcode);
+				} else {
+					fs_tab[n]->cl_mounted = 1;
+				}
+			} else {
+				/*
+				 * this is the child process
+				 */
+
+				int	i;
+
+				/* reset all signals to default */
+
+				for (i = 0; i < NSIG; i++) {
+					(void) sigset(i, SIG_DFL);
+				}
+
+				/*
+				 * Redirect output to /dev/null because the
+				 * mount error message may be confusing to
+				 * the user.
+				 */
+
+				i = open("/dev/null", O_WRONLY);
+				if (i >= 0) {
+					dup2(i, STDERR_FILENO);
+				}
+
+				/* close all file descriptors except stdio */
+
+				closefrom(3);
+
+				exit_no = execve(arg[0], arg, environ);
+				_exit(exit_no);
+				/*NOTREACHED*/
+			}
+		}
+	}
+	return (retcode);
+}
+
+/*
+ * This function maps path, on a loopback filesystem, back to the real server
+ * filesystem. fsys_value is the fs_tab[] entry to which the loopback'd path is
+ * mapped. This returns a pointer to a static area. If the result is needed
+ * for further processing, it should be strdup()'d or something.
+ */
+char *
+server_map(char *path, short fsys_value)
+{
+	static char server_construction[PATH_MAX];
+
+	if (fs_tab_used == 0) {
+		(void) strcpy(server_construction, path);
+	} else if (fsys_value >= 0 && fsys_value < fs_tab_used) {
+		(void) snprintf(server_construction,
+			sizeof (server_construction),
+			"%s%s", fs_tab[fsys_value]->remote_name,
+			path+strlen(fs_tab[fsys_value]->name));
+	} else {
+		(void) strcpy(server_construction, path);
+	}
+
+	return (server_construction);
+}
+
+/* This function sets up the standard parts of the fs_tab. */
+static struct fstable *
+fs_tab_init(char *mountp, char *fstype)
+{
+	struct fstable *nfte;
+
+	/* Create the array if necessary. */
+	if (fs_list == -1) {
+		fs_list = ar_create(ALLOC_CHUNK,
+		    (unsigned)sizeof (struct fstable),
+		    "filesystem mount data");
+		if (fs_list == -1) {
+			progerr(ERR_MALLOC, "fs_list", errno, strerror(errno));
+			return (NULL);
+		}
+	}
+
+	/*
+	 * Allocate an fstable entry for this mnttab entry.
+	 */
+	if ((nfte = *(struct fstable **)ar_next_avail(fs_list))
+	    == NULL) {
+		progerr(ERR_MALLOC, "nfte", errno, strerror(errno));
+		return (NULL);
+	}
+
+	/*
+	 * Point fs_tab at the head of the array again, since it may have
+	 * moved due to realloc in ar_next_avail(). If ar_next_avail() realizes
+	 * that there is no more room to grow the array, it reallocates the
+	 * array. Because we stored pointer to that array in fs_tab, we need
+	 * to make sure that it is updated as well.
+	 */
+	if ((fs_tab = (struct fstable **)ar_get_head(fs_list)) == NULL) {
+		progerr(ERR_NOTABLE, "mount", MOUNT_TABLE, strerror(errno));
+		return (NULL);
+	}
+
+	/*
+	 * Get the length of the 'mount point' name.
+	 */
+	nfte->namlen = strlen(mountp);
+	/*
+	 * Allocate space for the 'mount point' name.
+	 */
+	if ((nfte->name = malloc(nfte->namlen+1)) == NULL) {
+		progerr(ERR_MALLOC, "name", errno, strerror(errno));
+		return (NULL);
+	}
+	(void) strcpy(nfte->name, mountp);
+
+	if ((nfte->fstype = malloc(strlen(fstype)+1)) == NULL) {
+		progerr(ERR_MALLOC, "fstype", errno, strerror(errno));
+		return (NULL);
+	}
+	(void) strcpy(nfte->fstype, fstype);
+
+	fs_tab_used++;
+
+	return (nfte);
+}
+
+/* This function frees all memory associated with the filesystem table. */
+void
+fs_tab_free(void)
+{
+	int n;
+
+	if (fs_tab_used == 0) {
+		return;
+	}
+
+	for (n = 0; n < fs_tab_used; n++) {
+		free(fs_tab[n]->fstype);
+		free(fs_tab[n]->name);
+		free(fs_tab[n]->remote_name);
+	}
+
+	ar_free(fs_list);
+}
+
+/* This function scans a string of mount options for a specific keyword. */
+static int
+hasopt(char *options, char *keyword)
+{
+	char vfs_options[VFS_LINE_MAX], *optptr;
+
+	if (!options) {
+		(void) strcpy(vfs_options, "ro");
+	} else {
+		(void) strcpy(vfs_options, options);
+	}
+
+	while (optptr = strrchr(vfs_options, ',')) {
+		*optptr++ = '\0';
+
+		if (strcmp(optptr, keyword) == 0)
+			return (1);
+	}
+
+	/* Now deal with the remainder. */
+	if (strcmp(vfs_options, keyword) == 0)
+		return (1);
+
+	return (0);
+}
+
+/*
+ * This function constructs a new filesystem table (fs_tab[]) entry based on
+ * an /etc/mnttab entry. When it returns, the new entry has been inserted
+ * into fs_tab[].
+ */
+static int
+construct_mt(struct mnttab *mt)
+{
+	struct	fstable	*nfte;
+
+	/*
+	 * Initialize fstable structure and make the standard entries.
+	 */
+	if ((nfte = fs_tab_init(mt->mnt_mountp, mt->mnt_fstype)) == NULL)
+		return (1);
+
+	/* See if this is served from another host. */
+	if (is_remote_src(mt->mnt_special) == REAL_REMOTE ||
+	    strcmp(mt->mnt_fstype, MNTTYPE_AUTO) == 0)
+		nfte->remote = 1;
+	else
+		nfte->remote = 0;
+
+	/* It's mounted now (by definition), so we don't have to remap it. */
+	nfte->srvr_map = 0;
+	nfte->mounted = 1;
+
+	nfte->remote_name = strdup(mt->mnt_special);
+
+	/*
+	 * This checks the mount commands which establish the most
+	 * basic level of access. Later further tests may be
+	 * necessary to fully qualify this. We set this bit
+	 * preliminarily because we have access to the mount data
+	 * now.
+	 */
+	nfte->writeable = 0;	/* Assume read-only. */
+	if (hasmntopt(mt, MNTOPT_RO) == NULL) {
+		nfte->writeable = 1;
+		if (!(nfte->remote))
+			/*
+			 * There's no network involved, so this
+			 * assessment is confirmed.
+			 */
+			nfte->write_tested = 1;
+	} else
+		/* read-only is read-only */
+		nfte->write_tested = 1;
+
+	/* Is this coming to us from a server? */
+	if (nfte->remote && !(nfte->writeable))
+		nfte->served = 1;
+
+	return (0);
+}
+
+/*
+ * This function modifies an existing fs_tab[] entry. It was found mounted up
+ * exactly the way we would have mounted it in mount_client() only at the
+ * time we didn't know it was for the client. Now we do, so we're setting the
+ * various permissions to conform to the client view.
+ */
+static void
+mod_existing(struct vfstab *vfsent, int fstab_entry, int is_remote)
+{
+	/*
+	 * Establish whether the client will see this as served.
+	 */
+	if (is_remote && hasopt(vfsent->vfs_mntopts, MNTOPT_RO))
+		fs_tab[fstab_entry]->served = 1;
+
+	fs_tab[fstab_entry]->cl_mounted = 1;
+}
+
+/*
+ * This function constructs a new fs_tab[] entry based on
+ * an /etc/vfstab entry. When it returns, the new entry has been inserted
+ * into fstab[].
+ */
+static int
+construct_vfs(struct vfstab *vfsent, char *client_path, char *link_name,
+    int is_remote, int mnt_stat)
+{
+	int use_link;
+	struct	fstable	*nfte;
+
+	if ((nfte = fs_tab_init(client_path, vfsent->vfs_fstype)) == NULL)
+		return (1);
+
+	nfte->remote = (is_remote == REAL_REMOTE);
+
+	/*
+	 * The file system mounted on the client may or may not be writeable.
+	 * So we hand it over to fsys() to evaluate. This will have the same
+	 * read/write attributes as the corresponding mounted filesystem.
+	 */
+	use_link = 0;
+	if (nfte->remote) {
+		/*
+		 * Deal here with mount points actually on a system remote
+		 * from the server.
+		 */
+		if (mnt_stat == MNT_NOT) {
+			/*
+			 * This filesystem isn't in the current mount table
+			 * meaning it isn't mounted, the current host can't
+			 * write to it and there's no point to mapping it for
+			 * the server.
+			 */
+			link_name = NULL;
+			nfte->mounted = 0;
+			nfte->srvr_map = 0;
+			nfte->writeable = 0;
+		} else {	/* It's MNT_AVAIL. */
+			/*
+			 * This filesystem is associated with a current
+			 * mountpoint. Since it's mounted, it needs to be
+			 * remapped and it is writable if the real mounted
+			 * filesystem is writeable.
+			 */
+			use_link = 1;
+			link_name = strdup(fs_tab[match_mount]->name);
+			nfte->mounted = 1;
+			nfte->srvr_map = 1;
+			nfte->writeable = fs_tab[match_mount]->writeable;
+			nfte->write_tested = fs_tab[match_mount]->write_tested;
+		}
+	} else {	/* local filesystem */
+		use_link = 1;
+		nfte->mounted = 1;
+		nfte->srvr_map = 1;
+		nfte->writeable = fs_tab[fsys(link_name)]->writeable;
+		nfte->write_tested = 1;
+	}
+
+	/*
+	 * Now we establish whether the client will see this as served.
+	 */
+	if (is_remote && hasopt(vfsent->vfs_mntopts, MNTOPT_RO))
+		nfte->served = 1;
+
+	if (use_link) {
+		nfte->remote_name = link_name;
+	} else {
+		nfte->remote_name = strdup(vfsent->vfs_special);
+	}
+
+	return (0);
+}
+
+/*
+ * get_mntinfo - get the mount table, now dynamically allocated. Returns 0 if
+ * no problem and 1 if there's a fatal error.
+ */
+int
+get_mntinfo(int map_client, char *vfstab_file)
+{
+	static 	char 	*rn = "/";
+	FILE		*pp;
+	struct	mnttab	mtbuf;
+	struct	mnttab	*mt = &mtbuf;
+	char		*install_root;
+	int 		is_remote;
+
+	/*
+	 * Open the mount table for the current host and establish a global
+	 * table that holds data about current mount status.
+	 */
+	if ((pp = setmntent(MOUNT_TABLE, "r")) == NULL) {
+		progerr(ERR_NOTABLE, "mount", MOUNT_TABLE, strerror(errno));
+		return (1);
+	}
+
+	/*
+	 * First, review the mounted filesystems on the managing host. This
+	 * may also be the target host but we haven't decided that for sure
+	 * yet.
+	 */
+	while (!getmntent(pp, mt))
+		if (construct_mt(mt))
+			return (1);
+
+	(void) endmntent(pp);
+
+	/*
+	 * Now, we see if this installation is to a client. If it is, we scan
+	 * the client's vfstab to determine what filesystems are
+	 * inappropriate to write to. This simply adds the vfstab entries
+	 * representing what will be remote file systems for the client.
+	 * Everything that isn't remote to the client is already accounted
+	 * for in the fs_tab[] so far. If the remote filesystem is really on
+	 * this server, we will write through to the server from this client.
+	 */
+	install_root = get_inst_root();
+	if (install_root && strcmp(install_root, "/") != 0 && map_client) {
+		/* OK, this is a legitimate remote client. */
+		struct	vfstab	vfsbuf;
+		struct	vfstab	*vfs = &vfsbuf;
+		char VFS_TABLE[PATH_MAX];
+
+		/*
+		 * Since we use the fsys() function later, and it depends on
+		 * an ordered list, we have to sort the list here.
+		 */
+		qsort(fs_tab, fs_tab_used,
+		    sizeof (struct fstable *), fs_tab_ent_comp);
+
+		/*
+		 * Here's where the vfstab for the target is. If we can get
+		 * to it, we'll scan it for what the client will see as
+		 * remote filesystems, otherwise, we'll just skip this.
+		 */
+		if (vfstab_file) {
+			(void) snprintf(VFS_TABLE, sizeof (VFS_TABLE), "%s",
+				vfstab_file);
+		} else {
+			(void) snprintf(VFS_TABLE, sizeof (VFS_TABLE), "%s%s",
+				install_root, VFSTAB);
+		}
+
+		if (access(VFS_TABLE, R_OK) == 0) {
+			char *link_name;
+
+			/*
+			 * Open the vfs table for the target host.
+			 */
+			if ((pp = setmntent(VFS_TABLE, "r")) == NULL) {
+				progerr(ERR_NOTABLE, "vfs", VFS_TABLE,
+					strerror(errno));
+				return (1);
+			}
+
+			/* Do this for each entry in the vfstab. */
+			while (!getvfsent(pp, vfs)) {
+				char client_mountp[PATH_MAX];
+				int mnt_stat;
+
+				/*
+				 * We put it into the fs table if it's
+				 * remote mounted (even from this server) or
+				 * loopback mounted from the client's point
+				 * of view.
+				 */
+				if (!(is_remote =
+				    is_remote_src(vfs->vfs_special)) &&
+				    strcmp(vfs->vfs_fstype, MNTTYPE_LOFS) !=
+				    0)
+					continue;	/* not interesting */
+
+				/*
+				 * Construct client_mountp by prepending the
+				 * install_root to the 'mount point' name.
+				 */
+				if (strcmp(vfs->vfs_mountp, "/") == 0) {
+					(void) strcpy(client_mountp,
+								install_root);
+				} else {
+					(void) snprintf(client_mountp,
+						sizeof (client_mountp), "%s%s",
+						install_root, vfs->vfs_mountp);
+				}
+
+				/*
+				 * We also skip the entry if the vfs_special
+				 * path and the client_path are the same.
+				 * There's no need to mount it, it's just a
+				 * cachefs optimization that mounts a
+				 * directory over itself from this server.
+				 */
+				if ((is_remote == SELF_SERVE) &&
+				    strcmp(path_part(vfs->vfs_special),
+				    client_mountp) == 0)
+					continue;
+
+				/* Determine if this is already mounted. */
+				link_name = strdup(path_part(vfs->vfs_special));
+				mnt_stat = already_mounted(vfs,
+				    (is_remote != REAL_REMOTE), client_mountp,
+				    link_name);
+
+				if (mnt_stat == MNT_EXACT) {
+					mod_existing(vfs, match_mount,
+					    is_remote);
+				} else {	/* MNT_NOT */
+					if (construct_vfs(vfs, client_mountp,
+					    link_name, is_remote, mnt_stat))
+						return (1);
+				}
+			}
+			(void) endmntent(pp);
+		}	/* end of if(access()) */
+	}	/* end of if(install_root) */
+
+	/* This next one may look stupid, but it can really happen. */
+	if (fs_tab_used <= 0) {
+		progerr(ERR_MNT_NOMOUNTS);
+		return (1);
+	}
+
+	/*
+	 * Now that we have the complete list of mounted (or virtually
+	 * mounted) filesystems, we sort the mountpoints in reverse order
+	 * based on the length of the 'mount point' name.
+	 */
+	qsort(fs_tab, fs_tab_used, sizeof (struct fstable *), fs_tab_ent_comp);
+	if (strcmp(fs_tab[fs_tab_used-1]->name, rn) != 0) {
+		progerr(ERR_MNT_NOROOT, fs_tab[fs_tab_used-1]->name, rn, errno,
+				strerror(errno));
+		return (1);
+	} else {
+		return (0);
+	}
+}
+
+/*
+ * This function supports dryrun mode by allowing the filesystem table to be
+ * directly loaded from the continuation file.
+ */
+int
+load_fsentry(struct fstable *fs_entry, char *name, char *fstype,
+    char *remote_name)
+{
+	struct fstable *nfte;
+
+	if ((nfte = fs_tab_init(name, fstype)) == NULL)
+		return (1);
+
+	/* Grab the name and fstype from the new structure. */
+	fs_entry->name = nfte->name;
+	fs_entry->fstype = nfte->fstype;
+
+	/* Copy the basic structure into place. */
+	(void) memcpy(nfte, fs_entry, sizeof (struct fstable));
+
+	/*
+	 * Allocate space for the 'special' name.
+	 */
+	if ((nfte->remote_name = malloc(strlen(remote_name)+1)) == NULL) {
+		progerr(ERR_MALLOC, "remote_name", errno, strerror(errno));
+		return (1);
+	}
+
+	(void) strcpy(nfte->remote_name, remote_name);
+
+	return (0);
+}
+
+/*
+ * Given a path, return the table index of the filesystem the file apparently
+ * resides on. This doesn't put any time into resolving filesystems that
+ * refer to other filesystems. It just returns the entry containing this
+ * path.
+ */
+short
+fsys(char *path)
+{
+	register int i;
+	char	real_path[PATH_MAX];
+	char	*path2use = NULL;
+	char	*cp = NULL;
+	int	pathlen;
+
+	path2use = path;
+
+	real_path[0] = '\0';
+
+	(void) realpath(path, real_path);
+
+	if (real_path[0]) {
+		cp = strstr(path, real_path);
+		if (cp != path || cp == NULL)
+			path2use = real_path;
+	}
+
+	pathlen = strlen(path2use);
+
+	/*
+	 * The following algorithm scans the list of attached file systems
+	 * for the one containing path. At this point the file names in
+	 * fs_tab[] are sorted by decreasing length to facilitate the scan.
+	 * The first for() scans past all the file system names too short to
+	 * contain path. The second for() does the actual string comparison.
+	 * It tests first to assure that the comparison is against a complete
+	 * token by assuring that the end of the filesystem name aligns with
+	 * the end of a token in path2use (ie: '/' or NULL) then it does a
+	 * string compare. -- JST
+	 */
+
+	if (fs_tab_used == 0) {
+		return (-1);
+	}
+
+	for (i = 0; i < fs_tab_used; i++)
+		if (fs_tab[i] == NULL)
+			continue;
+		else if (fs_tab[i]->namlen <= pathlen)
+			break;
+	for (; i < fs_tab_used; i++) {
+		int fs_namelen;
+		char term_char;
+
+		if (fs_tab[i] == NULL)
+			continue;
+
+		fs_namelen = fs_tab[i]->namlen;
+		term_char = path2use[fs_namelen];
+
+		/*
+		 * If we're putting the file "/a/kernel" into the filesystem
+		 * "/a", then fs_namelen == 2 and term_char == '/'. If, we're
+		 * putting "/etc/termcap" into "/", fs_namelen == 1 and
+		 * term_char (unfortunately) == 'e'. In the case of
+		 * fs_namelen == 1, we check to make sure the filesystem is
+		 * "/" and if it is, we have a guaranteed fit, otherwise we
+		 * do the string compare. -- JST
+		 */
+		if ((fs_namelen == 1 && *(fs_tab[i]->name) == '/') ||
+		    ((term_char == '/' || term_char == NULL) &&
+		    strncmp(fs_tab[i]->name, path2use, fs_namelen) == 0))
+			return (i);
+	}
+
+	/*
+	 * It only gets here if the root filesystem is fundamentally corrupt.
+	 * (This can happen!)
+	 */
+	progerr(ERR_FSYS_FELLOUT, path2use);
+
+	return (-1);
+}
+
+/*
+ * This function returns the entry in the fs_tab[] corresponding to the
+ * actual filesystem of record. It won't return a loopback filesystem entry,
+ * it will return the filesystem that the loopback filesystem is mounted
+ * over.
+ */
+short
+resolved_fsys(char *path)
+{
+	int i = -1;
+	char path2use[PATH_MAX];
+
+	(void) strcpy(path2use, path);
+
+	/* If this isn't a "real" filesystem, resolve the map. */
+	do {
+		(void) strcpy(path2use, server_map(path2use, i));
+		i = fsys(path2use);
+	} while (fs_tab[i]->srvr_map);
+
+	return (i);
+}
+
+/*
+ * This function returns the srvr_map status based upon the fs_tab entry
+ * number. This tells us if the server path constructed from the package
+ * install root is really the target filesystem.
+ */
+int
+use_srvr_map_n(short n)
+{
+	return ((int)fs_tab[n]->srvr_map);
+}
+
+/*
+ * This function returns the mount status based upon the fs_tab entry
+ * number. This tells us if there is any hope of gaining access
+ * to this file system.
+ */
+int
+is_mounted_n(short n)
+{
+	return ((int)fs_tab[n]->mounted);
+}
+
+/*
+ * is_fs_writeable_n - given an fstab index, return 1
+ *	if it's writeable, 0 if read-only.
+ */
+int
+is_fs_writeable_n(short n)
+{
+	/*
+	 * If the write access permissions haven't been confirmed, do that
+	 * now. Note that the only reason we need to do the special check is
+	 * in the case of an NFS mount (remote) because we can't determine if
+	 * root has access in any other way.
+	 */
+	if (fs_tab[n]->remote && fs_tab[n]->mounted &&
+	    !fs_tab[n]->write_tested) {
+		if (fs_tab[n]->writeable && !really_write(fs_tab[n]->name))
+			fs_tab[n]->writeable = 0;	/* not really */
+
+		fs_tab[n]->write_tested = 1;	/* confirmed */
+	}
+
+	return ((int)fs_tab[n]->writeable);
+}
+
+/*
+ * is_remote_fs_n - given an fstab index, return 1
+ *	if it's a remote filesystem, 0 if local.
+ *
+ *	Note: Upon entry, a valid fsys() is required.
+ */
+int
+is_remote_fs_n(short n)
+{
+	return ((int)fs_tab[n]->remote);
+}
+
+/* index-driven is_served() */
+int
+is_served_n(short n)
+{
+	return ((int)fs_tab[n]->served);
+}
+
+/*
+ * This returns the number of blocks available on the indicated filesystem.
+ *
+ *	Note: Upon entry, a valid fsys() is required.
+ */
+fsblkcnt_t
+get_blk_free_n(short n)
+{
+	return (fs_tab[n]->bfree);
+}
+
+/*
+ * This returns the number of blocks being used on the indicated filesystem.
+ *
+ *	Note: Upon entry, a valid fsys() is required.
+ */
+fsblkcnt_t
+get_blk_used_n(short n)
+{
+	return (fs_tab[n]->bused);
+}
+
+/*
+ * This returns the number of inodes available on the indicated filesystem.
+ *
+ *	Note: Upon entry, a valid fsys() is required.
+ */
+fsblkcnt_t
+get_inode_free_n(short n)
+{
+	return (fs_tab[n]->ffree);
+}
+
+/*
+ * This returns the number of inodes being used on the indicated filesystem.
+ *
+ *	Note: Upon entry, a valid fsys() is required.
+ */
+fsblkcnt_t
+get_inode_used_n(short n)
+{
+	return (fs_tab[n]->fused);
+}
+
+/*
+ * Sets the number of blocks being used on the indicated filesystem.
+ *
+ *	Note: Upon entry, a valid fsys() is required.
+ */
+void
+set_blk_used_n(short n, fsblkcnt_t value)
+{
+	fs_tab[n]->bused = value;
+}
+
+/* Get the filesystem block size. */
+fsblkcnt_t
+get_blk_size_n(short n)
+{
+	return (fs_tab[n]->bsize);
+}
+
+/* Get the filesystem fragment size. */
+fsblkcnt_t
+get_frag_size_n(short n)
+{
+	return (fs_tab[n]->bsize);
+}
+
+/*
+ * This returns the name of the indicated filesystem.
+ */
+char *
+get_fs_name_n(short n)
+{
+	if (fs_tab_used == 0) {
+		return (NULL);
+	} else if (n >= fs_tab_used) {
+		return (NULL);
+	} else {
+		return (fs_tab[n]->name);
+	}
+}
+
+/*
+ * This returns the remote name of the indicated filesystem.
+ *
+ *	Note: Upon entry, a valid fsys() is required.
+ */
+char *
+get_source_name_n(short n)
+{
+	return (fs_tab[n]->remote_name);
+}
+
+/*
+ * This function returns the srvr_map status based upon the path.
+ */
+int
+use_srvr_map(char *path, short *fsys_value)
+{
+	if (*fsys_value == BADFSYS)
+		*fsys_value = fsys(path);
+
+	return (use_srvr_map_n(*fsys_value));
+}
+
+/*
+ * This function returns the mount status based upon the path.
+ */
+int
+is_mounted(char *path, short *fsys_value)
+{
+	if (*fsys_value == BADFSYS)
+		*fsys_value = fsys(path);
+
+	return (is_mounted_n(*fsys_value));
+}
+
+/*
+ * is_fs_writeable - given a cfent entry, return 1
+ *	if it's writeable, 0 if read-only.
+ *
+ *	Note: Upon exit, a valid fsys() is guaranteed. This is
+ *	an interface requirement.
+ */
+int
+is_fs_writeable(char *path, short *fsys_value)
+{
+	if (*fsys_value == BADFSYS)
+		*fsys_value = fsys(path);
+
+	return (is_fs_writeable_n(*fsys_value));
+}
+
+/*
+ * is_remote_fs - given a cfent entry, return 1
+ *	if it's a remote filesystem, 0 if local.
+ *
+ *	Also Note: Upon exit, a valid fsys() is guaranteed. This is
+ *	an interface requirement.
+ */
+int
+is_remote_fs(char *path, short *fsys_value)
+{
+	if (*fsys_value == BADFSYS)
+		*fsys_value = fsys(path);
+
+	return (is_remote_fs_n(*fsys_value));
+}
+
+/*
+ * This function returns the served status of the filesystem. Served means a
+ * client is getting this file from a server and it is not writeable by the
+ * client. It has nothing to do with whether or not this particular operation
+ * (eg: pkgadd or pkgrm) will be writing to it.
+ */
+int
+is_served(char *path, short *fsys_value)
+{
+	if (*fsys_value == BADFSYS)
+		*fsys_value = fsys(path);
+
+	return (is_served_n(*fsys_value));
+}
+
+/*
+ * get_remote_path - given a filesystem table index, return the
+ *	path of the filesystem on the remote system.  Otherwise,
+ *	return NULL if it's a local filesystem.
+ */
+char *
+get_remote_path(short n)
+{
+	char	*p;
+
+	if (!is_remote_fs_n(n))
+		return (NULL); 	/* local */
+	p = strchr(fs_tab[n]->remote_name, ':');
+	if (!p)
+		p = fs_tab[n]->remote_name; 	/* Loopback */
+	else
+		p++; 	/* remote */
+	return (p);
+}
+
+/*
+ * get_mount_point - given a filesystem table index, return the
+ *	path of the mount point.  Otherwise,
+ *	return NULL if it's a local filesystem.
+ */
+char *
+get_mount_point(short n)
+{
+	if (!is_remote_fs_n(n))
+		return (NULL); 	/* local */
+	return (fs_tab[n]->name);
+}
+
+struct fstable *
+get_fs_entry(short n)
+{
+	if (fs_tab_used == 0) {
+		return (NULL);
+	} else if (n >= fs_tab_used) {
+		return (NULL);
+	} else {
+		return (fs_tab[n]);
+	}
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/usr/src/cmd/svr4pkg/libinst/nblk.c	Fri Jun 05 10:28:40 2009 -0400
@@ -0,0 +1,84 @@
+/*
+ * 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
+ * or http://www.opensolaris.org/os/licensing.
+ * 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.
+ */
+
+/* Copyright (c) 1984, 1986, 1987, 1988, 1989 AT&T */
+/* All Rights Reserved */
+
+
+#include <sys/types.h>
+#include <sys/param.h>
+#include <sys/sysmacros.h>
+
+/*
+ * This should not be a constant, but for ufs it is 12, not 10 like for s5.
+ */
+#define	DIRECT 12	/* Number of logical blocks before indirection */
+
+fsblkcnt_t
+nblk(fsblkcnt_t size, ulong_t bsize, ulong_t frsize)
+{
+	fsblkcnt_t tot, count, count1, d_indirect, t_indirect, ind;
+	fsblkcnt_t frags = 0;
+
+	if (size == 0 || bsize == 0)
+		return (1);
+
+	/*
+	 * Need to keep track of indirect blocks.
+	 */
+
+	ind = howmany(bsize, sizeof (daddr_t));
+	d_indirect = ind + DIRECT; 			/* double indirection */
+	t_indirect = ind * (ind + 1) + DIRECT; 		/* triple indirection */
+
+	tot = howmany(size, bsize);
+
+	if (tot > t_indirect) {
+		count1 = (tot - ind * ind - (DIRECT + 1)) / ind;
+		count = count1 + count1 / ind + ind + 3;
+	} else if (tot > d_indirect) {
+		count = (tot - (DIRECT + 1)) / ind + 2;
+	} else if (tot > DIRECT) {
+		count = 1;
+	} else {
+		count = 0;
+		frags = (frsize > 0) ?
+		    roundup(size, frsize) :
+		    roundup(size, bsize);
+	}
+
+	/* Accounting for the indirect blocks, the total becomes */
+	tot += count;
+
+	/*
+	 * calculate number of 512 byte blocks, for frag or full block cases.
+	 */
+	if (!frags)
+		tot *= howmany(bsize, DEV_BSIZE);
+	else
+		tot = howmany(frags, DEV_BSIZE);
+	return (tot);
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/usr/src/cmd/svr4pkg/libinst/ocfile.c	Fri Jun 05 10:28:40 2009 -0400
@@ -0,0 +1,864 @@
+/*
+ * 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
+ * or http://www.opensolaris.org/os/licensing.
+ * 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.
+ */
+
+/* Copyright (c) 1984, 1986, 1987, 1988, 1989 AT&T */
+/* All Rights Reserved */
+
+
+#include <stdio.h>
+#include <fcntl.h>
+#include <sys/types.h>
+#include <sys/param.h>
+#include <sys/sysmacros.h>
+#include <string.h>
+#include <strings.h>
+#include <sys/wait.h>
+#include <sys/stat.h>
+#include <sys/mman.h>
+#include <sys/statvfs.h>
+#include <signal.h>
+#include <limits.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <time.h>
+#include <errno.h>
+#include <pkglocs.h>
+#include <locale.h>
+#include <libintl.h>
+#include <pkglib.h>
+#include "libinst.h"
+#include "libadm.h"
+
+#define	LOCKFILE	".pkg.lock"
+#define	LOCKWAIT	10	/* seconds between retries */
+#define	LOCKRETRY	20	/* number of retries for a DB lock */
+
+#define	ERR_TC_WRITE	"WARNING: unable to write temp contents file <%s>"
+#define	ERR_NOCLOSE	"WARNING: unable to close <%s>"
+#define	ERR_NOUNLINK_LATENT	"WARNING: unable to unlink latent <%s>"
+#define	ERR_LINK_FAIL	"link(%s, %s) failed (errno %d)"
+#define	ERR_NORENAME_CONTENTS	"unable to establish contents file <%s> "\
+			"from <%s>"
+#define	ERR_RENAME_FAIL	"rename(%s, %s) failed (errno %d)"
+#define	ERR_RESTORE_FAIL	"attempt to restore <%s> failed"
+#define	ERR_NOUNLINK	"WARNING: unable to unlink <%s>"
+#define	ERR_FCLOSE_FAIL	"fclose failed (errno %d)"
+#define	ERR_ERRNO	"(errno %d: %s)"
+#define	ERR_NOTMPOPEN	"unable to open temporary contents file image"
+#define	ERR_CFBACK	"Not enough space to backup <%s>"
+#define	ERR_CREAT_CONT	"unable to create contents file <%s>: %s"
+#define	ERR_ACCESS_CONT	"unable to access contents file <%s>: %s"
+#define	ERR_CFBACK1	"Need=%llu blocks, Available=%llu blocks " \
+			"(block size=%d)"
+#define	ERR_NOCFILE	"unable to locate contents file <%s>"
+#define	ERR_NOROPEN	"unable to open <%s> for reading"
+#define	ERR_NOOPEN	"unable to open <%s> for writing"
+#define	ERR_NOSTAT	"unable to stat contents file <%s>"
+#define	ERR_NOSTATV	"statvfs(%s) failed"
+#define	ERR_NOUPD	"unable to update contents file"
+#define	ERR_DRCONTCP	"unable to copy contents file to <%s>"
+
+#define	MSG_XWTING	"NOTE: Waiting for exclusive access to the package " \
+				"database."
+#define	MSG_NOLOCK	"NOTE: Couldn't lock the package database."
+
+#define	ERR_NOLOCK	"Database lock failed."
+#define	ERR_OPLOCK	"unable to open lock file <%s>."
+#define	ERR_MKLOCK	"unable to create lock file <%s>."
+#define	ERR_LCKREM	"unable to lock package database - remote host " \
+				"unavailable."
+#define	ERR_BADLCK	"unable to lock package database - unknown error."
+#define	ERR_DEADLCK	"unable to lock package database - deadlock condition."
+#define	ERR_TMOUT	"unable to lock package database - too many retries."
+#define	ERR_CFDIR	"unable to locate contents file directory"
+
+static int	active_lock;
+static int	lock_fd;	/* fd of LOCKFILE. */
+static char	*pkgadm_dir;
+
+static int	pkgWlock(int verbose);
+static int	pkgWunlock(void);
+
+/*
+ * This VFP is used to cache the last copy of the contents file that was
+ * written out - upon subsequent open if the contents file has not changed
+ * since it was last written out, use the last cached copy that is still
+ * in memory to avoid re-reading the contents file again. If the contents
+ * file has changed since the cached copy was written out, the previous
+ * copy is discarded and the new contents file contents are read in.
+ */
+
+static VFP_T	*contentsVfp = {(VFP_T *)NULL};
+
+/*
+ * This defines the maximum number of bytes that can be added to the contents
+ * file for a single package - this must be higher than the largest expected
+ * pkgmap file will ever be. This controls the amount of memory allocated for
+ * the contents file additions. A pkgmap file with an average line length of
+ * 128/256/512 bytes could add 62500/31250/15625 entries with this size. This
+ * allows the contents file to have a fixed allocation without having to check
+ * size and realloc as necessary with the attendant cost of the realloc. The
+ * real memory used will only be those pages that are actually touched when
+ * the contents file is written.
+ */
+
+#define	CONTENTS_DELTA	(32*1024*1024)	/* 32mb */
+
+/* forward declarations */
+
+int relslock(void);
+
+/*ARGSUSED*/
+static void
+do_alarm(int n)
+{
+	(void) signal(SIGALRM, SIG_IGN);
+	(void) signal(SIGALRM, do_alarm);
+	(void) alarm(LOCKWAIT);
+}
+
+/*
+ * Point packaging to the appropriate contents file. This is primarily used
+ * to establish a dryrun contents file. If the malloc() doesn't work, this
+ * returns 99 (internal error), else 0.
+ */
+int
+set_cfdir(char *cfdir)
+{
+	char	realcf[PATH_MAX];
+	char	tmpcf[PATH_MAX];
+	int	status;
+
+	if (cfdir == NULL) {
+		pkgadm_dir = get_PKGADM();
+		return (0);
+	}
+
+	if ((pkgadm_dir = strdup(cfdir)) == NULL) {
+		return (99);
+	}
+
+	(void) snprintf(tmpcf, sizeof (tmpcf), "%s/contents", pkgadm_dir);
+
+	/*
+	 * return if a temporary contents file already exists -
+	 * assume it is from a prior package in this series.
+	 */
+
+	if (access(tmpcf, F_OK) == 0) {
+		return (0);
+	}
+
+	/*
+	 * no temporary contents file exists - create one.
+	 */
+
+	(void) snprintf(realcf, sizeof (realcf), "%s/contents", get_PKGADM());
+
+	/*
+	 * If there's a contents file there already, copy it
+	 * over, otherwise initialize one.
+	 */
+
+	/* create new contents file if one does not already exist */
+
+	if (access(realcf, F_OK) != 0) {
+		int n;
+
+		n = open(tmpcf, O_WRONLY|O_CREAT|O_TRUNC|O_EXCL, 0644);
+		if (n < 0) {
+			progerr(gettext(ERR_CREAT_CONT), tmpcf,
+						strerror(errno));
+			return (99);
+		}
+		(void) close(n);
+	} else {
+
+		/* contents file exists, save in pkgadm-dir */
+
+		status = copyf(realcf, tmpcf, (time_t)0);
+		if (status != 0) {
+			progerr(gettext(ERR_DRCONTCP), tmpcf);
+			return (99);
+		}
+	}
+
+	return (0);
+}
+
+/*
+ * This function installs the database lock, opens the contents file for
+ * reading and creates and opens the temporary contents file for read/write.
+ * It returns 1 if successful, 0 otherwise.
+ */
+int
+ocfile(VFP_T **r_mapvfp, VFP_T **r_tmpvfp, fsblkcnt_t map_blks)
+{
+	struct	stat64	statb;
+	struct	statvfs64	svfsb;
+	fsblkcnt_t free_blocks;
+	fsblkcnt_t need_blocks;
+	VFP_T		*mapvfp = (VFP_T *)NULL;
+	VFP_T		*tmpvfp = (VFP_T *)NULL;
+	char		contents[PATH_MAX];
+	int		n;
+
+	/* reset return VFP/FILE pointers */
+
+	(*r_mapvfp) = (VFP_T *)NULL;
+	(*r_tmpvfp) = (VFP_T *)NULL;
+
+	/* establish package administration contents directory location */
+
+	if (pkgadm_dir == NULL) {
+		if (set_cfdir(NULL) != 0) {
+			progerr(gettext(ERR_CFDIR));
+			return (0);
+		}
+	}
+
+	/* Lock the file for exclusive access */
+
+	if (!pkgWlock(1)) {
+		progerr(gettext(ERR_NOLOCK));
+		return (0);
+	}
+
+	/* determine path to the primary contents file */
+
+	(void) snprintf(contents, sizeof (contents), "%s/contents", pkgadm_dir);
+
+	/*
+	 * open the contents file to read only - if a previous contents file has
+	 * been cached attempt to use that cached copy for the open, otherwise
+	 * just open the contents file directly
+	 */
+
+	n = vfpCheckpointOpen(&contentsVfp, &mapvfp, contents, "r", VFP_NONE);
+
+	/* return error if contents file cannot be accessed */
+
+	if (n != 0) {
+		int	lerrno = errno;
+
+		if (errno == ENOENT) {
+			progerr(gettext(ERR_NOCFILE), contents);
+		} else {
+			progerr(gettext(ERR_NOROPEN), contents);
+		}
+
+		logerr(gettext(ERR_ERRNO), lerrno, strerror(lerrno));
+		return (0);
+	}
+
+	/*
+	 * Check and see if there is enough space for the packaging commands
+	 * to back up the contents file, if there is not, then do not allow
+	 * execution to continue by failing the ocfile() call.
+	 */
+
+	/* Get the contents file size */
+
+	if (fstat64(fileno(mapvfp->_vfpFile), &statb) == -1) {
+		int	lerrno = errno;
+
+		progerr(gettext(ERR_NOSTAT), contents);
+		logerr(gettext(ERR_ERRNO), lerrno, strerror(lerrno));
+		(void) vfpClose(&mapvfp);
+		return (0);
+	}
+
+	/* Get the filesystem space */
+
+	if (fstatvfs64(fileno(mapvfp->_vfpFile), &svfsb) == -1) {
+		int	lerrno = errno;
+
+		progerr(gettext(ERR_NOSTATV), contents);
+		logerr(gettext(ERR_ERRNO), lerrno, strerror(lerrno));
+		(void) vfpClose(&mapvfp);
+		return (0);
+	}
+
+	free_blocks = (((fsblkcnt_t)svfsb.f_frsize > 0) ?
+			howmany(svfsb.f_frsize, DEV_BSIZE) :
+			howmany(svfsb.f_bsize, DEV_BSIZE)) * svfsb.f_bfree;
+
+	if (map_blks == 0LL) {
+		map_blks = 10LL;
+	}
+
+	/*
+	 * Calculate the number of blocks we need to be able to operate on
+	 * the contents file.
+	 */
+	need_blocks = map_blks +
+		nblk(statb.st_size, svfsb.f_bsize, svfsb.f_frsize);
+
+	if ((need_blocks + 10) > free_blocks) {
+		progerr(gettext(ERR_CFBACK), contents);
+		progerr(gettext(ERR_CFBACK1), need_blocks, free_blocks,
+			DEV_BSIZE);
+		(void) vfpClose(&mapvfp);
+		return (0);
+	}
+
+	/*
+	 * open the temporary contents file without a path name - this causes
+	 * the "vfp" to be opened on in-memory storage only, the size of which
+	 * is set following a successful return - this causes the temporary
+	 * contents file to be maintained in memory only - if no changes are
+	 * made as the primary contents file is processed, the in memory data
+	 * is discarded and not written to the disk.
+	 */
+
+	if (vfpOpen(&tmpvfp, (char *)NULL, "w", VFP_NONE) != 0) {
+		int	lerrno = errno;
+
+		progerr(gettext(ERR_NOTMPOPEN));
+		logerr(gettext(ERR_ERRNO), lerrno, strerror(lerrno));
+		(void) vfpClose(&mapvfp);
+		return (0);
+	}
+
+	/*
+	 * set size of allocation for temporary contents file - this sets the
+	 * size of the in-memory buffer associated with the open vfp.
+	 */
+
+	if (vfpSetSize(tmpvfp, statb.st_size + CONTENTS_DELTA) != 0) {
+		int	lerrno = errno;
+
+		progerr(gettext(ERR_NOTMPOPEN));
+		logerr(gettext(ERR_ERRNO), lerrno, strerror(lerrno));
+		(void) vfpClose(&tmpvfp);
+		(void) vfpClose(&mapvfp);
+		return (0);
+	}
+
+	/*
+	 * now that the temporary file is opened, advise the vm system to start
+	 * mapping the real contents file into memory if possible
+	 */
+
+	(void) vfpSetFlags(mapvfp, VFP_NEEDNOW);
+
+	/* set return ->s to open vfps */
+
+	(*r_mapvfp) = mapvfp;
+	(*r_tmpvfp) = tmpvfp;
+
+	return (1);	/* All OK */
+}
+
+/*
+ * This is a simple open and lock of the contents file. It doesn't create a
+ * temporary contents file and it doesn't need to do any space checking.
+ * Returns 1 for OK and 0 for "didn't do it".
+ */
+int
+socfile(VFP_T **r_mapvfp)
+{
+	VFP_T	*mapvfp = (VFP_T *)NULL;
+	char	contents[PATH_MAX];
+	int	n;
+
+	/* reset return VFP/FILE pointer */
+
+	(*r_mapvfp) = (VFP_T *)NULL;
+
+	if (pkgadm_dir == NULL) {
+		if (set_cfdir(NULL) != 0) {
+			progerr(gettext(ERR_CFDIR));
+			return (0);
+		}
+	}
+
+	/*
+	 * Lock the database for exclusive access, but don't make a fuss if
+	 * it fails (user may not be root and the .pkg.lock file may not
+	 * exist yet).
+	 */
+
+	if (!pkgWlock(0)) {
+		logerr(gettext(MSG_NOLOCK));
+	}
+
+	/* open the contents file to read only */
+
+	(void) snprintf(contents, sizeof (contents), "%s/contents", pkgadm_dir);
+
+	n = vfpCheckpointOpen(&contentsVfp, &mapvfp, contents,
+							"r", VFP_NEEDNOW);
+	if (n != 0) {
+		int lerrno = errno;
+
+		if (errno == ENOENT) {
+			progerr(gettext(ERR_NOCFILE), contents);
+		} else {
+			progerr(gettext(ERR_NOROPEN), contents);
+		}
+		logerr(gettext(ERR_ERRNO), lerrno, strerror(lerrno));
+		return (0);
+	}
+
+	*r_mapvfp = mapvfp;
+
+	return (1);
+}
+
+/*
+ * Name:	swapcfile
+ * Description: This function closes both the current and temporary contents
+ *		files specified, and conditionally replaces the old transitory
+ *		contents file with the newly updated temporary contents file.
+ *		The "ocfile()" or "socfile()" functions must be called to re-
+ *		open the real contents file for processing.
+ * Arguments:	a_cfVfp - (VFP_T **) - [RW, *RW]
+ *			This is the VFP associated with the real contents file
+ *			that is being read from and data processed.
+ *		a_cfTmpVfp - (VFP_T **) - [RW, *RW]
+ *			This is the VFP associated with the temporary contents
+ *			file that is being written to.
+ *		pkginst - (char) - [RO, *RO]
+ *			This is the name of the package being operated on;
+ *			this is used to write the "last modified by xxx"
+ *			comment at the end of the contents file.
+ *		dbchg - (int) - [RO]
+ *			== 0 - the temporary contents file has NOT been changed
+ *				with respect to the real contents file; do not
+ *				update the real contents file with the contents
+ *				of the temporary contents file.
+ *			!= 0 - the temporary contetns file HAS been changed with
+ *				respect to the real contents file; DO update the
+ *				real contents file with the contents of the
+ *				temporary contents file.
+ * Returns:	int	== RESULT_OK - successful
+ *			== RESULT_WRN - successful with warnings
+ *			== RESULT_ERR - failed with fatal errors - deserves an
+ *				alarming message and a quit()
+ * NOTES: If dbchg != 0, the contents file is always updated. If dbchg == 0,
+ *		the contents file is updated IF the data is modified indication
+ *		is set on the contents file associated with a_cfTmpVfp.
+ */
+
+int
+swapcfile(VFP_T **a_cfVfp, VFP_T **a_cfTmpVfp, char *pkginst, int dbchg)
+{
+	char	*pe;
+	char	*pl;
+	char	*ps;
+	char	contentsPath[PATH_MAX] = {'\0'};
+	char	line[256];
+	char	sContentsPath[PATH_MAX] = {'\0'};
+	char	tContentsPath[PATH_MAX] = {'\0'};
+	char	timeb[BUFSIZ];
+	int	retval = RESULT_OK;
+	struct tm	*timep;
+	time_t	clock;
+
+	/* normalize pkginst so its never null */
+
+	if (pkginst == (char *)NULL) {
+		dbchg = 0;
+		pkginst = "<unknown>";
+	}
+
+	/* cache all paths for the associated open files */
+
+	(void) strlcpy(contentsPath, vfpGetPath(*a_cfVfp),
+			sizeof (contentsPath));
+
+	(void) snprintf(tContentsPath, sizeof (tContentsPath),
+			"%s/t.contents", pkgadm_dir);
+
+	(void) snprintf(sContentsPath, sizeof (sContentsPath),
+			"%s/s.contents", pkgadm_dir);
+
+	/* original contents file no longer needed - close */
+
+	if (vfpClose(a_cfVfp) != 0) {
+		int	lerrno = errno;
+
+		logerr(gettext(ERR_NOCLOSE), contentsPath);
+		logerr(gettext(ERR_ERRNO), lerrno, strerror(lerrno));
+		retval = RESULT_WRN;
+	}
+
+	/*
+	 * If no changes were made to the database, checkpoint the temporary
+	 * contents file - if this fails, then just close the file which causes
+	 * the contents file to be reopened and reread if it is needed again
+	 */
+
+	if ((dbchg == 0) && (vfpGetModified(*a_cfTmpVfp) == 0)) {
+		if (vfpCheckpointFile(&contentsVfp, a_cfTmpVfp,
+							contentsPath) != 0) {
+			vfpClose(a_cfTmpVfp);
+		}
+		(void) pkgWunlock();	/* Free the database lock. */
+		return (retval);
+	}
+
+	/*
+	 * changes made to the current temporary contents file -
+	 * remove any trailing comment lines in the temp contents file, then
+	 * append updated modification info records to temp contents file
+	 */
+
+	pe = vfpGetCurrCharPtr(*a_cfTmpVfp);	/* last char in contents file */
+	ps = vfpGetFirstCharPtr(*a_cfTmpVfp);	/* 1st char in contents file */
+	pl = pe;	/* last match is last char in contents file */
+
+	/* skip past all trailing newlines and null bytes */
+
+	while ((pe > ps) && ((*pe == '\n') || (*pe == '\0'))) {
+		pe--;
+	}
+
+	/* remove trailing comments as long as there are lines in the file */
+
+	while (pe > ps) {
+		if (*pe != '\n') {
+			/* curr char is not newline: backup one byte */
+			pl = pe--;
+		} else if (*pl != '#') {
+			/* curr char is newline next char not comment break */
+			break;
+		} else {
+			/* curr char is newline next char is comment - remove */
+			*pl = '\0';
+			vfpSetLastCharPtr(*a_cfTmpVfp, pl);
+			pe--;
+		}
+	}
+
+	/* create two update comment lines */
+
+	(void) time(&clock);
+	timep = localtime(&clock);
+
+	(void) strftime(timeb, sizeof (timeb), "%c\n", timep);
+	(void) snprintf(line, sizeof (line),
+		gettext("# Last modified by %s for %s package\n# %s"),
+		get_prog_name(), pkginst, timeb);
+	vfpPuts(*a_cfTmpVfp, line);
+
+	/* commit temporary contents file bytes to storage */
+
+	if (vfpWriteToFile(*a_cfTmpVfp, tContentsPath) != 0) {
+		int	lerrno = errno;
+
+		logerr(gettext(ERR_TC_WRITE), tContentsPath);
+		logerr(gettext(ERR_ERRNO), lerrno, strerror(lerrno));
+		vfpClose(a_cfTmpVfp);
+		(void) remove(tContentsPath);
+		(void) pkgWunlock();	/* Free the database lock. */
+		return (RESULT_ERR);
+	}
+
+	/*
+	 * Now we want to make a copy of the old contents file as a
+	 * fail-safe. In support of that, we create a hard link to
+	 * s.contents.
+	 */
+
+	if ((access(sContentsPath, F_OK) == 0) && remove(sContentsPath)) {
+		int	lerrno = errno;
+
+		logerr(gettext(ERR_NOUNLINK_LATENT), sContentsPath);
+		logerr(gettext(ERR_ERRNO), lerrno, strerror(lerrno));
+		(void) remove(tContentsPath);
+		(void) pkgWunlock();	/* Free the database lock. */
+		vfpClose(a_cfTmpVfp);
+		return (RESULT_ERR);
+	}
+
+	if (link(contentsPath, sContentsPath) != 0) {
+		int	lerrno = errno;
+
+		progerr(gettext(ERR_NOUPD));
+		logerr(gettext(ERR_LINK_FAIL), contentsPath, sContentsPath,
+			lerrno);
+		(void) remove(tContentsPath);
+		(void) pkgWunlock();	/* Free the database lock. */
+		vfpClose(a_cfTmpVfp);
+		return (RESULT_ERR);
+	}
+
+	if (rename(tContentsPath, contentsPath) != 0) {
+		int	lerrno = errno;
+
+		progerr(gettext(ERR_NORENAME_CONTENTS), contentsPath,
+			tContentsPath);
+		logerr(gettext(ERR_RENAME_FAIL), tContentsPath,
+			contentsPath, lerrno);
+		if (rename(sContentsPath, contentsPath)) {
+			lerrno = errno;
+			progerr(gettext(ERR_RESTORE_FAIL), contentsPath);
+			logerr(gettext(ERR_RENAME_FAIL), sContentsPath,
+				contentsPath, lerrno);
+		}
+		(void) remove(tContentsPath);
+	}
+
+	if (remove(sContentsPath) != 0) {
+		int	lerrno = errno;
+
+		logerr(gettext(ERR_NOUNLINK), sContentsPath);
+		logerr(gettext(ERR_ERRNO), lerrno, strerror(lerrno));
+		retval = RESULT_WRN;
+	}
+
+	/*
+	 * checkpoint the temporary contents file - if this fails, then
+	 * just close the file which causes the contents file to be reopened
+	 * and reread if it is needed again
+	 */
+
+	if (vfpCheckpointFile(&contentsVfp, a_cfTmpVfp, contentsPath) != 0) {
+		vfpClose(a_cfTmpVfp);
+	}
+
+	return (relslock() == 0 ? RESULT_ERR : retval);
+}
+
+/* This function releases the lock on the package database. */
+int
+relslock(void)
+{
+	/*
+	 * This closes the contents file and releases the lock.
+	 */
+	if (!pkgWunlock()) {
+		int	lerrno = errno;
+
+		progerr(gettext(ERR_NOUPD));
+		logerr(gettext(ERR_FCLOSE_FAIL), lerrno);
+		return (0);
+	}
+	return (1);
+}
+
+/*
+ * This function attempts to lock the package database. It returns 1 on
+ * success, 0 on failure. The positive logic verbose flag determines whether
+ * or not the function displays the error message upon failure.
+ */
+static int
+pkgWlock(int verbose) {
+	int retry_cnt, retval;
+	char lockpath[PATH_MAX];
+
+	active_lock = 0;
+
+	(void) snprintf(lockpath, sizeof (lockpath),
+			"%s/%s", pkgadm_dir, LOCKFILE);
+
+	retry_cnt = LOCKRETRY;
+
+	/*
+	 * If the lock file is not present, create it. The mode is set to
+	 * allow any process to lock the database, that's because pkgchk may
+	 * be run by a non-root user.
+	 */
+	if (access(lockpath, F_OK) == -1) {
+		lock_fd = open(lockpath, O_RDWR|O_CREAT|O_TRUNC|O_EXCL, 0644);
+		if (lock_fd < 0) {
+			if (verbose)
+				progerr(gettext(ERR_MKLOCK), lockpath);
+			return (0);
+		} else {
+			(void) fchmod(lock_fd, 0644);	/* force perms. */
+		}
+	} else {
+		if ((lock_fd = open(lockpath, O_RDWR)) == -1) {
+			if (verbose)
+				progerr(gettext(ERR_OPLOCK), lockpath);
+			return (0);
+		}
+	}
+
+	(void) signal(SIGALRM, do_alarm);
+	(void) alarm(LOCKWAIT);
+
+	do {
+		if (lockf(lock_fd, F_LOCK, 0)) {
+			if (errno == EAGAIN || errno == EINTR)
+				logerr(gettext(MSG_XWTING));
+			else if (errno == ECOMM) {
+				logerr(gettext(ERR_LCKREM));
+				retval = 0;
+				break;
+			} else if (errno == EBADF) {
+				logerr(gettext(ERR_BADLCK));
+				retval = 0;
+				break;
+			} else if (errno == EDEADLK) {
+				logerr(gettext(ERR_DEADLCK));
+				retval = 0;
+				break;
+			}
+		} else {
+			active_lock = 1;
+			retval = 1;
+			break;
+		}
+	} while (retry_cnt--);
+
+	(void) signal(SIGALRM, SIG_IGN);
+
+	if (retval == 0)
+	{
+		if (retry_cnt == -1) {
+			logerr(gettext(ERR_TMOUT));
+		}
+
+		(void) pkgWunlock();	/* close the lockfile. */
+	}
+
+	return (retval);
+}
+
+/*
+ * Release the lock on the package database. Returns 1 on success, 0 on
+ * failure.
+ */
+static int
+pkgWunlock(void) {
+	if (active_lock) {
+		active_lock = 0;
+		if (close(lock_fd))
+			return (0);
+		else
+			return (1);
+	} else
+		return (1);
+}
+
+/*
+ * This function verifies that the contents file is in place.
+ * returns 1 - if it exists
+ * returns 0 - if it does not exist
+ */
+int
+iscfile(void)
+{
+	char	contents[PATH_MAX];
+
+	(void) snprintf(contents, PATH_MAX, "%s/contents", get_PKGADM());
+
+	return (access(contents, F_OK) == 0 ? 1 : 0);
+}
+
+/*
+ * This function verifies that the contents file is in place. If it is - no
+ * change. If it isn't - this creates it.
+ * Returns:	== 0 : failure
+ *		!= 0 : success
+ */
+
+int
+vcfile(void)
+{
+	int	lerrno;
+	int	fd;
+	char	contents[PATH_MAX];
+
+	/*
+	 * create full path to contents file
+	 */
+
+	(void) snprintf(contents, sizeof (contents),
+			"%s/contents", get_PKGADM());
+
+	/*
+	 * Attempt to create the file - will only be successful
+	 * if the file does not currently exist.
+	 */
+
+	fd = open(contents, O_WRONLY | O_CREAT | O_EXCL, 0644);
+	if (fd >= 0) {
+		/*
+		 * Contents file wasn't there, but is now.
+		 */
+
+		echo(gettext("## Software contents file initialized"));
+		(void) close(fd);
+		return (1);	/* success */
+	}
+
+	/*
+	 * Could not create the file - it may exist or there may be
+	 * permissions issues - find out and act accordingly.
+	 */
+
+	lerrno = errno;
+
+	/* success if error is 'file exists' */
+
+	if (lerrno == EEXIST) {
+		return (1);	/* success */
+	}
+
+	/* success if error is 'permission denied' but file exists */
+
+	if (lerrno == EACCES) {
+		/*
+		 * Because O_CREAT and O_EXCL are specified in open(),
+		 * if the contents file already exists, the open will
+		 * fail with EACCES - determine if this is the case -
+		 * if so return success.
+		 */
+
+		if (access(contents, F_OK) == 0) {
+			return (1);	/* success */
+		}
+
+		/*
+		 * access() failed - if because of permissions failure this
+		 * means the contents file exists but it cannot be accessed
+		 * or the path to the contents file cannot be accessed - in
+		 * either case the contents file cannot be accessed.
+		 */
+
+		if (errno == EACCES) {
+			progerr(gettext(ERR_ACCESS_CONT), contents,
+					strerror(lerrno));
+			logerr(gettext(ERR_ERRNO), lerrno, strerror(lerrno));
+			return (0);	/* failure */
+		}
+	}
+
+	/*
+	 * the contents file does not exist and it cannot be created.
+	 */
+
+	progerr(gettext(ERR_CREAT_CONT), contents, strerror(lerrno));
+	logerr(gettext(ERR_ERRNO), lerrno, strerror(lerrno));
+	return (0);	/* failure */
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/usr/src/cmd/svr4pkg/libinst/open_package_datastream.c	Fri Jun 05 10:28:40 2009 -0400
@@ -0,0 +1,298 @@
+/*
+ * 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
+ * or http://www.opensolaris.org/os/licensing.
+ * 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 2006 Sun Microsystems, Inc.  All rights reserved.
+ * Use is subject to license terms.
+ */
+
+
+#include <stdio.h>
+#include <time.h>
+#include <wait.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <ulimit.h>
+#include <sys/stat.h>
+#include <sys/statvfs.h>
+#include <assert.h>
+#include <fcntl.h>
+#include <errno.h>
+#include <ctype.h>
+#include <dirent.h>
+#include <string.h>
+#include <signal.h>
+#include <locale.h>
+#include <libintl.h>
+#include <pkgstrct.h>
+#include <pkginfo.h>
+#include <pkgdev.h>
+#include <pkglocs.h>
+#include <pwd.h>
+#include <pkglib.h>
+#include <libinst.h>
+#include <libadm.h>
+#include <messages.h>
+
+/*
+ * *****************************************************************************
+ * global external (public) functions
+ * *****************************************************************************
+ */
+
+/*
+ * open package datastream
+ * Arguments:	a_argc - (int) - [RO, *RO]
+ *			- number of arguments available in a_argv
+ *		a_argv - (char **) - [RO, *RO]
+ *			- arguments representing package names to add
+ *		a_spoolDir - (char *) - [RO, *RO]
+ *			- directory to write the package (spool) into
+ *			- if == (char *)NULL then install the packages
+ *			- if != (char *)NULL then write packages into directory
+ *		a_device - (char *) - [RO, *RO]
+ *			- device to read packages from when spooling
+ *			- ignored if a_spoolDir == (char *)NULL
+ *		r_repeat - (int *) - [RO, *RW]
+ *			- set == 0 if no further package names in argc/argv
+ *			- set != 0 IF there are package names in argc/argv
+ *			- if == (int *)NULL - not set
+ *		r_idsName - (char **) - [RW, *RW]
+ *			- set to the name of package input data stream device
+ *			- if == (char *)NULL - no input data stream; that is,
+ *			-- the packages are in a directory and not in a stream
+ *			- if != (char *)NULL - this is the device/file that
+ *			-- is the datastream that contains the packages to add
+ *		a_pkgdev - (struct pkgdev *) - [RO, *RW]
+ *			- pkgdev structure containing package device to open
+ * Returns:	B_TRUE - datastream opened successfully
+ *		B_FALSE - datastream failed to open
+ */
+
+boolean_t
+open_package_datastream(int a_argc, char **a_argv, char *a_spoolDir,
+	char *a_device, int *r_repeat, char **r_idsName, char *a_tmpdir,
+	struct pkgdev *a_pkgdev, int a_optind)
+{
+	int	n;
+
+	/* entry assertions */
+
+	assert(a_argv != (char **)NULL);
+	assert(r_idsName != (char **)NULL);
+	assert(a_tmpdir != (char *)NULL);
+	assert(a_pkgdev != (struct pkgdev *)NULL);
+
+	/* entry debug information */
+
+	echoDebug(DBG_ODS_ENTRY);
+	echoDebug(DBG_ODS_ARGS,
+			a_pkgdev->bdevice ? a_pkgdev->bdevice : "?",
+			a_pkgdev->cdevice ? a_pkgdev->cdevice : "?",
+			a_pkgdev->pathname ? a_pkgdev->pathname : "?",
+			a_argc, a_device ? a_device : "?");
+
+	/* reset possible return values to defaults */
+
+	*r_idsName = (char *)NULL;
+	if (r_repeat != (int *)NULL) {
+		*r_repeat = 0;
+	}
+
+	/*
+	 * Determine how to access the package source "device":
+	 * - if a block device is associated with the source:
+	 * -- make sure the next "volume" is mounted and ready.
+	 * -- input data stream is associated character device
+	 * - if char device but no block device associated with device:
+	 * -- input data stream is associated character device
+	 * - else if a path is associated with device:
+	 * -- input data stream is associated path
+	 */
+
+	if (a_pkgdev->bdevice != (char *)NULL) {
+		/* package source is block device */
+
+		/*
+		 * _getvol verifies that the specified device is accessible and
+		 * that a volume of the appropriate medium has been inserted.
+		 * _getvol is in libadm.h - delivered by ON as part of SUNWcsl
+		 * is somewhat analagous to getvol(1M) - args are:
+		 *  - char *device
+		 *  - char *label
+		 *  - int options
+		 *  - char *prompt
+		 *  - char *norewind - no rewind device (NULL to use device)
+		 * Returns:
+		 *	0 - okay, label matches
+		 *	1 - device not accessable
+		 *	2 - unknown device (devattr failed)
+		 *	3 - user selected quit
+		 *	4 - label does not match
+		 */
+
+		echoDebug(DBG_ODS_DATASTREAM_BDEV, a_pkgdev->bdevice);
+
+		n = _getvol(a_pkgdev->bdevice, NULL, 0L,
+				MSG_INSERT_VOL, a_pkgdev->norewind);
+
+		switch (n) {
+		case 0:	/* volume open, label matches */
+			if (ds_readbuf(a_pkgdev->cdevice)) {
+				(*r_idsName) = a_pkgdev->cdevice;
+			}
+			break;
+		case 3:	/* user selected quit */
+			quit(3);
+			/* NOTREACHED */
+		case 2:	/* unknown device (devattr failed) */
+			progerr(ERR_UNKNOWN_DEV, a_pkgdev->name);
+			quit(99);
+			/* NOTREACHED */
+		default:	/* device not accessable */
+			progerr(ERR_PKGVOL);
+			logerr(LOG_GETVOL_RET, n);
+			quit(99);
+			/* NOTREACHED */
+		}
+	} else if (a_pkgdev->cdevice != (char *)NULL) {
+		/* package source is character device */
+
+		echoDebug(DBG_ODS_DATASTREAM_CDEV, a_pkgdev->cdevice);
+
+		(*r_idsName) = a_pkgdev->cdevice;
+	} else if (a_pkgdev->pathname != (char *)NULL) {
+		/* package source is path name to file */
+
+		echoDebug(DBG_ODS_DATASTREAM_ISFILE, a_pkgdev->pathname);
+
+		(*r_idsName) = a_pkgdev->pathname;
+	} else {
+		echoDebug(DBG_ODS_DATASTREAM_UNK);
+	}
+
+	/*
+	 * If writing the packages into a spool directory instead of
+	 * installing the packages, invoke pkgtrans to perform the
+	 * conversion and exit.
+	 */
+
+	if (a_spoolDir) {
+		return (B_TRUE);
+	}
+
+	/* create temp dir for op if input data stream specified */
+
+	if (*r_idsName) {
+		/*
+		 * initialize datastream,
+		 * dirname is set to directory where package is unstreamed
+		 */
+		if (setup_temporary_directory(&a_pkgdev->dirname, a_tmpdir,
+			"dstream") == B_FALSE) {
+			progerr(ERR_STREAMDIR, strerror(errno));
+			quit(99);
+			/* NOTREACHED */
+		}
+	}
+
+	if (r_repeat != (int *)NULL) {
+		*r_repeat = (a_optind >= a_argc);
+	}
+
+	/*
+	 * mount source device (e.g. floppy) if no input data stream
+	 * specified, and the package source device is mountable. If
+	 * the pkgmount fails, go back and try to mount the package
+	 * source again. When a package is split up into multiple
+	 * volumes (such as floppies), it might be possible to go back
+	 * and insert a different copy of the required volume/floppy
+	 * if the current one cannot be mounted. Otherwise this could
+	 * have just called quit() if the mount failed...
+	 */
+
+	if (((*r_idsName) == (char *)NULL) && a_pkgdev->mount) {
+		echoDebug(DBG_ODS_DATASTREAM_MOUNTING, *r_idsName,
+							a_pkgdev->mount);
+		a_pkgdev->rdonly++;
+		n = pkgmount(a_pkgdev, NULL, 0, 0, 0);
+		if (n != 0) {
+			/* pkgmount failed */
+			return (B_FALSE);
+		}
+	}
+
+	/*
+	 * open and initialize input data stream if specified
+	 */
+
+	if ((*r_idsName) != (char *)NULL) {
+		echoDebug(DBG_ODS_DATASTREAM_INIT, *r_idsName);
+
+		/* use character device to force rewind of datastream */
+		if ((a_pkgdev->cdevice != (char *)NULL) &&
+			(a_pkgdev->bdevice == (char *)NULL)) {
+			n = _getvol(a_pkgdev->name, NULL, 0L, NULL,
+					a_pkgdev->norewind);
+
+			switch (n) {
+			case 0:	/* volume open, label matches */
+				break;
+			case 3:	/* user selected quit */
+				quit(3);
+				/* NOTREACHED */
+			case 2:	/* unknown device (devattr failed) */
+				progerr(ERR_UNKNOWN_DEV, a_pkgdev->name);
+				quit(99);
+				/* NOTREACHED */
+			default:
+				progerr(ERR_PKGVOL);
+				logerr(LOG_GETVOL_RET, n);
+				quit(99);
+				/* NOTREACHED */
+			}
+		}
+
+		if (chdir(a_pkgdev->dirname)) {
+			progerr(ERR_CHDIR, a_pkgdev->dirname);
+			quit(99);
+			/* NOTREACHED */
+		}
+
+		/*
+		 * initialize datastream for subsequent installation;
+		 * read the source device;
+		 * aquire the header data and check it for validity;
+		 * creates subdirectories in package stream directory
+		 * (a_pkgdev->dirname) for each package and retrieves each
+		 * packages pkginfo and pkgmap files
+		 */
+
+		if (ds_init(*r_idsName, &a_argv[a_optind],
+						a_pkgdev->norewind)) {
+			progerr(ERR_DSINIT, *r_idsName);
+			quit(99);
+			/* NOTREACHED */
+		}
+	}
+
+	return (B_TRUE);
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/usr/src/cmd/svr4pkg/libinst/pathdup.c	Fri Jun 05 10:28:40 2009 -0400
@@ -0,0 +1,158 @@
+/*
+ * 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
+ * or http://www.opensolaris.org/os/licensing.
+ * 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 2006 Sun Microsystems, Inc.  All rights reserved.
+ * Use is subject to license terms.
+ */
+
+/* Copyright (c) 1984, 1986, 1987, 1988, 1989 AT&T */
+/* All Rights Reserved */
+
+
+
+#include <limits.h>
+#include <string.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <errno.h>
+#include <locale.h>
+#include <libintl.h>
+#include <pkglib.h>
+#include <libadm.h>
+
+#define	ERR_MEMORY	"memory allocation failure, errno=%d"
+
+/*
+ * using factor of eight limits maximum
+ * memory fragmentation to 12.5%
+ */
+#define	MEMSIZ	PATH_MAX*8
+#define	NULL	0
+
+struct dup {
+	char	mem[MEMSIZ];
+	struct dup *next;
+};
+
+static struct dup *head, *tail, *new;
+
+static int	size, initialized;
+static void	pathinit();
+static void	growstore();
+
+/*
+ * These functions allocate space for all the path names required
+ * in the packaging code. They are all allocated here so as to reduce
+ * memory fragmentation.
+ */
+
+/* Initialize storage area. */
+static void
+pathinit()
+{
+	if (head == NULL)
+		size = (-1);
+	else {
+		/* free all memory used except initial structure */
+		tail = head->next;
+		while (tail) {
+			new = tail->next;
+			free(tail);
+			tail = new;
+		}
+		tail = head;
+		size = MEMSIZ;
+	}
+
+	initialized = 1;
+}
+
+/* Allocate additional space for storage area. */
+static void
+growstore()
+{
+	/* need more memory */
+	new = calloc(1, sizeof (struct dup));
+	if (new == NULL) {
+		progerr(gettext(ERR_MEMORY), errno);
+		quit(99);
+	}
+	if (head == NULL)
+		head = new;
+	else
+		tail->next = new;
+	tail = new;
+	size = MEMSIZ;
+}
+
+/* Allocate and return a pointer. If n == 0, initialize. */
+char *
+pathalloc(int n)
+{
+	char	*pt;
+
+	if (n <= 0) {
+		pathinit();
+		pt = NULL;
+	} else {
+		if (!initialized)
+			pathinit();
+
+		n++;	/* Account for terminating null. */
+
+		if (size < n)
+			growstore();
+
+		pt = &tail->mem[MEMSIZ-size];
+		size -= n;
+	}
+
+	return (pt);
+}
+
+/* Allocate and insert a pathname returning a pointer to the new string. */
+char *
+pathdup(char *s)
+{
+	char	*pt;
+	int	n;
+
+	if (s == NULL) {
+		pathinit();
+		pt = NULL;
+	} else {
+		if (!initialized)
+			pathinit();
+
+		n = strlen(s) + 1;	/* string + null terminator */
+
+		if (size < n)
+			growstore();
+
+		pt = &tail->mem[MEMSIZ-size];
+		size -= n;
+
+		(void) strcpy(pt, s);
+	}
+
+	return (pt);
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/usr/src/cmd/svr4pkg/libinst/pkgdbmerg.c	Fri Jun 05 10:28:40 2009 -0400
@@ -0,0 +1,1259 @@
+/*
+ * 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
+ * or http://www.opensolaris.org/os/licensing.
+ * 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 2009 Sun Microsystems, Inc.  All rights reserved.
+ * Use is subject to license terms.
+ */
+
+/* Copyright (c) 1984, 1986, 1987, 1988, 1989 AT&T */
+/* All Rights Reserved */
+
+
+#include <stdio.h>
+#include <signal.h>
+#include <string.h>
+#include <errno.h>
+#include <unistd.h>
+#include <stdlib.h>
+#include <assert.h>
+#include <pkgstrct.h>
+#include <sys/stat.h>
+#include <locale.h>
+#include <libintl.h>
+#include <pkginfo.h>
+#include <instzones_api.h>
+#include <pkglib.h>
+#include <libinst.h>
+#include <messages.h>
+
+/* merg() return codes */
+#define	MRG_SAME	0
+#define	MRG_DIFFERENT	1
+#define	MRG_REPLACE	2
+
+/* typechg() return codes */
+#define	TYPE_OK		0
+#define	TYPE_WARNING	1
+#define	TYPE_IGNORED	2
+#define	TYPE_REPLACE	3
+#define	TYPE_FATAL	4
+
+/* message pool */
+#define	ERR_OUTPUT	"unable to update package database"
+#define	ERR_PINFO	"missing pinfo structure for <%s>"
+#define	INFO_PROCESS	"   %2ld%% of information processed; continuing ..."
+
+#define	WRN_NOTFILE	"WARNING: %s <no longer a regular file>"
+#define	WRN_NOTSYMLN	"WARNING: %s <no longer a symbolic link>"
+#define	WRN_NOTLINK	"WARNING: %s <no longer a linked file>"
+#define	WRN_NOTDIR	"WARNING: %s <no longer a directory>"
+#define	WRN_NOTCHAR	"WARNING: %s <no longer a character special device>"
+#define	WRN_NOTBLOCK	"WARNING: %s <no longer a block special device>"
+#define	WRN_NOTPIPE	"WARNING: %s <no longer a named pipe>"
+#define	WRN_TOEXCL	"WARNING: cannot convert %s to an exclusive directory."
+#define	WRN_ODDVERIFY	"WARNING: quick verify disabled for class %s."
+
+#define	MSG_TYPIGN	"Object type change ignored."
+#define	MSG_TYPE_ERR	"Package attempts fatal object type change."
+
+extern char	*pkginst;
+extern int	nosetuid, nocnflct, otherstoo;
+
+/* pkgobjmap.c */
+extern int	cp_cfent(struct cfent *cf_ent, struct cfextra *el_ent);
+
+/* setlist.c */
+extern void	cl_def_dverify(int idx);
+
+char dbst = '\0';	/* usually set by installf() or removef() */
+
+int files_installed(void);	/* return number of files installed. */
+
+static int	errflg = 0;
+static int	eptnum;
+static VFP_T	*fpvfp = {(VFP_T *)NULL};
+static long	sizetot;
+static int	seconds;
+static int	installed;	/* # of files, already properly installed. */
+static struct	pinfo	*pkgpinfo = (struct pinfo *)0;
+
+static int	is_setuid(struct cfent *ent);
+static int	is_setgid(struct cfent *ent);
+static int	merg(struct cfextra *el_ent, struct cfent *cf_ent);
+static int	do_like_ent(VFP_T *vfpo, struct cfextra *el_ent,
+		    struct cfent *cf_ent, int ctrl);
+static int	do_new_ent(VFP_T *vfpo, struct cfextra *el_ent, int ctrl);
+static int	typechg(struct cfent *el_ent, struct cfent *cf_ent,
+		    struct mergstat *mstat);
+
+static void	set_change(struct cfextra *el_ent);
+static void	chgclass(struct cfent *cf_ent, struct pinfo *pinfo);
+static void	output(VFP_T *vfpo, struct cfent *ent, struct pinfo *pinfo);
+
+/* ARGSUNUSED */
+void
+notice(int n)
+{
+#ifdef lint
+	int i = n;
+	n = i;
+#endif	/* lint */
+	(void) signal(SIGALRM, SIG_IGN);
+	if (sizetot != 0) {
+		echo(gettext(INFO_PROCESS),
+			vfpGetBytesRemaining(fpvfp) * 100L / sizetot);
+	}
+	(void) signal(SIGALRM, notice);
+	(void) alarm(seconds);
+}
+
+/* ARGSUSED */
+
+/*
+ * This scans the extlist (pkgmap) and the package database to the end,
+ * copying out the merged contents to the file at tmpfp. It updates the mergstat
+ * structures and deals with administrative defaults regarding setuid and
+ * conflict.
+ *
+ * Since both the extlist and the package database entries are in numerical
+ * order, they both scan unidirectionally. If the entry in the extlist is
+ * found in the package database (by pathname) then do_like_ent() is called.
+ * If the extlist entry is not found in the package database then
+ * do_new_ent() is called. srchcfile() is responsible for copying out
+ * non-matching package database entries. At package database EOF, the
+ * eocontents flag is set and the rest of the extlist are assumed to be new
+ * entries. At the end of the extlist, the eoextlist flag is set and the
+ * remaining package database ends up copied out by srchcfile().
+ */
+
+int
+pkgdbmerg(VFP_T *mapvfp, VFP_T *tmpvfp, struct cfextra **extlist, int notify)
+{
+	static	struct	cfent	cf_ent;	/* scratch area */
+	struct	cfextra	*el_ent;	/* extlist entry under review */
+	int	eocontents = 0;
+	int	eoextlist = 0;
+	int	n;
+	int	changed;
+	int	assume_ok = 0;
+
+	cf_ent.pinfo = (NULL);
+	errflg = 0;
+	eptnum = 0;
+	installed = changed = 0;
+
+	fpvfp = mapvfp;	/* for notice function ...arg! */
+
+	if (notify) {
+		seconds = notify;
+		(void) signal(SIGALRM, notice);
+		(void) alarm(seconds);
+	}
+
+	(void) sighold(SIGALRM);
+
+	sizetot = (((ptrdiff_t)(mapvfp->_vfpEnd)) -
+				((ptrdiff_t)(mapvfp->_vfpStart)));
+	vfpRewind(mapvfp);
+	vfpRewind(tmpvfp);
+
+	(void) sigrelse(SIGALRM);
+
+	do {
+		(void) sighold(SIGALRM);
+
+		/*
+		 * If there's an entry in the extlist at this position,
+		 * process that entry.
+		 */
+		if (!eoextlist && (el_ent = extlist[eptnum])) {
+
+			/* Metafiles don't get merged. */
+			if ((el_ent->cf_ent.ftype == 'i') ||
+				(el_ent->cf_ent.ftype == 'n')) {
+				continue;
+			}
+
+			/*
+			 * Copy cfextra structure for duplicated paths.
+			 * This is not just an optimization, it is
+			 * necessary for correct operation of algorithm.
+			 */
+			if ((eptnum > 0) && (strncmp(el_ent->cf_ent.path,
+			    extlist[eptnum-1]->cf_ent.path, PATH_MAX) == 0)) {
+				memcpy(extlist[eptnum], extlist[eptnum-1],
+				    sizeof (struct cfextra));
+				continue;
+			}
+
+			/*
+			 * Normally dbst comes to us from installf() or
+			 * removef() in order to specify their special
+			 * database status codes. They cannot implement a
+			 * quick verify (it just doesn't make sense). For
+			 * that reason, we can test to see if we already have
+			 * a special database status. If we don't (it's from
+			 * pkgadd) then we can test to see if this is calling
+			 * for a quick verify wherein we assume the install
+			 * will work and fix it if it doesn't. In that case
+			 * we set our own dbst to be ENTRY_OK.
+			 */
+			if (dbst == '\0') {
+				if (cl_dvfy(el_ent->cf_ent.pkg_class_idx) ==
+				    QKVERIFY) {
+					assume_ok = 1;
+				}
+			} else {
+				/*
+				 * If we DO end up with an installf/quick
+				 * verify combination, we fix that by simply
+				 * denying the quick verify for this class.
+				 * This forces everything to come out alright
+				 * by forcing the standard assumptions as
+				 * regards package database for the rest of
+				 * the load.
+				 */
+				if (cl_dvfy(el_ent->cf_ent.pkg_class_idx) ==
+				    QKVERIFY) {
+					logerr(gettext(WRN_ODDVERIFY),
+					    cl_nam(
+					    el_ent->cf_ent.pkg_class_idx));
+					/*
+					 * Set destination verification to
+					 * default.
+					 */
+					cl_def_dverify(
+					    el_ent->cf_ent.pkg_class_idx);
+				}
+			}
+
+			/*
+			 * Comply with administrative requirements regarding
+			 * setuid/setgid processes.
+			 */
+			if (is_setuid(&(el_ent->cf_ent))) {
+				el_ent->mstat.setuid = 1;
+			}
+			if (is_setgid(&(el_ent->cf_ent))) {
+				el_ent->mstat.setgid = 1;
+			}
+
+			/*
+			 * If setuid/setgid processes are not allowed, reset
+			 * those bits.
+			 */
+			if (nosetuid && (el_ent->mstat.setgid ||
+			    el_ent->mstat.setuid)) {
+				el_ent->cf_ent.ainfo.mode &=
+				    ~(S_ISUID | S_ISGID);
+			}
+		} else {
+			eoextlist = 1;	/* end of extlist[] */
+		}
+
+		/*
+		 * If we're not at the end of the package database, get the
+		 * next entry for comparison.
+		 */
+		if (!eocontents) {
+
+			/* Search package database for this entry. */
+			n = srchcfile(&cf_ent, el_ent ?
+				el_ent->cf_ent.path : NULL,
+				mapvfp, tmpvfp);
+
+			/*
+			 * If there was an error, note it and return an error
+			 * flag.
+			 */
+			if (n < 0) {
+				char	*errstr = getErrstr();
+				logerr(gettext(
+				    "bad entry read from contents file"));
+				logerr(gettext("- pathname: %s"),
+				    (cf_ent.path && *cf_ent.path) ?
+				    cf_ent.path : "Unknown");
+				logerr(gettext("- problem: %s"),
+				    (errstr && *errstr) ? errstr : "Unknown");
+				return (-1);
+			/*
+			 * If there was a match, then merge them into a
+			 * single entry.
+			 */
+			} else if (n == 1) {
+				/*
+				 * If this package is overwriting a setuid or
+				 * setgid process, set the status bits so we
+				 * can inform the administrator.
+				 */
+				if (is_setuid(&cf_ent)) {
+					el_ent->mstat.osetuid = 1;
+				}
+
+				if (is_setgid(&cf_ent)) {
+					el_ent->mstat.osetgid = 1;
+				}
+				/*
+				 * Detect if a symlink has changed to directory
+				 * If so mark all the files/dir supposed to be
+				 * iniside this dir, so that they are not miss
+				 * understood by do_new_ent later as already
+				 * installed.
+				 */
+				if ((!eoextlist) && (cf_ent.ftype == 's') &&
+				    (el_ent->cf_ent.ftype == 'd')) {
+					int i;
+					int plen = strlen(el_ent->cf_ent.path);
+					for (i = eptnum + 1; extlist[i]; i++) {
+						if (strncmp(el_ent->cf_ent.path,
+						    extlist[i]->cf_ent.path,
+						    plen) != 0)
+							break;
+						extlist[i]->mstat.parentsyml2dir
+						    = 1;
+					}
+				}
+
+				if (do_like_ent(tmpvfp, el_ent, &cf_ent,
+				    assume_ok)) {
+					changed++;
+				}
+
+			/*
+			 * If the alphabetical position in the package
+			 * database is unfilled, then this will be a new
+			 * entry. If n == 0, then we're also at the end of
+			 * the contents file.
+			 */
+			} else {
+				if (n == 0) {
+					eocontents++;
+				}
+
+				/*
+				 * If there is an extlist entry in the
+				 * hopper, insert it at the end of the
+				 * package database.
+				 */
+				if (!eoextlist) {
+					if (do_new_ent(tmpvfp, el_ent,
+					    assume_ok)) {
+						changed++;
+					}
+				}
+			}
+		/*
+		 * We have passed the last entry in the package database,
+		 * tagging these extlist entries onto the end.
+		 */
+		} else if (!eoextlist) {
+			if (do_new_ent(tmpvfp, el_ent, assume_ok)) {
+				changed++;
+			}
+		}
+		/* Else, we'll drop out of the loop. */
+
+		(void) sigrelse(SIGALRM);
+	} while (eptnum++, (!eocontents || !eoextlist));
+
+	if (notify) {
+		(void) alarm(0);
+		(void) signal(SIGALRM, SIG_IGN);
+	}
+
+	return (errflg ? -1 : changed);
+}
+
+/*
+ * Merge a new entry with an installed package object of the same name and
+ * insert that object into the package database. Obey administrative defaults
+ * as regards conflicting files.
+ */
+
+static int
+do_like_ent(VFP_T *vfpo, struct cfextra *el_ent, struct cfent *cf_ent, int ctrl)
+{
+	int	stflag, ignore, changed, mrg_result;
+
+	ignore = changed = 0;
+
+	/*
+	 * Construct the record defining the current package. If there are
+	 * other packages involved, this will be appended to the existing
+	 * list. If this is an update of the same package, it will get merged
+	 * with the existing record. If this is a preloaded record (like from
+	 * a dryrun file), it will keep it's current pinfo pointer and will
+	 * pass it on to the record from the contents file - because on the
+	 * final continuation, the contents file will be wrong.
+	 */
+	if (el_ent->mstat.preloaded) {
+		struct pinfo *pkginfo;
+
+		/* Contents file is not to be trusted for this list. */
+		pkginfo = cf_ent->pinfo;
+
+		/* Free the potentially bogus list. */
+		while (pkginfo) {
+			struct pinfo *next;
+			next = pkginfo->next;
+			free(pkginfo);
+			pkginfo = next;
+		}
+
+		cf_ent->pinfo = el_ent->cf_ent.pinfo;
+	}
+
+	pkgpinfo = eptstat(cf_ent, pkginst, DUP_ENTRY);
+
+	stflag = pkgpinfo->status;
+
+	if (otherstoo)
+		el_ent->mstat.shared = 1;
+
+	/* If it's marked for erasure, make it official */
+	if (el_ent->cf_ent.ftype == RM_RDY) {
+		if (!errflg) {
+			pkgpinfo = eptstat(cf_ent, pkginst, RM_RDY);
+
+			/*
+			 * Get copy of status character in case the object is
+			 * "shared" by a server, in which case we need to
+			 * maintain the shared status after the entry is
+			 * written to the package database with RM_RDY
+			 * status. This is needed to support the `removef'
+			 * command.
+			 */
+			stflag = pkgpinfo->status;
+			pkgpinfo->status = RM_RDY;
+
+			if (putcvfpfile(cf_ent, vfpo)) {
+				progerr(gettext(ERR_OUTPUT));
+				quit(99);
+			}
+
+			/*
+			 * If object is provided by a server, allocate an
+			 * info block and set the status to indicate this.
+			 * This is needed to support the `removef' command.
+			 */
+			if (stflag == SERVED_FILE) {
+				el_ent->cf_ent.pinfo =
+				    (struct pinfo *)calloc(1,
+				    sizeof (struct pinfo));
+				el_ent->cf_ent.pinfo->next = NULL;
+				el_ent->cf_ent.pinfo->status = SERVED_FILE;
+			}
+		}
+		return (1);
+	}
+
+	/*
+	 * If there is no package associated with it, there's something
+	 * very wrong.
+	 */
+	if (!pkgpinfo) {
+		progerr(gettext(ERR_PINFO), cf_ent->path);
+		quit(99);
+	}
+
+	/*
+	 * Do not allow installation if nocnflct is set and other packages
+	 * reference this pathname. The cp_cfent() function below writes the
+	 * information from the installed file over the new entry, so the
+	 * package database will be unchanged.
+	 *
+	 * By the way, ftype "e" is often shared and that's OK, so ftype
+	 * "e" doesn't count here.
+	 */
+	if ((nocnflct && el_ent->mstat.shared && el_ent->cf_ent.ftype != 'e')) {
+		/*
+		 * First set the attrchg and contchg entries for proper
+		 * messaging in the install phase.
+		 */
+		set_change(el_ent);
+
+		/*
+		 * Now overwrite the new entry with the entry for the
+		 * currently installed object.
+		 */
+		if (cp_cfent(cf_ent, el_ent) == 0)
+			quit(99);
+
+		ignore++;
+	} else {
+		mrg_result = merg(el_ent, cf_ent);
+
+		switch (mrg_result) {
+		    case MRG_SAME:
+			break;
+
+		    case MRG_DIFFERENT:
+			changed++;
+			break;
+
+		    case MRG_REPLACE:
+			/*
+			 * We'll pick one or the other later. For now, cf_ent
+			 * will have the fault value and el_ent will retain
+			 * the other value. This is the only state that allows
+			 * the database and the pkgmap to differ.
+			 */
+
+			el_ent->mstat.contchg = 1;	/* subject to change */
+			ignore++;
+			break;
+
+		    default:
+			break;
+		}
+	}
+
+	/* el_ent structure now contains updated entry */
+	if (!el_ent->mstat.contchg && !ignore) {
+		/*
+		 * We know the DB entry matches the pkgmap, so now we need to
+		 * see if the actual object matches the pkgmap.
+		 */
+		set_change(el_ent);
+	}
+
+	if (!errflg) {
+		if (ctrl == 1) {	/* quick verify assumes OK */
+			/*
+			 * The pkgpinfo entry is already correctly
+			 * constructed. Look into dropping this soon.
+			 */
+			pkgpinfo = eptstat(&(el_ent->cf_ent), pkginst,
+			    ENTRY_OK);
+
+			if (stflag != DUP_ENTRY) {
+				changed++;
+			}
+
+			/*
+			 * We could trust the prior pkginfo entry, but things
+			 * could have changed and  we need to update the
+			 * fs_tab[] anyway. We check for a server object
+			 * here.
+			 */
+			if (is_served(el_ent->server_path,
+			    &(el_ent->fsys_value)))
+				pkgpinfo->status = SERVED_FILE;
+		} else {
+			if (!ignore && el_ent->mstat.contchg) {
+				pkgpinfo =
+				    eptstat(&(el_ent->cf_ent), pkginst,
+				    (dbst ? dbst : CONFIRM_CONT));
+			} else if (!ignore && el_ent->mstat.attrchg) {
+				pkgpinfo =
+				    eptstat(&(el_ent->cf_ent), pkginst,
+				    (dbst ? dbst : CONFIRM_ATTR));
+			} else if (!ignore && el_ent->mstat.shared) {
+				pkgpinfo =
+				    eptstat(&(el_ent->cf_ent), pkginst,
+				    dbst);
+				changed++;
+			} else if (stflag != DUP_ENTRY) {
+				pkgpinfo = eptstat(&(el_ent->cf_ent),
+				    pkginst, '\0');
+				if (stflag != ENTRY_OK) {
+					changed++;
+				}
+			}
+		}
+
+		if (mrg_result == MRG_REPLACE) {
+			/*
+			 * Put the original package database entry back into
+			 * the package database for now.
+			 */
+			output(vfpo, cf_ent, pkgpinfo);
+		} else {
+			/* Put the merged entry into the package database. */
+			output(vfpo, &(el_ent->cf_ent), pkgpinfo);
+		}
+	}
+
+	if (pkgpinfo->aclass[0] != '\0') {
+		(void) strcpy(el_ent->cf_ent.pkg_class, pkgpinfo->aclass);
+	}
+
+	/*
+	 * If a sym link entry exists in the contents file and
+	 * and the destination of the link does not exist on the the system
+	 * then the contents file needs to be updated appropriately so a
+	 * subsequent invocation of "installf -f" will create the destination.
+	 */
+	if (el_ent->mstat.contchg && pkgpinfo->status == INST_RDY) {
+		changed++;
+	}
+
+	if (!(el_ent->mstat.preloaded))
+		el_ent->cf_ent.pinfo = NULL;
+
+	/*
+	 * If no change during the merg and we don't have a case where types
+	 * were different in odd ways, count this as installed.
+	 */
+	if (!el_ent->mstat.attrchg && !el_ent->mstat.contchg &&
+	    !el_ent->mstat.replace)
+		installed++;
+	return (changed);
+}
+
+/* Insert an entirely new entry into the package database. */
+static int
+do_new_ent(VFP_T *vfpo, struct cfextra *el_ent, int ctrl)
+{
+	struct pinfo	*pinfo;
+	char		*tp;
+	int		changed = 0;
+
+	if (el_ent->cf_ent.ftype == RM_RDY) {
+		return (0);
+	}
+
+	tp = el_ent->server_path;
+	/*
+	 * Check the file/dir existence only if any of the parent directory
+	 * of the file/dir has not changed from symbolic link to directory.
+	 * At this time we are only doing a dry run, the symlink is not yet
+	 * replaced, so if this is done directly then access will result in
+	 * incorrect information in case a file with the same attr and cont
+	 * exists in the link target.
+	 */
+	if ((!el_ent->mstat.parentsyml2dir) && (access(tp, F_OK) == 0)) {
+		/*
+		 * Path exists, and although its not referenced by any
+		 * package we make it look like it is so it appears as a
+		 * conflicting file in case the user doesn't want it
+		 * installed. We set the rogue flag to distinguish this from
+		 * package object conflicts if the administrator is queried
+		 * about this later. Note that noconflict means NO conflict
+		 * at the file level. Even rogue files count.
+		 */
+		el_ent->mstat.shared = 1;
+		el_ent->mstat.rogue = 1;
+		set_change(el_ent);
+	} else {
+		/* since path doesn't exist, we're changing everything */
+		el_ent->mstat.rogue = 0;
+		el_ent->mstat.contchg = 1;
+		el_ent->mstat.attrchg = 1;
+	}
+
+	if (el_ent->cf_ent.ainfo.mode == WILDCARD) {
+		if (el_ent->cf_ent.ftype == 'd') {
+			el_ent->cf_ent.ainfo.mode = DEFAULT_MODE;
+		} else {
+			el_ent->cf_ent.ainfo.mode = DEFAULT_MODE_FILE;
+		}
+		logerr(WRN_SET_DEF_MODE, el_ent->cf_ent.path,
+		    (int)el_ent->cf_ent.ainfo.mode);
+	}
+
+	if (strcmp(el_ent->cf_ent.ainfo.owner, DB_UNDEFINED_ENTRY) == 0)
+		(void) strcpy(el_ent->cf_ent.ainfo.owner,
+				DEFAULT_OWNER);
+	if (strcmp(el_ent->cf_ent.ainfo.group, DB_UNDEFINED_ENTRY) == 0)
+		(void) strcpy(el_ent->cf_ent.ainfo.group,
+				DEFAULT_GROUP);
+
+	/*
+	 * Do not allow installation if nocnflct is set and this pathname is
+	 * already in place. Since this entry is new (not associated with a
+	 * package), we don't issue anything to the database we're building.
+	 */
+	if (nocnflct && el_ent->mstat.shared) {
+		return (0);
+	}
+
+	if (!errflg) {
+		if (el_ent->mstat.preloaded) {
+			/* Add this package to the already established list. */
+			pinfo = eptstat(&(el_ent->cf_ent), pkginst, DUP_ENTRY);
+		} else {
+			el_ent->cf_ent.npkgs = 1;
+			pinfo = (struct pinfo *)calloc(1,
+			    sizeof (struct pinfo));
+			if (!pinfo) {
+				progerr(gettext(ERR_MEMORY), errno);
+				quit(99);
+			}
+			el_ent->cf_ent.pinfo = pinfo;
+			(void) strcpy(pinfo->pkg, pkginst);
+		}
+
+		if (ctrl == 1) {	/* quick verify assumes OK */
+			pinfo->status = dbst ? dbst : ENTRY_OK;
+			/*
+			 * The entry won't be verified, but the entry in the
+			 * database isn't necessarily ENTRY_OK. If this is
+			 * coming from a server, we need to note that
+			 * instead.
+			 */
+			if (is_served(el_ent->server_path,
+			    &(el_ent->fsys_value)))
+				pinfo->status = SERVED_FILE;
+		} else {
+			pinfo->status = dbst ? dbst : CONFIRM_CONT;
+		}
+
+		output(vfpo, &(el_ent->cf_ent), pinfo);
+		changed++;
+
+		free(pinfo);
+		el_ent->cf_ent.pinfo = NULL;
+		}
+	if (!el_ent->mstat.attrchg && !el_ent->mstat.contchg) {
+		installed++;
+	}
+
+	return (changed);
+}
+
+int
+files_installed(void)
+{
+	return (installed);
+}
+
+/*
+ * This function determines if there is a difference between the file on
+ * the disk and the file to be laid down. It set's mstat flags attrchg
+ * and contchg accordingly.
+ */
+static void
+set_change(struct cfextra *el_ent)
+{
+	int	n;
+	char 	*tp;
+
+	tp = el_ent->server_path;
+	if ((el_ent->cf_ent.ftype == 'f') || (el_ent->cf_ent.ftype == 'e') ||
+		(el_ent->cf_ent.ftype == 'v')) {
+		if (cverify(0, &(el_ent->cf_ent.ftype), tp,
+		    &(el_ent->cf_ent.cinfo), 1)) {
+			el_ent->mstat.contchg = 1;
+		} else if (!el_ent->mstat.contchg && !el_ent->mstat.attrchg) {
+			if (averify(0, &(el_ent->cf_ent.ftype), tp,
+			    &(el_ent->cf_ent.ainfo)))
+				el_ent->mstat.attrchg = 1;
+		}
+	} else if (!el_ent->mstat.attrchg &&
+		((el_ent->cf_ent.ftype == 'd') ||
+		(el_ent->cf_ent.ftype == 'x') ||
+		(el_ent->cf_ent.ftype == 'c') ||
+		(el_ent->cf_ent.ftype == 'b') ||
+		(el_ent->cf_ent.ftype == 'p'))) {
+		n = averify(0, &(el_ent->cf_ent.ftype), tp,
+		    &(el_ent->cf_ent.ainfo));
+		if (n == VE_ATTR)
+			el_ent->mstat.attrchg = 1;
+		else if (n && (n != VE_EXIST)) {
+			el_ent->mstat.contchg = 1;
+		}
+	} else if (!el_ent->mstat.attrchg &&
+		((el_ent->cf_ent.ftype == 's') ||
+		(el_ent->cf_ent.ftype == 'l'))) {
+		n = averify(0, &(el_ent->cf_ent.ftype), tp,
+		    &(el_ent->cf_ent.ainfo));
+		if (n == VE_ATTR)
+			el_ent->mstat.attrchg = 1;
+		else if (n && (n == VE_EXIST)) {
+			el_ent->mstat.contchg = 1;
+		}
+	}
+}
+
+static int
+is_setuid(struct cfent *ent)
+{
+	return (((ent->ftype == 'f') || (ent->ftype == 'v') ||
+		(ent->ftype == 'e')) &&
+		(ent->ainfo.mode != BADMODE) &&
+		(ent->ainfo.mode != WILDCARD) &&
+		(ent->ainfo.mode & S_ISUID));
+}
+
+static int
+is_setgid(struct cfent *ent)
+{
+	return (((ent->ftype == 'f') || (ent->ftype == 'v') ||
+		(ent->ftype == 'e')) && (ent->ainfo.mode != BADMODE) &&
+		(ent->ainfo.mode != WILDCARD) &&
+		(ent->ainfo.mode & S_ISGID) &&
+		(ent->ainfo.mode & (S_IEXEC|S_IXUSR|S_IXOTH)));
+}
+
+char *types[] = {
+	"fev",	/* type 1, regular files */
+	"s", 	/* type 2, symbolic links */
+	"l", 	/* type 3, linked files */
+	"dx", 	/* type 4, directories */
+	"c", 	/* type 5, character special devices */
+	"b", 	/* type 6, block special devices */
+	"p", 	/* type 7, named pipes */
+	NULL
+};
+
+/*
+ * This determines if the ftype of the file on the disk and the file to be
+ * laid down are close enough. If they aren't, this either returns an error
+ * or displays a warning. This returns :
+ *	TYPE_OK		they're identical or close enough
+ *	TYPE_WARNING	they're pretty close (probably no problem)
+ *	TYPE_IGNORED	the type change was not allowed
+ *	TYPE_REPLACE	to be reviewed later - in endofclass() maybe
+ *	TYPE_FATAL	something awful happened
+ */
+static int
+typechg(struct cfent *el_ent, struct cfent *cf_ent, struct mergstat *mstat)
+{
+	int	i, etype, itype, retcode;
+
+	/* If they are identical, return OK */
+	if (cf_ent->ftype == el_ent->ftype)
+		return (TYPE_OK);
+
+	/*
+	 * If package database entry is ambiguous, set it to the new entity's
+	 * ftype
+	 */
+	if (cf_ent->ftype == BADFTYPE) {
+		cf_ent->ftype = el_ent->ftype;
+		return (TYPE_OK); /* do nothing; not really different */
+	}
+
+	/* If the new entity is ambiguous, wait for the verify */
+	if (el_ent->ftype == BADFTYPE)
+		return (TYPE_OK);
+
+	/*
+	 * If we're trying to convert an existing regular directory to an
+	 * exclusive directory, this is very dangerous. We will continue, but
+	 * we will deny the conversion.
+	 */
+	if (el_ent->ftype == 'x' && cf_ent->ftype == 'd') {
+		logerr(gettext(WRN_TOEXCL), el_ent->path);
+		return (TYPE_IGNORED);
+	}
+
+	etype = itype = 0;
+
+	/* Set etype to that of the new entity */
+	for (i = 0; types[i]; ++i) {
+		if (strchr(types[i], el_ent->ftype)) {
+			etype = i+1;
+			break;
+		}
+	}
+
+	/* Set itype to that in the package database. */
+	for (i = 0; types[i]; ++i) {
+		if (strchr(types[i], cf_ent->ftype)) {
+			itype = i+1;
+			break;
+		}
+	}
+
+	if (itype == etype) {
+		/* same basic object type */
+		return (TYPE_OK);
+	}
+
+	retcode = TYPE_WARNING;
+
+	/*
+	 * If a simple object (like a file) is overwriting a directory, mark
+	 * it for full inspection during installation.
+	 */
+	if (etype != 4 && itype == 4) {
+		mstat->dir2nondir = 1;
+		retcode = TYPE_REPLACE;
+	}
+
+	/* allow change, but warn user of possible problems */
+	switch (itype) {
+	    case 1:
+		logerr(gettext(WRN_NOTFILE), el_ent->path);
+		break;
+
+	    case 2:
+		logerr(gettext(WRN_NOTSYMLN), el_ent->path);
+		break;
+
+	    case 3:
+		logerr(gettext(WRN_NOTLINK), el_ent->path);
+		break;
+
+	    case 4:
+		logerr(gettext(WRN_NOTDIR), el_ent->path);
+		break;
+
+	    case 5:
+		logerr(gettext(WRN_NOTCHAR), el_ent->path);
+		break;
+
+	    case 6:
+		logerr(gettext(WRN_NOTBLOCK), el_ent->path);
+		break;
+
+	    case 7:
+		logerr(gettext(WRN_NOTPIPE), el_ent->path);
+		break;
+
+	    default:
+		break;
+	}
+	return (retcode);
+}
+
+/*
+ * This function takes el_ent (the entry from the pkgmap) and cf_ent (the
+ * entry from the package database) and merge them into el_ent. The rules
+ * are still being figured out, but the comments should make the approach
+ * pretty clear.
+ *
+ * RETURN CODES:
+ *	MRG_DIFFERENT	The two entries are different and el_ent now contains
+ *			the intended new entry to be installed.
+ *	MRG_SAME	The two entries were identical and the old database
+ *			entry will be replaced unchanged.
+ *	MRG_REPLACE	One or the other entry will be used but the decision
+ *			has to be made at install time.
+ */
+static int
+merg(struct cfextra *el_ent, struct cfent *cf_ent)
+{
+	int	n, changed = 0;
+
+	/*
+	 * We need to change the original entry to make it look like the new
+	 * entry (the eptstat() routine has already added appropriate package
+	 * information, but not about 'aclass' which may represent a change
+	 * in class from the previous installation.
+	 *
+	 * NOTE: elent->cf_ent.pinfo (the list of associated packages) is NULL
+	 * upon entry to this function.
+	 */
+
+	el_ent->cf_ent.pinfo = cf_ent->pinfo;
+
+	if (dbst == INST_RDY && el_ent->cf_ent.ftype == '?') {
+		el_ent->cf_ent.ftype = cf_ent->ftype;
+	}
+
+	/*
+	 * Evaluate the ftype change. Usually the ftype won't change. If it
+	 * does it may be easy (s -> f), not allowed (d -> x), so complex we
+	 * can't figure it 'til later (d -> s) or fatal (a hook for later).
+	 */
+	if (cf_ent->ftype != el_ent->cf_ent.ftype) {
+		n = typechg(&(el_ent->cf_ent), cf_ent, &(el_ent->mstat));
+
+		switch (n) {
+		    case TYPE_OK:
+			break;
+
+		    /* This is an allowable change. */
+		    case TYPE_WARNING:
+			el_ent->mstat.contchg = 1;
+			break;
+
+		    /* Not allowed, but leaving it as is is OK. */
+		    case TYPE_IGNORED:
+			logerr(gettext(MSG_TYPIGN));
+			if (cp_cfent(cf_ent, el_ent) == 0)
+				quit(99);
+			return (MRG_SAME);
+
+		    /* Future analysis will reveal if this is OK. */
+		    case TYPE_REPLACE:
+			el_ent->mstat.replace = 1;
+			return (MRG_REPLACE);
+
+		    /* Kill it before it does any damage. */
+		    case TYPE_FATAL:
+			logerr(gettext(MSG_TYPE_ERR));
+			quit(99);
+
+		    default:
+			break;
+		}
+
+		changed++;
+	}
+
+	/* Evaluate and merge the class. */
+	if (strcmp(cf_ent->pkg_class, el_ent->cf_ent.pkg_class)) {
+		/*
+		 * we always allow a class change as long as we have
+		 * consistent ftypes, which at this point we must
+		 */
+		changed++;
+		if (strcmp(cf_ent->pkg_class, "?")) {
+			(void) strcpy(pkgpinfo->aclass,
+			    el_ent->cf_ent.pkg_class);
+			(void) strcpy(el_ent->cf_ent.pkg_class,
+			    cf_ent->pkg_class);
+			chgclass(&(el_ent->cf_ent), pkgpinfo);
+		}
+	}
+
+	/*
+	 * Evaluate and merge based upon the ftype of the intended package
+	 * database entry.
+	 */
+	if (((el_ent->cf_ent.ftype == 's') || (el_ent->cf_ent.ftype == 'l'))) {
+
+		/* If both have link sources, then they need to be merged. */
+		if (cf_ent->ainfo.local && el_ent->cf_ent.ainfo.local) {
+			/*
+			 * If both sources are identical, the merge is
+			 * already done.
+			 */
+			if (strcmp(cf_ent->ainfo.local,
+			    el_ent->cf_ent.ainfo.local) != NULL) {
+				changed++;
+
+				/*
+				 * Otherwise, if the pkgmap entry is
+				 * ambiguous, it will inherit the database
+				 * entry.
+				 */
+				if (strcmp(el_ent->cf_ent.ainfo.local,
+				    "?") == NULL) {
+					(void) strlcpy(
+						el_ent->cf_ent.ainfo.local,
+						cf_ent->ainfo.local,
+						PATH_MAX);
+				} else {
+					el_ent->mstat.contchg = 1;
+				}
+			}
+		}
+		return (changed ? MRG_DIFFERENT : MRG_SAME);
+
+	} else if (el_ent->cf_ent.ftype == 'e') {
+
+		/*
+		 * The contents of edittable files are assumed to be changing
+		 * since some class action script will be doing the work and
+		 * we have no way of evaluating what it will actually do.
+		 */
+		el_ent->mstat.contchg = 1;
+		changed++;
+	} else if (((el_ent->cf_ent.ftype == 'f') ||
+					(el_ent->cf_ent.ftype == 'v'))) {
+		/*
+		 * For regular files, Look at content information; a BADCONT
+		 * in any el_ent field indicates the contents are unknown --
+		 * since cf_ent is guaranteed to have a valid entry here (bad
+		 * assumption?) this function will recognize this as a
+		 * change. The ambiguous el_ent values will be evaluated and
+		 * set later.
+		 */
+
+		/*
+		 * for type f/v files, if the file is in an area that is
+		 * inherited from the global zone, that area is read only
+		 * and the object cannot be changed - ignore any settings
+		 * in the current package database that may be present for
+		 * any existing object because they are irrelevant - since
+		 * the object is in a read-only area shared from the global
+		 * zone, accept that file's actual attributes as being correct.
+		 */
+
+		if (z_path_is_inherited(el_ent->cf_ent.path,
+			el_ent->cf_ent.ftype, get_inst_root()) == B_TRUE) {
+			echoDebug(DBG_PKGDBMRG_INHERITED, el_ent->cf_ent.path);
+		} else if (cf_ent->cinfo.size != el_ent->cf_ent.cinfo.size) {
+			changed++;
+			el_ent->mstat.contchg = 1;
+		} else if (cf_ent->cinfo.modtime !=
+		    el_ent->cf_ent.cinfo.modtime) {
+			changed++;
+			el_ent->mstat.contchg = 1;
+		} else if (cf_ent->cinfo.cksum != el_ent->cf_ent.cinfo.cksum) {
+			changed++;
+			el_ent->mstat.contchg = 1;
+		}
+	} else if (((el_ent->cf_ent.ftype == 'c') ||
+					(el_ent->cf_ent.ftype == 'b'))) {
+		/*
+		 * For devices, if major or minor numbers are identical the
+		 * merge is trivial. If the el_ent value is ambiguous (BAD),
+		 * the cf_ent value is inherited. Otherwise, the el_ent value
+		 * is preserved.
+		 */
+		if (cf_ent->ainfo.major != el_ent->cf_ent.ainfo.major) {
+			changed++;
+			if (el_ent->cf_ent.ainfo.major == BADMAJOR) {
+				el_ent->cf_ent.ainfo.major =
+				    cf_ent->ainfo.major;
+			} else {
+				el_ent->mstat.contchg = 1;
+			}
+		}
+		if (cf_ent->ainfo.minor != el_ent->cf_ent.ainfo.minor) {
+			changed++;
+			if (el_ent->cf_ent.ainfo.minor == BADMINOR)
+				el_ent->cf_ent.ainfo.minor =
+				    cf_ent->ainfo.minor;
+			else
+				el_ent->mstat.contchg = 1;
+		}
+	}
+
+	/*
+	 * For mode, owner and group follow the same rules as above - if
+	 * ambiguous, inherit, otherwise keep the new one.
+	 */
+	if (cf_ent->ainfo.mode != el_ent->cf_ent.ainfo.mode) {
+		changed++;  /* attribute info is changing */
+		if (el_ent->cf_ent.ainfo.mode == BADMODE) {
+			el_ent->cf_ent.ainfo.mode = cf_ent->ainfo.mode;
+		} else if (el_ent->cf_ent.ainfo.mode == WILDCARD) {
+			/*
+			 * If pkgmap has a '?' set for mode, use the mode from
+			 * the pkg DB (contents file).
+			 */
+			el_ent->cf_ent.ainfo.mode = cf_ent->ainfo.mode;
+			el_ent->mstat.attrchg = 0;
+		} else {
+			el_ent->mstat.attrchg = 1;
+		}
+	}
+	if (strcmp(cf_ent->ainfo.owner, el_ent->cf_ent.ainfo.owner) != 0) {
+		changed++;  /* attribute info is changing */
+		if (strcmp(el_ent->cf_ent.ainfo.owner, BADOWNER) == 0)
+			(void) strcpy(el_ent->cf_ent.ainfo.owner,
+			    cf_ent->ainfo.owner);
+		else
+			el_ent->mstat.attrchg = 1;
+	}
+	if (strcmp(cf_ent->ainfo.group, el_ent->cf_ent.ainfo.group) != 0) {
+		changed++;  /* attribute info is changing */
+		if (strcmp(el_ent->cf_ent.ainfo.group, BADGROUP) == 0)
+			(void) strcpy(el_ent->cf_ent.ainfo.group,
+			    cf_ent->ainfo.group);
+		else
+			el_ent->mstat.attrchg = 1;
+	}
+	return (changed ? MRG_DIFFERENT : MRG_SAME);
+}
+
+/*
+ * This puts the current entry into the package database in the appropriate
+ * intermediate format for this stage of the installation. This also assures
+ * the correct format for the various package object ftypes, stripping the
+ * link name before storing a regular file and stuff like that.
+ */
+
+static void
+output(VFP_T *vfpo, struct cfent *ent, struct pinfo *pinfo)
+{
+	short	svvolno;
+	char	*svpt;
+
+	/* output without volume information */
+	svvolno = ent->volno;
+	ent->volno = 0;
+
+	pinfo->editflag = 0;
+	if (((ent->ftype == 's') || (ent->ftype == 'l'))) {
+		if (putcvfpfile(ent, vfpo)) {
+			progerr(gettext(ERR_OUTPUT));
+			quit(99);
+		}
+	} else {
+
+		/* output without local pathname */
+		svpt = ent->ainfo.local;
+		ent->ainfo.local = NULL;
+		if (putcvfpfile(ent, vfpo)) {
+			progerr(gettext(ERR_OUTPUT));
+			quit(99);
+		}
+
+		ent->ainfo.local = svpt;
+		/*
+		 * If this entry represents a file which is being edited, we
+		 * need to store in memory the fact that it is an edittable
+		 * file so that when we audit it after installation we do not
+		 * worry about its contents; we do this by resetting the ftype
+		 * to 'e' in the memory array which is later used to control
+		 * the audit
+		 */
+		if (pinfo->editflag)
+			ent->ftype = 'e';
+	}
+	/* restore volume information */
+	ent->volno = svvolno;
+}
+
+static void
+chgclass(struct cfent *cf_ent, struct pinfo *pinfo)
+{
+	struct pinfo *pp;
+	char	*oldclass, newclass[CLSSIZ+1];
+	int	newcnt, oldcnt;
+
+	/*
+	 * we use this routine to minimize the use of the aclass element by
+	 * optimizing the use of the cf_ent->pkg_class element
+	 */
+
+	(void) strlcpy(newclass, pinfo->aclass, sizeof (newclass));
+	newcnt = 1;
+
+	oldclass = cf_ent->pkg_class;
+	oldcnt = 0;
+
+	/*
+	 * count the number of times the newclass will be used and see if it
+	 * exceeds the number of times the oldclass is referenced
+	 */
+	pp = cf_ent->pinfo;
+	while (pp) {
+		if (pp->aclass[0] != '\0') {
+			if (strcmp(pp->aclass, newclass) == 0)
+				newcnt++;
+			else if (strcmp(pp->aclass, oldclass) == 0)
+				oldcnt++;
+		}
+		pp = pp->next;
+	}
+	if (newcnt > oldcnt) {
+		pp = cf_ent->pinfo;
+		while (pp) {
+			if (pp->aclass[0] == '\0') {
+				(void) strcpy(pp->aclass, oldclass);
+			} else if (strcmp(pp->aclass, newclass) == 0) {
+				pp->aclass[0] = '\0';
+			}
+			pp = pp->next;
+		}
+		(void) strcpy(cf_ent->pkg_class, newclass);
+	}
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/usr/src/cmd/svr4pkg/libinst/pkgobjmap.c	Fri Jun 05 10:28:40 2009 -0400
@@ -0,0 +1,742 @@
+/*
+ * 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
+ * or http://www.opensolaris.org/os/licensing.
+ * 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.
+ */
+
+
+#include <stdio.h>
+#include <limits.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <errno.h>
+#include <string.h>
+#include <sys/types.h>
+#include <pkgstrct.h>
+#include <locale.h>
+#include <libintl.h>
+#include <pkglib.h>
+#include <install.h>
+#include <libinst.h>
+
+#define	WRN_NOPKGOBJ	"WARNING: no package objects found"
+
+#define	ERR_MEMORY	"memory allocation failure"
+#define	ERR_DUPPATH	"duplicate pathname <%s>"
+
+/* libpkg/gpkgmap */
+extern int	getmapmode(void);
+
+#define	EPTMALLOC	512
+
+static struct cfextra **extlist;
+
+int	eptnum;
+static int	array_preloaded = 0;
+static int	errflg;
+static int	nparts;
+static int	xspace = -1;
+
+void	pkgobjinit(void);
+static int	pkgobjassign(struct cfent *ept, char **server_local,
+		    char **client_local, char **server_path,
+		    char **client_path, char **map_path, int mapflag,
+		    int nc);
+
+static int	ckdup(struct cfent *ept1, struct cfent *ept2);
+static int	sortentry(int index);
+static int	dup_merg(struct cfextra *ext1, struct cfextra *ext2);
+
+void
+pkgobjinit(void)
+{
+	if (array_preloaded)	/* Already done. */
+		return;
+
+	errflg = nparts = eptnum = 0;
+
+	if (xspace != -1) {
+		ar_free(xspace);
+		xspace = -1;
+	}
+
+	/*
+	 * initialize dynamic memory used to store
+	 * path information which is read in
+	 */
+	(void) pathdup((char *)0);
+}
+
+/*
+ * This function assigns appropriate values based upon the pkgmap entry
+ * in the cfent structure.
+ */
+static int
+pkgobjassign(struct cfent *ept, char **server_local, char **client_local,
+    char **server_path, char **client_path, char **map_path, int mapflag,
+    int nc)
+{
+	int	path_duped = 0;
+	int	local_duped = 0;
+	char	source[PATH_MAX+1];
+
+	if (nc >= 0 && ept->ftype != 'i')
+		if ((ept->pkg_class_idx = cl_idx(ept->pkg_class)) == -1)
+			return (1);
+
+	if (ept->volno > nparts)
+		nparts++;
+
+	/*
+	 * Generate local (delivered source) paths for files
+	 * which need them so that the install routine will know
+	 * where to get the file from the package. Note that we
+	 * do not resolve path environment variables here since
+	 * they won't be resolved in the reloc directory.
+	 */
+	if ((mapflag > 1) && strchr("fve", ept->ftype)) {
+		if (ept->ainfo.local == NULL) {
+			source[0] = '~';
+			(void) strlcpy(&source[1], ept->path,
+						sizeof (source)-1);
+			ept->ainfo.local = pathdup(source);
+			*server_local = ept->ainfo.local;
+			*client_local = ept->ainfo.local;
+
+			local_duped = 1;
+		}
+	}
+
+	/*
+	 * Evaluate the destination path based upon available
+	 * environment, then produce a client-relative and
+	 * server-relative canonized path.
+	 */
+	if (mapflag && (ept->ftype != 'i')) {
+		mappath(getmapmode(), ept->path); /* evaluate variables */
+		canonize(ept->path);	/* Fix path as necessary. */
+
+		(void) eval_path(server_path,
+		    client_path,
+		    map_path,
+		    ept->path);
+		path_duped = 1;	/* eval_path dup's it */
+		ept->path = *server_path;	/* default */
+	}
+
+	/*
+	 * Deal with source for hard and soft links.
+	 */
+	if (strchr("sl", ept->ftype)) {
+		if (mapflag) {
+			mappath(getmapmode(), ept->ainfo.local);
+			if (!RELATIVE(ept->ainfo.local)) {
+				canonize(ept->ainfo.local);
+
+				/* check for hard link */
+				if (ept->ftype == 'l') {
+					(void) eval_path(
+					    server_local,
+					    client_local,
+					    NULL,
+					    ept->ainfo.local);
+					local_duped = 1;
+
+					/* Default to server. */
+					ept->ainfo.local = *server_local;
+				}
+			}
+		}
+	}
+
+	/*
+	 * For the paths (both source and target) that were too mundane to
+	 * have been copied into dup space yet, do that.
+	 */
+	if (!path_duped) {
+		*server_path = pathdup(ept->path);
+		*client_path = *server_path;
+		ept->path = *server_path;
+
+		path_duped = 1;
+	}
+	if (ept->ainfo.local != NULL)
+		if (!local_duped) {
+			*server_local = pathdup(ept->ainfo.local);
+			ept->ainfo.local = *server_local;
+			*client_local = ept->ainfo.local;
+
+		local_duped = 1;
+	}
+
+	return (0);
+}
+
+/* This initializes the package object array. */
+int
+init_pkgobjspace(void)
+{
+	if (array_preloaded)	/* Already done. */
+		return (1);
+
+	if (xspace == -1) {
+		xspace = ar_create(EPTMALLOC, sizeof (struct cfextra),
+		    "package object");
+		if (xspace == -1) {
+			progerr(gettext(ERR_MEMORY));
+			return (0);
+		}
+	}
+
+	return (1);
+}
+
+int
+seed_pkgobjmap(struct cfextra *ext_entry, char *path, char *local)
+{
+	struct cfextra *ext, **ext_ptr;
+
+	/* offsets for the various path images. */
+	int client_path_os;
+	int server_path_os;
+	int map_path_os;
+	int client_local_os;
+	int server_local_os;
+
+	ext_ptr = (struct cfextra **)ar_next_avail(xspace);
+
+	if (ext_ptr == NULL || *ext_ptr == NULL) {
+		progerr(gettext(ERR_MEMORY));
+		return (NULL);
+	}
+
+	ext = *ext_ptr;
+
+	(void) memcpy(ext, ext_entry, sizeof (struct cfextra));
+
+	/* Figure out all of the offsets. */
+	client_path_os = ((ptrdiff_t)ext->client_path -
+			(ptrdiff_t)ext->cf_ent.path);
+	server_path_os = ((ptrdiff_t)ext->server_path -
+			(ptrdiff_t)ext->cf_ent.path);
+	map_path_os = ((ptrdiff_t)ext->map_path -
+			(ptrdiff_t)ext->cf_ent.path);
+	client_local_os = ((ptrdiff_t)ext->client_local -
+			(ptrdiff_t)ext->cf_ent.ainfo.local);
+	server_local_os = ((ptrdiff_t)ext->server_local -
+			(ptrdiff_t)ext->cf_ent.ainfo.local);
+
+	/* Allocate and store the path name. */
+	ext->cf_ent.path = pathdup(path);
+
+	/* Assign the path substring pointers. */
+	ext->client_path = (ext->cf_ent.path + client_path_os);
+	ext->server_path = (ext->cf_ent.path + server_path_os);
+	ext->map_path = (ext->cf_ent.path + map_path_os);
+
+	/* If there's a local entry, allocate and store it as well. */
+	if (local) {
+		ext->cf_ent.ainfo.local = pathdup(local);
+
+		ext->client_local = (ext->cf_ent.ainfo.local + client_local_os);
+		ext->server_local = (ext->cf_ent.ainfo.local + server_local_os);
+	} else {
+		ext->cf_ent.ainfo.local = NULL;
+		ext->client_local = NULL;
+		ext->server_local = NULL;
+	}
+
+	eptnum++;
+	array_preloaded = 1;
+
+	return (0);
+}
+
+/*
+ * This function reads the pkgmap (or any file similarly formatted) and
+ * returns a pointer to a list of struct cfextra (each of which
+ * contains a struct cfent) representing the contents of that file.
+ */
+
+/* ARGSUSED ir in pkgobjmap */
+struct cfextra **
+pkgobjmap(VFP_T *vfp, int mapflag, char *ir)
+{
+	struct	cfextra *ext, **ext_ptr;
+	struct	cfent *ept, map_entry;
+	int	i;
+	int	n;
+	int	nc;
+
+	pkgobjinit();
+	if (!init_pkgobjspace())
+		quit(99);
+
+	nc = cl_getn();
+	for (;;) {
+		/* Clear the buffer. */
+		(void) memset(&map_entry, '\000', sizeof (struct cfent));
+
+		/*
+		 * Fill in a cfent structure in a very preliminary fashion.
+		 * ept->path and ept->ainfo.local point to static memory
+		 * areas of size PATH_MAX. These are manipulated and
+		 * then provided their own allocations later in this function.
+		 */
+		n = gpkgmapvfp(&map_entry, vfp);
+
+		if (n == 0)
+			break; /* no more entries in pkgmap */
+		else if (n < 0) {
+			char	*errstr = getErrstr();
+			progerr(gettext("bad entry read in pkgmap"));
+			logerr(gettext("pathname=%s"),
+			    (map_entry.path && *map_entry.path) ?
+			    map_entry.path : "Unknown");
+			logerr(gettext("problem=%s"),
+			    (errstr && *errstr) ? errstr : "Unknown");
+			return (NULL);
+		}
+
+		/*
+		 * A valid entry was found in the map, so allocate an
+		 * official record.
+		 */
+		ext_ptr = (struct cfextra **)ar_next_avail(xspace);
+		if (ext_ptr == NULL || *ext_ptr == NULL) {
+			progerr(gettext(ERR_MEMORY));
+			return (NULL);
+		}
+
+		ext = *ext_ptr;
+		ept = &(ext->cf_ent);
+
+		/* Transfer what we just read in. */
+		(void) memcpy(ept, &map_entry, sizeof (struct cfent));
+
+		/* And process it into the cfextra structure. */
+		if (pkgobjassign(ept,
+		    &(ext->server_local),
+		    &(ext->client_local),
+		    &(ext->server_path),
+		    &(ext->client_path),
+		    &(ext->map_path),
+		    mapflag, nc)) {
+			/* It didn't take. */
+			(void) ar_delete(xspace, eptnum);
+			continue;
+		}
+
+		eptnum++;
+		ext->fsys_value = BADFSYS;	/* No file system data yet */
+		ext->fsys_base = BADFSYS;
+	}
+
+	if (eptnum == 0) {
+		logerr(gettext(WRN_NOPKGOBJ));
+		return (NULL);
+	}
+
+	/* setup a pointer array to point to malloc'd entries space */
+	extlist = (struct cfextra **)ar_get_head(xspace);
+	if (extlist == NULL) {
+		progerr(gettext(ERR_MEMORY));
+		return (NULL);
+	}
+
+	(void) sortentry(-1);
+	for (i = 0; i < eptnum; /* void */) {
+		if (!sortentry(i))
+			i++;
+	}
+
+	return (errflg ? NULL : extlist);
+}
+
+/*
+ * This function sorts the final list of cfextra entries. If index = -1, the
+ * function is initialized. index = 0 doesn't get us anywhere because this
+ * sorts against index-1. Positive natural index values are compared and
+ * sorted into the array appropriately. Yes, it does seem we should use a
+ * quicksort on the whole array or something. The apparent reason for taking
+ * this approach is that there are enough special considerations to be
+ * applied to each package object that inserting them one-by-one doesn't cost
+ * that much.
+ */
+static int
+sortentry(int index)
+{
+	struct cfextra *ext;
+	struct cfent *ept, *ept_i;
+	static int last = 0;
+	int	i, n, j;
+	int	upper, lower;
+
+	if (index == 0)
+		return (0);
+	else if (index < 0) {
+		last = 0;
+		return (0);
+	}
+
+	/*
+	 * Based on the index, this is the package object we're going to
+	 * review. It may stay where it is or it may be repositioned in the
+	 * array.
+	 */
+	ext = extlist[index];
+	ept = &(ext->cf_ent);
+
+	/* quick comparison optimization for pre-sorted arrays */
+	if (strcmp(ept->path, extlist[index-1]->cf_ent.path) > 0) {
+		/* do nothing */
+		last = index-1;
+		return (0);
+	}
+
+	lower = 0;		/* lower bound of the unsorted elements */
+	upper = index;		/* upper bound */
+	i = last;
+	do {
+		/*
+		 * NOTE: This does a binary sort on path. There are lots of
+		 * other worthy items in the array, but path is the key into
+		 * the package database.
+		 */
+		ept_i = &(extlist[i]->cf_ent);
+
+		n = strcmp(ept->path, ept_i->path);
+		if (n == 0) {
+			if (!ckdup(ept, ept_i)) {
+				/*
+				 * If the array was seeded then there are
+				 * bound to be occasional duplicates.
+				 * Otherwise, duplicates are definitely a
+				 * sign of major damage.
+				 */
+				if (array_preloaded) {
+					if (!dup_merg(ext, extlist[i])) {
+						progerr(gettext(ERR_DUPPATH),
+						    ept->path);
+						errflg++;
+					}
+				} else {
+					progerr(gettext(ERR_DUPPATH),
+					    ept->path);
+					errflg++;
+				}
+			}
+			/* remove the entry at index */
+			(void) ar_delete(xspace, index);
+
+			eptnum--;
+			return (1);	/* Use this index again. */
+		} else if (n < 0) {
+			/*
+			 * The path of interest is smaller than the path
+			 * under test. Move down array using the method of
+			 * division
+			 */
+			upper = i;
+			i = lower + (upper-lower)/2;
+		} else {
+			/* Move up array */
+			lower = i+1;
+			i = upper - (upper-lower)/2 - 1;
+		}
+	} while (upper != lower);
+	last = i = upper;
+
+	/* expand to insert at i */
+	for (j = index; j > i; j--)
+		extlist[j] = extlist[j-1];
+
+	extlist[i] = ext;
+
+	return (0);
+}
+
+/* Return the number of blocks required by the package object provided. */
+static fsblkcnt_t
+nblks(short fsys_entry, struct cfextra *ext)
+{
+	fsblkcnt_t blk;
+	ulong_t block_size;
+	ulong_t frag_size;
+
+	block_size = (ulong_t)get_blk_size_n(fsys_entry);
+	frag_size = (ulong_t)get_frag_size_n(fsys_entry);
+
+	if (strchr("dxs", ext->cf_ent.ftype))
+		blk =
+		    nblk(block_size, block_size, frag_size);
+	else if (ext->cf_ent.cinfo.size != BADCONT)
+		blk = nblk(ext->cf_ent.cinfo.size, block_size,
+		    frag_size);
+	else
+		blk = 0;
+
+	return (blk);
+}
+
+/* Remove ext1 from the filesystem size calculations and add ext2. */
+static void
+size_xchng(struct cfextra *ext1, struct cfextra *ext2)
+{
+	fsblkcnt_t bused;
+	ulong_t block_size;
+	ulong_t frag_size;
+	fsblkcnt_t	blks1, blks2;
+	short	fsys_entry;
+
+	/*
+	 * Since these are on the same filesystem, either one will yield the
+	 * correct block and fragment size.
+	 */
+	fsys_entry = ext1->fsys_base;
+	block_size = (ulong_t)get_blk_size_n(fsys_entry);
+	frag_size = (ulong_t)get_frag_size_n(fsys_entry);
+
+	blks1 = nblk(ext1->cf_ent.cinfo.size, block_size, frag_size);
+	blks2 = nblk(ext2->cf_ent.cinfo.size, block_size, frag_size);
+
+	if (blks1 != blks2) {
+		/* First, lose the old size, then add the new size. */
+		bused = get_blk_used_n(fsys_entry);
+		bused -= nblks(fsys_entry, ext1);
+		bused += nblks(fsys_entry, ext2);
+
+		set_blk_used_n(fsys_entry, bused);
+	}
+}
+
+/*
+ * This function merges duplicate non-directory entries resulting from a
+ * dryrun or other procedure which preloads the extlist. It uses an odd
+ * heuristic to determine which package object is newest: only package
+ * objects from the dryrun file will have pinfo pointers. Therefore, the
+ * object with a pinfo pointer is from the dryrun file and it will be
+ * overwritten by the object being installed by this package.
+ *
+ * Assumptions:
+ *	1. The newer object will be overwriting the older object.
+ *	2. The two objects are close enough to the same size that
+ *	   the sizing is still OK.
+ *
+ * The calling routine will overwrite ept1, so this must return ept2 with
+ * the correct data to keep. There being only one logical outcome of a
+ * failure, this returns 1 for OK and 0 for FAIL.
+ */
+static int
+dup_merg(struct cfextra *ext1, struct cfextra *ext2)
+{
+	struct cfent *ept1, *ept2;
+
+	ept1 = &(ext1->cf_ent);
+	ept2 = &(ext2->cf_ent);
+
+	if (strchr("?dx", ept1->ftype))
+		return (0);
+
+	if (strchr("?dx", ept2->ftype))
+		return (0);
+
+	/* First, which is the eldest? */
+	if (ext2->mstat.preloaded) {
+		/*
+		 * While ept2 has the correct pinfo list (it was preloaded into
+		 * the array before the pkgmap was read), ept1 has everything
+		 * else. Here we copy the guts of ept1 into ept2.
+		 *
+		 * Start by grabbing the pointers to the ext2 items that we
+		 * need to either restore or free.
+		 */
+		/* to free() */
+		char *path = ept2->path;
+		char *local = ept2->ainfo.local;
+
+		/* to preserve */
+		short npkgs = ept2->npkgs;
+		struct pinfo *pinfo = ept2->pinfo;
+
+		/* Copy everything from the new entry to the old */
+		(void) memcpy(ept2, ept1, sizeof (struct cfent));
+
+		/* Now restore the original stuff.. */
+		ept2->path = path;
+		ept2->ainfo.local = local;
+		ept2->npkgs = npkgs;
+		ept2->pinfo = pinfo;
+
+		size_xchng(ext2, ext1);
+	} else if (ext1->mstat.preloaded) {
+		/*
+		 * ept2 is already the one we will keep. All we have to do is
+		 * copy over the pinfo pointer.
+		 */
+		ept2->pinfo = ept1->pinfo;
+		size_xchng(ext1, ext2);
+	} else
+		return (0);
+
+	return (1);
+}
+
+/*
+ * Check duplicate entries in the package object list. If it's a directory,
+ * this just merges them, if not, it returns a 0 to force further processing.
+ */
+static int
+ckdup(struct cfent *ept1, struct cfent *ept2)
+{
+	/* ept2 will be modified to contain "merged" entries */
+
+	if (!strchr("?dx", ept1->ftype))
+		return (0);
+
+	if (!strchr("?dx", ept2->ftype))
+		return (0);
+
+	if (ept2->ainfo.mode == BADMODE)
+		ept2->ainfo.mode = ept1->ainfo.mode;
+	if ((ept1->ainfo.mode != ept2->ainfo.mode) &&
+	    (ept1->ainfo.mode != BADMODE))
+		return (0);
+
+	if (strcmp(ept2->ainfo.owner, "?") == 0)
+		(void) strlcpy(ept2->ainfo.owner, ept1->ainfo.owner,
+			sizeof (ept2->ainfo.owner));
+	if (strcmp(ept1->ainfo.owner, ept2->ainfo.owner) &&
+	    strcmp(ept1->ainfo.owner, "?"))
+		return (0);
+
+	if (strcmp(ept2->ainfo.group, "?") == 0)
+		(void) strlcpy(ept2->ainfo.group, ept1->ainfo.group,
+			sizeof (ept2->ainfo.group));
+	if (strcmp(ept1->ainfo.group, ept2->ainfo.group) &&
+	    strcmp(ept1->ainfo.group, "?"))
+		return (0);
+
+	if (ept1->pinfo) {
+		ept2->npkgs = ept1->npkgs;
+		ept2->pinfo = ept1->pinfo;
+	}
+
+	return (1);
+}
+
+/*
+ * Replace the old package database entry with the new one preserving the
+ * data which remains constant across the replacement.
+ *	copied directly:
+ *		ftype, pkg_class
+ *
+ *	preserved from old:
+ *		path, npkgs, pinfo
+ */
+void
+repl_cfent(struct cfent *new, struct cfent *old)
+{
+	char *path = old->path;
+	short npkgs = old->npkgs;
+	struct pinfo *pinfo = old->pinfo;
+
+	/* Copy everything from the new entry over */
+	(void) memcpy(old, new, sizeof (struct cfent));
+
+	if (strchr("sl", new->ftype) == NULL)
+		old->ainfo.local = NULL;
+
+	old->path = path;
+	old->npkgs = npkgs;
+	old->pinfo = pinfo;
+
+	old->volno = 0;
+}
+
+/*
+ * Copy critical portions of cf_ent (from the package database) and el_ent
+ * (constructed from the pkgmap) into a merged cfent structure, tp. Then copy
+ * that to the el_ent structure. The approach we take here is to copy over
+ * everything from the package database entry, condition the paths based upon
+ * the currently installed path and then insert the following entries from
+ * the new structure :
+ *	cfent.volno
+ *	pkg_class
+ *	pkg_class_idx
+ *
+ * The pinfo list is then copied from the cfent list. While
+ * fsys_value is also copied over, it hasn't been set yet. This function
+ * copies over whatever the default value is from the new structure.
+ *
+ * The copied entry is returned in the el_ent argument and the function
+ * value is 1 on success, 0 on failure. There is no recovery plan for
+ * failure.
+ */
+int
+cp_cfent(struct cfent *cf_ent, struct cfextra *el_ent)
+{
+	struct cfextra	*tp;
+
+	/* Allocate space for cfent copy */
+	if ((tp = (struct cfextra *)calloc(1,
+	    sizeof (struct cfextra))) == NULL) {
+		progerr(gettext("cp_cfent: memory allocation error"));
+		return (0);
+	}
+
+	/* Copy everything from the package database over */
+	(void) memcpy(&(tp->cf_ent), cf_ent, sizeof (struct cfent));
+
+	/* Now overlay new items from the pkgmap */
+	tp->fsys_value = el_ent->fsys_value;
+	tp->cf_ent.volno = el_ent->cf_ent.volno;
+	(void) strlcpy(tp->cf_ent.pkg_class, el_ent->cf_ent.pkg_class,
+			sizeof (tp->cf_ent.pkg_class));
+	tp->cf_ent.pkg_class_idx = el_ent->cf_ent.pkg_class_idx;
+	tp->cf_ent.pinfo = cf_ent->pinfo;
+
+	/*
+	 * The paths are identical, so we get them from the new entry.  These
+	 * are pointing to a malloc'd section of memory containing a string
+	 * that we aren't moving in this operation, so everybody points to
+	 * the same thing during these transfers.
+	 */
+	tp->cf_ent.path = el_ent->client_path;
+	tp->server_path = el_ent->server_path;
+	tp->client_path = el_ent->client_path;
+	tp->map_path = el_ent->map_path;
+
+	/*
+	 * Since instvol() expects to work with the *original* mstat data,
+	 * mstat is just copied here. NOTE: mstat looks like a structure, but
+	 * it's really a short bit array.
+	 */
+	tp->mstat = el_ent->mstat;
+
+	/* Copy everything from the temporary structure to the new entry */
+	(void) memcpy(el_ent, tp, sizeof (struct cfextra));
+	free(tp);
+
+	return (1);
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/usr/src/cmd/svr4pkg/libinst/pkgops.c	Fri Jun 05 10:28:40 2009 -0400
@@ -0,0 +1,1426 @@
+/*
+ * 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
+ * or http://www.opensolaris.org/os/licensing.
+ * 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 2009 Sun Microsystems, Inc.  All rights reserved.
+ * Use is subject to license terms.
+ */
+
+
+#include <stdio.h>
+#include <limits.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <string.h>
+#include <fcntl.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <signal.h>
+#include <errno.h>
+#include <assert.h>
+#include <pkgdev.h>
+#include <pkginfo.h>
+#include <pkglocs.h>
+#include <locale.h>
+#include <libintl.h>
+#include <instzones_api.h>
+#include <pkglib.h>
+#include <install.h>
+#include <libinst.h>
+#include <libadm.h>
+#include <messages.h>
+
+/* commands to execute */
+
+#define	PKGINFO_CMD	"/usr/bin/pkginfo"
+
+#define	GLOBALZONE_ONLY_PACKAGE_FILE_PATH	\
+					"/var/sadm/install/gz-only-packages"
+
+#if	!defined(TEXT_DOMAIN)	/* Should be defined by cc -D */
+#define	TEXT_DOMAIN	"SYS_TEST"
+#endif
+
+/*
+ * forward declarations
+ */
+
+static void		_pkginfoInit(struct pkginfo *a_info);
+static struct pkginfo	*_pkginfoFactory(void);
+static char		**thisZonePackages;
+static int		numThisZonePackages;
+
+/*
+ * *****************************************************************************
+ * global external (public) functions
+ * *****************************************************************************
+ */
+
+/*
+ * Name:	pkginfoFree
+ * Description:	free pkginfo structure returned from various functions
+ * Arguments:	r_info - pointer to pointer to pkginfo structure to free
+ * Returns:	void
+ */
+
+void
+pkginfoFree(struct pkginfo **r_info)
+{
+	struct pkginfo	*pinfo;
+
+	/* entry assertions */
+
+	assert(r_info != (struct pkginfo **)NULL);
+
+	/* localize reference to info structure to free */
+
+	pinfo = *r_info;
+
+	/* reset callers handle to info structure */
+
+	*r_info = (struct pkginfo *)NULL;
+
+	assert(pinfo != (struct pkginfo *)NULL);
+
+	/* free up contents of the structure */
+
+	_pkginfoInit(pinfo);
+
+	/* free up structure itself */
+
+	(void) free(pinfo);
+}
+
+/*
+ * Name:	pkginfoIsPkgInstalled
+ * Description:	determine if specified package is installed, return pkginfo
+ *		structure describing package if package is installed
+ * Arguments:	r_pinfo - pointer to pointer to pkginfo structure
+ *			If this pointer is NOT null:
+ *			-On success, this handle is filled in with a pointer
+ *			--to a newly allocated pkginfo structure describing
+ *			--the package discovered
+ *			-On failure, this handle is filled with NULL
+ *			If this pointer is NULL:
+ *			-no pkginfo structure is returned on success.
+ *		a_pkgInst - package instance (name) to lookup
+ * Returns:	boolean_t
+ *			B_TRUE - package installed, pkginfo returned
+ *			B_FALSE - package not installed, no pkginfo returned
+ * NOTE:	This function returns the first instance of package that
+ *		is installed - see pkginfo() function for details
+ * NOTE:    	Any pkginfo structure returned is placed in new storage for the
+ *		calling function. The caller must use 'pkginfoFree' to dispose
+ *		of the storage once the pkginfo structure is no longer needed.
+ */
+
+boolean_t
+pkginfoIsPkgInstalled(struct pkginfo **r_pinfo, char *a_pkgInst)
+{
+	int		r;
+	struct pkginfo	*pinf;
+
+	/* entry assertions */
+
+	assert(a_pkgInst != (char *)NULL);
+	assert(*a_pkgInst != '\0');
+
+	/* reset returned pkginfo structure handle */
+
+	if (r_pinfo != (struct pkginfo **)NULL) {
+		*r_pinfo = (struct pkginfo *)NULL;
+	}
+
+	/* allocate a new pinfo structure for use in the call to pkginfo */
+
+	pinf = _pkginfoFactory();
+
+	/* lookup the specified package */
+
+	/* NOTE: required 'pkgdir' set to spool directory or NULL */
+	r = pkginfo(pinf, a_pkgInst, NULL, NULL);
+	echoDebug(DBG_PKGOPS_PKGINFO_RETURNED, a_pkgInst, r);
+
+	if (r_pinfo != (struct pkginfo **)NULL) {
+		*r_pinfo = pinf;
+	} else {
+		/* free pkginfo structure */
+		pkginfoFree(&pinf);
+	}
+
+	return (r == 0 ? B_TRUE : B_FALSE);
+}
+
+/*
+ * Name:	pkgOpenInGzOnlyFile
+ * Description:	Open the global zone only package list file
+ * Arguments:	a_rootPath - pointer to string representing the root path
+ *			where the global zone only package list file is
+ *			located - NULL is the same as "/"
+ * Returns:	FILE *
+ *			== NULL - failure - file not open
+ *			!= NULL - success - file pointer returned
+ * NOTE:	This function will create the file if it does not exist.
+ */
+
+FILE *
+pkgOpenInGzOnlyFile(char *a_rootPath)
+{
+	FILE	*pkgingzonlyFP;
+	char	pkgingzonlyPath[PATH_MAX];
+	int	len;
+
+	/* normalize root path */
+
+	if (a_rootPath == (char *)NULL) {
+		a_rootPath = "";
+	}
+
+	/* generate path to glocal zone only list file */
+
+	len = snprintf(pkgingzonlyPath, sizeof (pkgingzonlyPath), "%s/%s",
+		a_rootPath, GLOBALZONE_ONLY_PACKAGE_FILE_PATH);
+	if (len > sizeof (pkgingzonlyPath)) {
+		progerr(ERR_CREATE_PATH_2, a_rootPath,
+				GLOBALZONE_ONLY_PACKAGE_FILE_PATH);
+		return ((FILE *)NULL);
+	}
+
+	/* open global zone only list file */
+
+	pkgingzonlyFP = fopen(pkgingzonlyPath, "r+");
+	if ((pkgingzonlyFP == (FILE *)NULL) && (errno == ENOENT)) {
+		pkgingzonlyFP = fopen(pkgingzonlyPath, "w+");
+	}
+
+	if ((pkgingzonlyFP == (FILE *)NULL) && (errno != ENOENT)) {
+		progerr(ERR_PKGOPS_OPEN_GZONLY, pkgingzonlyPath,
+				strerror(errno));
+		return ((FILE *)NULL);
+	}
+
+	/* success - return FILE pointer open on global zone only list file */
+
+	return (pkgingzonlyFP);
+}
+
+/*
+ * Name:	pkgIsPkgInGzOnly
+ * Description:	determine if package is recorded as "in global zone only"
+ *		by opening the appropriate files and searching for the
+ *		specified package
+ * Arguments:	a_rootPath - pointer to string representing the root path
+ *			where the global zone only package list file is
+ *			located - NULL is the same as "/"
+ *		a_pkgInst - pointer to string representing the package instance
+ *			(name) of the package to lookup
+ * Returns:	boolean_t
+ *			B_TRUE - package is recorded as "in global zone only"
+ *			B_FALSE - package is NOT recorded as "in gz only"
+ * NOTE:	This function will create the file if it does not exist.
+ */
+
+boolean_t
+pkgIsPkgInGzOnly(char *a_rootPath, char *a_pkgInst)
+{
+	FILE		*fp;
+	boolean_t	in_gz_only;
+
+	/* normalize root path */
+
+	if (a_rootPath == (char *)NULL) {
+		a_rootPath = "";
+	}
+
+	/* open the global zone only package list file */
+
+	fp = pkgOpenInGzOnlyFile(a_rootPath);
+	if (fp == (FILE *)NULL) {
+		echoDebug(ERR_PKGOPS_CANNOT_OPEN_GZONLY,
+				a_rootPath ? a_rootPath : "/");
+		return (B_FALSE);
+	}
+
+	/* is the package recorded as "in global zone only" ? */
+
+	in_gz_only = pkgIsPkgInGzOnlyFP(fp, a_pkgInst);
+
+	/* close the global zone only package list file */
+
+	(void) fclose(fp);
+
+	/* return results */
+
+	return (in_gz_only);
+}
+
+/*
+ * Name:	pkgIsPkgInGzOnly
+ * Description:	determine if package is recorded as "in global zone only"
+ *		by searching the specified open FILE for the specified package
+ * Arguments:	a_fp - pointer to FILE handle open on file to search
+ *		a_pkgInst - pointer to string representing the package instance
+ *			(name) of the package to lookup
+ * Returns:	boolean_t
+ *			B_TRUE - package is recorded as "in global zone only"
+ *			B_FALSE - package is NOT recorded as "in gz only"
+ */
+
+boolean_t
+pkgIsPkgInGzOnlyFP(FILE *a_fp, char *a_pkgInst)
+{
+	char	line[PATH_MAX+1];
+
+	/* entry assertions */
+
+	assert(a_fp != (FILE *)NULL);
+	assert(a_pkgInst != (char *)NULL);
+	assert(*a_pkgInst != '\0');
+
+	/* rewind the file to the beginning */
+
+	rewind(a_fp);
+
+	/* read the file line by line searching for the specified package */
+
+	while (fgets(line, sizeof (line), a_fp) != (char *)NULL) {
+		int	len;
+
+		/* strip off trailing newlines */
+		len = strlen(line);
+		while ((len > 0) && (line[len-1] == '\n')) {
+			line[--len] = '\0';
+		}
+
+		/* ignore blank and comment lines */
+		if ((line[0] == '#') || (line[0] == '\0')) {
+			continue;
+		}
+
+		/* return true if this is the package we are looking for */
+		if (strcmp(a_pkgInst, line) == 0) {
+			echoDebug(DBG_PKGOPS_PKG_IS_GZONLY, a_pkgInst);
+			return (B_TRUE);
+		}
+	}
+
+	/* end of file - package not found */
+
+	echoDebug(DBG_PKGOPS_PKG_NOT_GZONLY, a_pkgInst);
+
+	return (B_FALSE);
+}
+
+/*
+ * Name:	pkgRemovePackageFromGzonlyList
+ * Description:	Remove specified package from the global zone only package list
+ *		file located at a specified root path
+ * Arguments:	a_rootPath - pointer to string representing the root path
+ *			where the global zone only package list file is
+ *			located - NULL is the same as "/"
+ *		a_pkgInst - pointer to string representing the package instance
+ *			(name) of the package to remove
+ * Returns:	boolean_t
+ *			B_TRUE - package is successfully removed
+ *			B_FALSE - failed to remove package from file
+ * NOTE:	This function will create the file if it does not exist.
+ */
+
+boolean_t
+pkgRemovePackageFromGzonlyList(char *a_rootPath, char *a_pkgInst)
+{
+	FILE		*destFP;
+	FILE		*srcFP;
+	boolean_t	pkgremoved = B_FALSE;
+	char		destPath[PATH_MAX];
+	char		line[PATH_MAX+1];
+	char		savePath[PATH_MAX];
+	char		srcPath[PATH_MAX];
+	char		timeb[BUFSIZ];
+	int		len;
+	struct tm	*timep;
+	time_t		clock;
+
+	/* entry assertions */
+
+	assert(a_pkgInst != (char *)NULL);
+	assert(*a_pkgInst != '\0');
+
+	/* normalize root path */
+
+	if (a_rootPath == (char *)NULL) {
+		a_rootPath = "";
+	}
+
+	/*
+	 * calculate paths to various objects
+	 */
+
+	/* path to current "source" ingzonly file */
+
+	len = snprintf(srcPath, sizeof (srcPath), "%s/%s",
+		a_rootPath, GLOBALZONE_ONLY_PACKAGE_FILE_PATH);
+	if (len > sizeof (srcPath)) {
+		progerr(ERR_CREATE_PATH_2, a_rootPath,
+				GLOBALZONE_ONLY_PACKAGE_FILE_PATH);
+		return (B_FALSE);
+	}
+
+	/* path to new "destination" ingzonly file */
+
+	len = snprintf(destPath, sizeof (destPath), "%s/%s.tmp",
+		a_rootPath, GLOBALZONE_ONLY_PACKAGE_FILE_PATH);
+	if (len > sizeof (srcPath)) {
+		progerr(ERR_CREATE_PATH_2, a_rootPath,
+				GLOBALZONE_ONLY_PACKAGE_FILE_PATH);
+		return (B_FALSE);
+	}
+
+	/* path to temporary "saved" ingzonly file */
+
+	len = snprintf(savePath, sizeof (savePath), "%s/%s.save",
+		a_rootPath, GLOBALZONE_ONLY_PACKAGE_FILE_PATH);
+	if (len > sizeof (srcPath)) {
+		progerr(ERR_CREATE_PATH_2, a_rootPath,
+				GLOBALZONE_ONLY_PACKAGE_FILE_PATH);
+		return (B_FALSE);
+	}
+
+	/* open source file, creating if necessary */
+
+	srcFP = fopen(srcPath, "r+");
+	if ((srcFP == (FILE *)NULL) && (errno == ENOENT)) {
+		srcFP = fopen(srcPath, "w+");
+	}
+
+	/* error if could not open/create file */
+
+	if (srcFP == (FILE *)NULL) {
+		progerr(ERR_PKGOPS_OPEN_GZONLY, srcPath, strerror(errno));
+		return (B_FALSE);
+	}
+
+	/* open/create new destination file */
+
+	(void) remove(destPath);
+	destFP = fopen(destPath, "w");
+	if (destFP == (FILE *)NULL) {
+		progerr(ERR_PKGOPS_TMPOPEN, destPath, strerror(errno));
+		if (srcFP != (FILE *)NULL) {
+			(void) fclose(srcFP);
+		}
+		return (B_FALSE);
+	}
+
+	/* add standard comment to beginning of file */
+
+	(void) time(&clock);
+	timep = localtime(&clock);
+
+	(void) strftime(timeb, sizeof (timeb), "%c\n", timep);
+
+	/* put standard header at the beginning of the file */
+
+	(void) fprintf(destFP, MSG_GZONLY_FILE_HEADER,
+			get_prog_name(), "remove", a_pkgInst, timeb);
+
+	/* read source/write destination - removing specified package */
+
+	while (fgets(line, sizeof (line), srcFP) != (char *)NULL) {
+		int	len;
+
+		/* strip off trailing newlines */
+		len = strlen(line);
+		while ((len > 0) && (line[len-1] == '\n')) {
+			line[--len] = '\0';
+		}
+
+		/* ignore blank and comment lines */
+		if ((line[0] == '#') || (line[0] == '\0')) {
+			continue;
+		}
+
+		/* add pkg if yet to add and pkg <= line */
+		if ((pkgremoved == B_FALSE) && (strcmp(a_pkgInst, line) == 0)) {
+			pkgremoved = B_TRUE;
+		} else {
+			(void) fprintf(destFP, "%s\n", line);
+		}
+	}
+
+	/* close both files */
+
+	(void) fclose(srcFP);
+
+	(void) fclose(destFP);
+
+	/*
+	 * if package not found there is no need to update the original file
+	 */
+
+	if (pkgremoved == B_FALSE) {
+		(void) unlink(destPath);
+		return (B_TRUE);
+	}
+
+	/*
+	 * Now we want to make a copy of the old gzonly file as a
+	 * fail-safe.
+	 */
+
+	if ((access(savePath, F_OK) == 0) && remove(savePath)) {
+		progerr(ERR_REMOVE, savePath, strerror(errno));
+		(void) remove(destPath);
+		return (B_FALSE);
+	}
+
+	if (link(srcPath, savePath) != 0) {
+		progerr(ERR_LINK, savePath, srcPath, strerror(errno));
+		(void) remove(destPath);
+		return (B_FALSE);
+	}
+
+	if (rename(destPath, srcPath) != 0) {
+		progerr(ERR_RENAME, destPath, srcPath, strerror(errno));
+		if (rename(savePath, srcPath)) {
+			progerr(ERR_RENAME, savePath, srcPath, strerror(errno));
+		}
+		(void) remove(destPath);
+		return (B_FALSE);
+	}
+
+	if (remove(savePath) != 0) {
+		progerr(ERR_REMOVE, savePath, strerror(errno));
+	}
+
+	/* successfully removed package */
+
+	echoDebug(DBG_PKGOPS_REMOVED_GZPKG, a_pkgInst);
+
+	return (B_TRUE);
+}
+
+/*
+ * Name:	pkgAddPackageFromGzonlyList
+ * Description:	Add specified package to the global zone only package list
+ *		file located at a specified root path
+ * Arguments:	a_rootPath - pointer to string representing the root path
+ *			where the global zone only package list file is
+ *			located - NULL is the same as "/"
+ *		a_pkgInst - pointer to string representing the package instance
+ *			(name) of the package to add
+ * Returns:	boolean_t
+ *			B_TRUE - package is successfully added
+ *			B_FALSE - failed to add package to the file
+ * NOTE:	This function will create the file if it does not exist.
+ */
+
+boolean_t
+pkgAddPackageToGzonlyList(char *a_pkgInst, char *a_rootPath)
+{
+	FILE		*destFP;
+	FILE		*srcFP;
+	boolean_t	pkgadded = B_FALSE;
+	char		destPath[PATH_MAX];
+	char		line[PATH_MAX+1];
+	char		savePath[PATH_MAX];
+	char		srcPath[PATH_MAX];
+	char		timeb[BUFSIZ];
+	int		len;
+	struct tm	*timep;
+	time_t		clock;
+
+	/* entry assertions */
+
+	assert(a_pkgInst != (char *)NULL);
+	assert(*a_pkgInst != '\0');
+
+	/* normalize root path */
+
+	if (a_rootPath == (char *)NULL) {
+		a_rootPath = "";
+	}
+
+	/* entry debugging info */
+
+	echoDebug(DBG_PKGOPS_ADDGZPKG, a_pkgInst, a_rootPath);
+
+	/*
+	 * calculate paths to various objects
+	 */
+
+	/* path to current "source" ingzonly file */
+
+	len = snprintf(srcPath, sizeof (srcPath), "%s/%s",
+		a_rootPath, GLOBALZONE_ONLY_PACKAGE_FILE_PATH);
+	if (len > sizeof (srcPath)) {
+		progerr(ERR_CREATE_PATH_2, a_rootPath,
+				GLOBALZONE_ONLY_PACKAGE_FILE_PATH);
+		return (B_FALSE);
+	}
+
+	/* path to new "destination" ingzonly file */
+
+	len = snprintf(destPath, sizeof (destPath), "%s/%s.tmp",
+		a_rootPath, GLOBALZONE_ONLY_PACKAGE_FILE_PATH);
+	if (len > sizeof (srcPath)) {
+		progerr(ERR_CREATE_PATH_2, a_rootPath,
+				GLOBALZONE_ONLY_PACKAGE_FILE_PATH);
+		return (B_FALSE);
+	}
+
+	/* path to temporary "saved" ingzonly file */
+
+	len = snprintf(savePath, sizeof (savePath), "%s/%s.save",
+		a_rootPath, GLOBALZONE_ONLY_PACKAGE_FILE_PATH);
+	if (len > sizeof (srcPath)) {
+		progerr(ERR_CREATE_PATH_2, a_rootPath,
+				GLOBALZONE_ONLY_PACKAGE_FILE_PATH);
+		return (B_FALSE);
+	}
+
+	/* open source file, creating if necessary */
+
+	srcFP = fopen(srcPath, "r+");
+	if ((srcFP == (FILE *)NULL) && (errno == ENOENT)) {
+		srcFP = fopen(srcPath, "w+");
+	}
+
+	/* error if could not open/create file */
+
+	if (srcFP == (FILE *)NULL) {
+		progerr(ERR_PKGOPS_OPEN_GZONLY, srcPath, strerror(errno));
+		return (B_FALSE);
+	}
+
+	/* open/create new destination file */
+
+	(void) remove(destPath);
+	destFP = fopen(destPath, "w");
+	if (destFP == (FILE *)NULL) {
+		progerr(ERR_PKGOPS_TMPOPEN, destPath, strerror(errno));
+		if (srcFP != (FILE *)NULL) {
+			(void) fclose(srcFP);
+		}
+		return (B_FALSE);
+	}
+
+	/* add standard comment to beginning of file */
+
+	(void) time(&clock);
+	timep = localtime(&clock);
+
+	(void) strftime(timeb, sizeof (timeb), "%c\n", timep);
+
+	/* put standard header at the beginning of the file */
+
+	(void) fprintf(destFP, MSG_GZONLY_FILE_HEADER,
+			get_prog_name(), "add", a_pkgInst, timeb);
+
+	/* read source/write destination; add package at appropriate location */
+
+	while (fgets(line, sizeof (line), srcFP) != (char *)NULL) {
+		int	len;
+
+		/* strip off trailing newlines */
+		len = strlen(line);
+		while ((len > 0) && (line[len-1] == '\n')) {
+			line[--len] = '\0';
+		}
+
+		/* ignore blank and comment lines */
+		if ((line[0] == '#') || (line[0] == '\0')) {
+			continue;
+		}
+
+		/* add pkg if yet to add and pkg <= line */
+		if ((pkgadded == B_FALSE) && (strcmp(a_pkgInst, line) <= 0)) {
+			if (strcmp(a_pkgInst, line) != 0) {
+				(void) fprintf(destFP, "%s\n", a_pkgInst);
+			}
+			pkgadded = B_TRUE;
+		}
+
+		(void) fprintf(destFP, "%s\n", line);
+	}
+
+	/* if package not added yet, add to end of the file */
+
+	if (pkgadded == B_FALSE) {
+		(void) fprintf(destFP, "%s\n", a_pkgInst);
+	}
+
+	/* close both files */
+
+	(void) fclose(srcFP);
+
+	(void) fclose(destFP);
+
+	/*
+	 * Now we want to make a copy of the old gzonly file as a
+	 * fail-safe.
+	 */
+
+	if ((access(savePath, F_OK) == 0) && remove(savePath)) {
+		progerr(ERR_REMOVE, savePath, strerror(errno));
+		(void) remove(destPath);
+		return (B_FALSE);
+	}
+
+	if (link(srcPath, savePath) != 0) {
+		progerr(ERR_LINK, savePath, srcPath, strerror(errno));
+		(void) remove(destPath);
+		return (B_FALSE);
+	}
+
+	if (rename(destPath, srcPath) != 0) {
+		progerr(ERR_RENAME, destPath, srcPath, strerror(errno));
+		if (rename(savePath, srcPath)) {
+			progerr(ERR_RENAME, savePath, srcPath, strerror(errno));
+		}
+		(void) remove(destPath);
+		return (B_FALSE);
+	}
+
+	if (remove(savePath) != 0) {
+		progerr(ERR_REMOVE, savePath, strerror(errno));
+	}
+
+	/* successfully added package */
+
+	echoDebug(DBG_PKGOPS_ADDED_GZPKG, a_pkgInst);
+
+	return (B_TRUE);
+}
+
+/*
+ * Name:	pkginfoParamTruth
+ * Description:	Search pkginfo file for specified parameter/value pair
+ * Arguments:	a_fp - Pointer to FILE handle open on pkginfo file to search
+ *		a_param - Pointer to string representing the parameter name
+ *			to search for
+ *		a_value - Pointer to string representing the "success" value
+ *			being searched for
+ *		a_default - determine results if parameter NOT found
+ *			B_TRUE - parameter is TRUE if not found
+ *			B_FALSE - parameter is FALSE if not found
+ * Returns:	boolean_t
+ *		B_TRUE - the parameter was found and matched the specified value
+ *			OR the paramter was not found and a_default == B_TRUE
+ *		B_FALSE - the parameter was found and did NOT match the value
+ *			OR the paramter was not found and a_default == B_FALSE
+ */
+
+boolean_t
+pkginfoParamTruth(FILE *a_fp, char *a_param, char *a_value, boolean_t a_default)
+{
+	char		*param;
+	boolean_t	result;
+
+	/* entry assertions */
+
+	assert(a_fp != (FILE *)NULL);
+	assert(a_param != (char *)NULL);
+	assert(*a_param != '\0');
+	assert(a_value != (char *)NULL);
+	assert(*a_value != '\0');
+
+	/* rewind the file to the beginning */
+
+	rewind(a_fp);
+
+	/* search pkginfo file for the specified parameter */
+
+	param = fpkgparam(a_fp, a_param);
+
+	if (param == (char *)NULL) {
+		/* parameter not found - return default */
+		result = a_default;
+	} else if (*param == '\0') {
+		/* parameter found but no value - return default */
+		result = a_default;
+	} else if (strcasecmp(param, a_value) == 0) {
+		/* paramter found - matches value */
+		result = B_TRUE;
+	} else {
+		/* parameter found - does not match value */
+		result = B_FALSE;
+	}
+
+	/* exit debugging info */
+
+	echoDebug(DBG_PKGOPS_PARAMTRUTH_RESULTS,
+		a_param, a_value, a_default == B_TRUE ? "true" : "false",
+		param ? param : "?", result == B_TRUE ? "true" : "false");
+
+	/* if parameter value found, free results */
+
+	if (param != (char *)NULL) {
+		(void) free(param);
+	}
+
+	/* return results of search */
+
+	return (result);
+}
+
+/*
+ * Name:	pkgGetPackageList
+ * Description:	Determine list of packages based on list of packages that are
+ *		available, category of packages to select, and list of packages
+ *		to select.
+ * Arguments:	r_pkgList - pointer to pointer to string array where the list
+ *			of selected packages will be returned
+ *		a_argv - pointer to string array containing list of packages
+ *			to select
+ *		a_optind - index into string array of first package to select
+ *		a_categories - pointer to string representing the categories of
+ *			packages to select
+ *		a_categoryList - pointer to string array representing a list
+ *			of categories to select
+ *		a_pkgdev - package dev containing packages that can be selected
+ * Returns:	int
+ *	== 0  - packages found r_pkgList contains results package list retrieved
+ *	== -1 - no packages found (errno == ENOPKG)
+ *	!= 0 - "quit" value entered by user
+ * NOTE:	If both a category and a list of packages to select are provided
+ *		the category is used over the list of packages provided
+ * NOTE:	If neither a category nor a list of packages to select are
+ *		provided, an error is returned
+ */
+
+int
+pkgGetPackageList(char ***r_pkgList, char **a_argv, int a_optind,
+	char *a_categories, char **a_categoryList, struct pkgdev *a_pkgdev)
+{
+	char	*all_pkgs[4] = {"all", NULL};
+
+	/* entry assertions */
+
+	assert(a_pkgdev != (struct pkgdev *)NULL);
+	assert(a_pkgdev->dirname != (char *)NULL);
+	assert(*a_pkgdev->dirname != '\0');
+	assert(r_pkgList != (char ***)NULL);
+	assert(a_argv != (char **)NULL);
+
+	/* entry debugging info */
+
+	echoDebug(DBG_PKGOPS_GETPKGLIST_ENTRY);
+	echoDebug(DBG_PKGOPS_GETPKGLIST_ARGS, a_pkgdev->dirname,
+			a_categories ? a_categories : "?");
+
+	/* reset returned package list handle */
+
+	*r_pkgList = (char **)NULL;
+
+	/*
+	 * generate list of packages to be removed: if removing by category,
+	 * then generate package list based on all packages by category,
+	 * else generate package list based on all packages specified.
+	 */
+
+	if (a_categories != NULL) {
+		/* generate package list from all packages in given category */
+
+		*r_pkgList = gpkglist(a_pkgdev->dirname, &all_pkgs[0],
+					a_categoryList);
+
+		if (*r_pkgList == NULL) {
+			echoDebug(DBG_PKGOPS_GPKGLIST_CATFAILED, a_categories);
+			progerr(ERR_CAT_FND, a_categories);
+			return (1);
+		}
+
+		echoDebug(DBG_PKGOPS_GPKGLIST_CATOK, a_categories);
+
+		return (0);
+	}
+
+	/* generate package list from specified packages */
+
+	*r_pkgList = gpkglist(a_pkgdev->dirname, &a_argv[a_optind], NULL);
+
+	/* if list generated return results */
+
+	if (*r_pkgList != NULL) {
+		echoDebug(DBG_PKGOPS_GPKGLIST_OK);
+		return (0);
+	}
+
+	/* handle error from gpkglist */
+
+	switch (errno) {
+	    case ENOPKG:	/* no packages */
+		echoDebug(DBG_PKGOPS_GPKGLIST_ENOPKG);
+		return (-1);
+
+	    case ESRCH:
+		echoDebug(DBG_PKGOPS_GPKGLIST_ESRCH);
+		return (1);
+
+	    case EINTR:
+		echoDebug(DBG_PKGOPS_GPKGLIST_EINTR);
+		return (3);
+
+	    default:
+		echoDebug(DBG_PKGOPS_GPKGLIST_UNKNOWN, errno);
+		progerr(ERR_GPKGLIST_ERROR);
+		return (99);
+	}
+}
+
+/*
+ * Name:	pkgMatchInherited
+ * Description:	given a pointer to a "source" and a "destination" for an object,
+ *		along with other attributes of the object, determine if the
+ *		object is already installed and is current.
+ * Arguments:	a_src - pointer to string representing the "source" file to
+ *			verify - this would be the current temporary location of
+ *			the file that would be installed
+ *		a_dst - pointer to string representing the "destination" file to
+ *			verify - this would be the ultimate destination for the
+ *			file if installed
+ *		a_rootDir - pointer to string representing the "root directory"
+ *			where the package is being installed
+ *		a_mode - final "mode" file should have when installed
+ *		a_modtime - final "modtime" file should have when installed
+ *		a_ftype - contents "type" of file (f/e/v/s/l)
+ *		a_cksum - final "checksum" file should have when installed
+ * Returns:	boolean_t
+ *			B_TRUE - the specified source file MATCHES the file
+ *				located at the specified destination
+ *			B_FALSE - the specified source files does NOT match
+ *				the file located at the specified destination
+ */
+
+boolean_t
+pkgMatchInherited(char *a_src, char *a_dst, char *a_rootDir,
+	char a_mode, time_t a_modtime, char a_ftype, unsigned long a_cksum)
+{
+	char		cwd[PATH_MAX+1] = {'\0'};
+	char		dstpath[PATH_MAX+1];
+	int		cksumerr;
+	int		n;
+	struct stat	statbufDst;
+	struct stat	statbufSrc;
+	unsigned long	dstcksum;
+	unsigned long	srcksum;
+
+	/* entry assertions */
+
+	assert(a_src != (char *)NULL);
+	assert(*a_src != '\0');
+	assert(a_dst != (char *)NULL);
+	assert(*a_dst != '\0');
+
+	/* normalize root directory */
+
+	if ((a_rootDir == (char *)NULL) || (*a_rootDir == '\0')) {
+		a_rootDir = "/";
+	}
+
+	/* entry debugging */
+
+	echoDebug(DBG_PKGOPS_MATCHINHERIT_ENTRY);
+	echoDebug(DBG_PKGOPS_MATCHINHERIT_ARGS, a_src, a_dst, a_rootDir,
+		a_mode, a_modtime, a_ftype, a_cksum);
+
+	/* save current working directory - resolvepath can change it */
+
+	(void) getcwd(cwd, sizeof (cwd));
+
+	n = resolvepath(a_dst, dstpath, sizeof (dstpath));
+	if (n <= 0) {
+		if (errno != ENOENT) {
+			progerr(ERR_RESOLVEPATH, a_dst, strerror(errno));
+		}
+		(void) chdir(cwd);
+		return (B_FALSE);
+	}
+	dstpath[n++] = '\0';	/* make sure string is terminated */
+
+	/* return false if path is not in inherited file system space */
+
+	if (!z_path_is_inherited(dstpath, a_ftype, a_rootDir)) {
+		return (B_FALSE);
+	}
+
+	/*
+	 * path is in inherited file system space: verify existence
+	 */
+
+	/* return false if source file cannot be stat()ed */
+
+	if (stat(a_src, &statbufSrc) != 0) {
+		progerr(ERR_STAT, a_src, strerror(errno));
+		return (B_FALSE);
+	}
+
+	/* return false if destination file cannot be stat()ed */
+
+	if (stat(dstpath, &statbufDst) != 0) {
+		progerr(ERR_STAT, dstpath, strerror(errno));
+		return (B_FALSE);
+	}
+
+	/*
+	 * if this is an editable or volatile file, then the only
+	 * thing to guarantee is that the file exists - the file
+	 * attributes do not need to match
+	 */
+
+	/* editable file only needs to exist */
+
+	if (a_ftype == 'e') {
+		echoDebug(DBG_PKGOPS_EDITABLE_EXISTS, dstpath);
+		return (B_TRUE);
+	}
+
+	/* volatile file only needs to exist */
+
+	if (a_ftype == 'v') {
+		echoDebug(DBG_PKGOPS_VOLATILE_EXISTS, dstpath);
+		return (B_TRUE);
+	}
+
+	/*
+	 * verify modtime if file is not modifiable after install
+	 */
+
+	/* return false if source and destination have different mod times */
+
+	if (statbufSrc.st_mtim.tv_sec != statbufDst.st_mtim.tv_sec) {
+		echoDebug(DBG_PKGOPS_MOD_MISMATCH,  a_src,
+			statbufSrc.st_mtim.tv_sec, dstpath,
+			statbufDst.st_mtim.tv_sec);
+		return (B_FALSE);
+	}
+
+	/* return false if destination does not have required mod time */
+
+	if (statbufDst.st_mtim.tv_sec != a_modtime) {
+		echoDebug(DBG_PKGOPS_MOD_MISMATCH, dstpath,
+			statbufDst.st_mtim.tv_sec, "source", a_modtime);
+		return (B_FALSE);
+	}
+
+	/*
+	 * verify checksums of both files
+	 */
+
+	/* generate checksum of installed file */
+
+	cksumerr = 0;
+	dstcksum = compute_checksum(&cksumerr, dstpath);
+	if (cksumerr != 0) {
+		progerr(ERR_CANNOT_CKSUM_FILE, dstpath, strerror(errno));
+		return (B_FALSE);
+	}
+
+	/* return false if destination does not match recorded checksum */
+
+	if (dstcksum != a_cksum) {
+		echoDebug(DBG_PKGOPS_CKSUM_MISMATCH, dstpath, dstcksum,
+				"source", a_cksum);
+		return (B_FALSE);
+	}
+
+	/* generate checksum of file to install */
+
+	cksumerr = 0;
+	srcksum = compute_checksum(&cksumerr, a_src);
+	if (cksumerr != 0) {
+		progerr(ERR_CANNOT_CKSUM_FILE, a_src, strerror(errno));
+		return (B_FALSE);
+	}
+
+	/* return false if source to install does not match recorded checksum */
+
+	if (srcksum != dstcksum) {
+		echoDebug(DBG_PKGOPS_CKSUM_MISMATCH, a_src, srcksum, dstpath,
+				dstcksum);
+		return (B_FALSE);
+	}
+
+	/* src/dest identical - return true */
+
+	echoDebug(DBG_PKGOPS_IS_INHERITED, dstpath, "");
+
+	return (B_TRUE);
+}
+
+/*
+ * return string representing path to "global zone only file"
+ */
+
+char *
+pkgGetGzOnlyPath(void)
+{
+	return (GLOBALZONE_ONLY_PACKAGE_FILE_PATH);
+}
+
+/*
+ * Name:	pkgAddThisZonePackage
+ * Description:	Add specified package to internal list of "this zone only" pkgs
+ * Arguments:	a_pkgInst - name of package to add to list
+ * Returns:	void
+ */
+
+void
+pkgAddThisZonePackage(char *a_pkgInst)
+{
+	/* entry assertions */
+
+	assert(a_pkgInst != (char *)NULL);
+	assert(*a_pkgInst != '\0');
+
+	/* do not duplicate entries */
+
+	if (pkgPackageIsThisZone(a_pkgInst) == B_TRUE) {
+		return;
+	}
+
+	/* add package name to internal list */
+
+	if (thisZonePackages == (char **)NULL) {
+		thisZonePackages =
+				(char **)calloc(2, sizeof (char **));
+	} else {
+		thisZonePackages =
+				(char **)realloc(thisZonePackages,
+				sizeof (char **)*(numThisZonePackages+2));
+	}
+
+	/* handle out of memory error */
+
+	if (thisZonePackages == (char **)NULL) {
+		progerr(ERR_MEMORY, errno);
+		quit(99);
+	}
+
+	/* add this entry to the end of the list */
+
+	thisZonePackages[numThisZonePackages] = strdup(a_pkgInst);
+	if (thisZonePackages[numThisZonePackages] == (char *)NULL) {
+		progerr(ERR_MEMORY, errno);
+		quit(99);
+	}
+
+	numThisZonePackages++;
+
+	/* make sure end of the list is properly terminated */
+
+	thisZonePackages[numThisZonePackages] = (char *)NULL;
+
+	/* exit debugging info */
+
+	echoDebug(DBG_PKGOPS_ADD_TZP, numThisZonePackages,
+			thisZonePackages[numThisZonePackages-1]);
+}
+
+/*
+ * Name:	pkgPackageIsThisZone
+ * Description:	Determine if the specified package is marked to be installed
+ *		in this zone only
+ * Arguments:	a_pkgInst - pointer to string representing package name to check
+ * Returns:	boolean_t
+ *			B_TRUE - the package IS "this zone only"
+ *			B_FALSE - the paackage is NOT "this zone only"
+ */
+
+boolean_t
+pkgPackageIsThisZone(char *a_pkgInst)
+{
+	int		n;
+
+	/* entry assertions */
+
+	assert(a_pkgInst != (char *)NULL);
+	assert(*a_pkgInst != '\0');
+
+	/* if no inherited file systems, there can be no match */
+
+	if (numThisZonePackages == 0) {
+		echoDebug(DBG_PKGOPS_NOT_THISZONE, a_pkgInst);
+		return (B_FALSE);
+	}
+
+	/*
+	 * see if this package is in the "this zone only" list
+	 */
+
+	for (n = 0; n < numThisZonePackages; n++) {
+		if (strcmp(a_pkgInst, thisZonePackages[n]) == 0) {
+			echoDebug(DBG_PKGOPS_IS_THISZONE, a_pkgInst);
+			return (B_TRUE);
+		}
+	}
+
+	/* path is not in "this zone only" list */
+
+	echoDebug(DBG_PKGOPS_IS_NOT_THISZONE, a_pkgInst);
+
+	return (B_FALSE);
+}
+
+/*
+ * Name:	pkgLocateHighestInst
+ * Description:	Locate the highest installed instance of a package
+ * Arguments:	r_path - [RO, *RW] - (char *)
+ *			Pointer to buffer where the full path to the top level
+ *			directory containing the latest instance of the
+ *			specified package is located is placed.
+ *		r_pathLen - [RO, *RO] - (int)
+ *			Integer representing the size of r_path in bytes.
+ *		r_pkgInst - [RO, *RW] - (char *)
+ *			Pointer to buffer where the package instance name of the
+ *			latest instance of the specified package is placed.
+ *		r_pkgInstLen - [RO, *RO] - (int)
+ *			Integer representing the size of r_pkgInst in bytes.
+ *		a_rootPath - [RO, *RO] - (char *)
+ *			Pointer to string representing the root path to look
+ *			for the latest instance of the specified package.
+ *		a_pkgInst - [RO, *RO] - (char *)
+ *			Pointer to string representing the name of the package
+ *			to locate the latest installed instance of.
+ */
+
+void
+pkgLocateHighestInst(char *r_path, int r_pathLen, char *r_pkgInst,
+	int r_pkgInstLen, char *a_rootPath, char *a_pkgInst)
+{
+	char		pkgInstPath[PATH_MAX] = {'\0'};
+	char		pkgWild[PKGSIZ+1] = {'\0'};
+	char		pkgName[PKGSIZ+1] = {'\0'};
+	int		npkgs;
+	struct pkginfo	*pinf = (struct pkginfo *)NULL;
+
+	/* entry assertions */
+
+	assert(r_path != (char *)NULL);
+	assert(r_pathLen > 0);
+	assert(r_pkgInst != (char *)NULL);
+	assert(r_pkgInstLen > 0);
+	assert(a_pkgInst != (char *)NULL);
+	assert(*a_pkgInst != '\0');
+
+	/* normalize root path */
+
+	if ((a_rootPath == (char *)NULL) || (strcmp(a_rootPath, "/") == 0)) {
+		a_rootPath = "";
+	}
+
+	/* construct path to package repository directory (eg. /var/sadm/pkg) */
+
+	(void) snprintf(pkgInstPath, sizeof (pkgInstPath), "%s%s", a_rootPath,
+		PKGLOC);
+
+	/* entry debugging info */
+
+	echoDebug(DBG_PKGOPS_LOCHIGH_ENTRY);
+	echoDebug(DBG_PKGOPS_LOCHIGH_ARGS, pkgInstPath, a_pkgInst);
+
+	/* reset returned path/package instance so both ares empty */
+
+	*r_path = '\0';
+	*r_pkgInst = '\0';
+
+	/* remove any architecture extension */
+
+	pkgstrGetToken_r((char *)NULL, a_pkgInst, 0, ".",
+		pkgName, sizeof (pkgName));
+
+	/* make sure that the package name is valid and can be wild carded */
+
+	if (pkgnmchk(pkgName, NULL, 0) || strchr(pkgName, '.')) {
+		progerr(ERR_PKGOPS_LOCHIGH_BAD_PKGNAME, pkgName);
+		quit(99);
+	}
+
+	/* create wild card specification for this package instance */
+
+	(void) snprintf(pkgWild, sizeof (pkgWild), "%s.*", pkgName);
+
+	echoDebug(DBG_PKGOPS_LOCHIGH_WILDCARD, pkgName, pkgWild);
+
+	/*
+	 * inspect the system to determine if any instances of the
+	 * package being installed already exist on the system
+	 */
+
+	for (npkgs = 0; ; npkgs++) {
+		char	*savePkgdir;
+		int	r;
+
+		/* allocate new pinfo structure for use in the pkginfo call */
+
+		pinf = _pkginfoFactory();
+
+		/*
+		 * lookup the specified package; the first call will cause the
+		 * pkgdir directory to be opened - it will be closed when the
+		 * end of directory is read and pkginfo() returns != 0. You must
+		 * cycle through all instances until pkginfo() returns != 0.
+		 * NOTE: pkginfo() requires the global variable 'pkgdir' be set
+		 * to the package installed directory (<root>/var/sadm/pkg).
+		 */
+
+		savePkgdir = pkgdir;
+		pkgdir = pkgInstPath;
+
+		r = pkginfo(pinf, pkgWild, NULL, NULL);
+
+		pkgdir = savePkgdir;
+
+		echoDebug(DBG_PKGOPS_PKGINFO_RETURNED, pkgName, r);
+
+		/* break out of loop of no package found */
+
+		if (r != 0) {
+			pkginfoFree(&pinf);
+			break;
+		}
+
+		echoDebug(DBG_PKGOPS_LOCHIGH_INSTANCE, npkgs,
+			pinf->pkginst ? pinf->pkginst : "",
+			pinf->name ? pinf->name : "",
+			pinf->arch ? pinf->arch : "",
+			pinf->version ? pinf->version : "",
+			pinf->vendor ? pinf->vendor : "",
+			pinf->basedir ? pinf->basedir : "",
+			pinf->catg ? pinf->catg : "",
+			pinf->status);
+
+		/* save path/instance name for this instance found */
+
+		(void) strlcpy(r_pkgInst, pinf->pkginst, r_pkgInstLen);
+		pkgstrPrintf_r(r_path, r_pathLen, "%s%s/%s", a_rootPath,
+			PKGLOC, pinf->pkginst);
+
+		pkginfoFree(&pinf);
+	}
+
+	echoDebug(DBG_PKGOPS_LOCHIGH_RETURN, npkgs, r_pkgInst, r_path);
+}
+
+/*
+ * Name:	pkgTestInstalled
+ * Description:	determine if package is installed at specified root path
+ * Arguments:	a_packageName - name of package to test
+ * 		a_rootPath - root path of alternative root to test
+ * Returns:	B_TRUE - package is installed
+ *		B_FALSE - package is not installed
+ */
+
+boolean_t
+pkgTestInstalled(char *a_packageName, char *a_rootPath)
+{
+	char	cmd[MAXPATHLEN+1];
+	int	rc;
+
+	/* entry assertions */
+
+	assert(a_packageName != (char *)NULL);
+	assert(*a_packageName != '\0');
+	assert(a_rootPath != (char *)NULL);
+	assert(a_rootPath != '\0');
+
+	/* entry debugging info */
+
+	echoDebug(DBG_PKG_TEST_EXISTENCE, a_packageName, a_rootPath);
+
+	/*
+	 * create pkginfo command to execute:
+	 * /usr/bin/pkginfo -q <packageName>
+	 */
+	(void) snprintf(cmd, sizeof (cmd),
+		"%s -q %s", PKGINFO_CMD, a_packageName);
+
+	/* execute command */
+
+	rc = system(cmd);
+
+	/* return success if pkginfo returns "0" */
+
+	if (rc == 0) {
+		echoDebug(DBG_PKG_INSTALLED, a_packageName, a_rootPath);
+		return (B_TRUE);
+	}
+
+	/* package not installed */
+
+	echoDebug(DBG_PKG_NOT_INSTALLED, a_packageName, a_rootPath);
+
+	return (B_FALSE);
+}
+
+/*
+ * *****************************************************************************
+ * static internal (private) functions
+ * *****************************************************************************
+ */
+
+static void
+_pkginfoInit(struct pkginfo *a_info)
+{
+	/* entry assertions */
+
+	assert(a_info != (struct pkginfo *)NULL);
+
+	/* free previously allocated space */
+
+	if (a_info->pkginst) {
+		free(a_info->pkginst);
+		if (a_info->arch)
+			free(a_info->arch);
+		if (a_info->version)
+			free(a_info->version);
+		if (a_info->basedir)
+			free(a_info->basedir);
+		if (a_info->name)
+			free(a_info->name);
+		if (a_info->vendor)
+			free(a_info->vendor);
+		if (a_info->catg)
+			free(a_info->catg);
+	}
+
+	a_info->pkginst = NULL;
+	a_info->arch = a_info->version = NULL;
+	a_info->basedir = a_info->name = NULL;
+	a_info->vendor = a_info->catg = NULL;
+	a_info->status = PI_UNKNOWN;
+}
+
+static struct pkginfo *
+_pkginfoFactory(void)
+{
+	struct pkginfo *pinf;
+
+	pinf = (struct pkginfo *)calloc(1, sizeof (struct pkginfo));
+	if (pinf == (struct pkginfo *)NULL) {
+		progerr(ERR_MEM);
+		exit(1);
+	}
+
+	_pkginfoInit(pinf);
+	return (pinf);
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/usr/src/cmd/svr4pkg/libinst/procmap.c	Fri Jun 05 10:28:40 2009 -0400
@@ -0,0 +1,403 @@
+/*
+ * 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
+ * or http://www.opensolaris.org/os/licensing.
+ * 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 2006 Sun Microsystems, Inc.  All rights reserved.
+ * Use is subject to license terms.
+ */
+
+/* Copyright (c) 1984, 1986, 1987, 1988, 1989 AT&T */
+/* All Rights Reserved */
+
+
+#include <stdio.h>
+#include <limits.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <errno.h>
+#include <string.h>
+#include <sys/types.h>
+#include <pkgstrct.h>
+#include <locale.h>
+#include <libintl.h>
+#include <pkglib.h>
+#include <install.h>
+#include <libinst.h>
+
+#define	ERR_MEMORY	"memory allocation failure"
+#define	ERR_DUPPATH	"duplicate pathname <%s>"
+
+/* libpkg/gpkgmap */
+extern int	getmapmode(void);
+
+#define	EPTMALLOC	512
+
+static struct cfent **eptlist;
+
+static int	eptnum;
+static int	errflg;
+static int	nparts;
+static int	space = -1;
+
+static void	procinit(void);
+static int	procassign(struct cfent *ept, char **server_local,
+		    char **client_local, char **server_path,
+		    char **client_path, char **map_path, int mapflag,
+		    int nc);
+
+static int	ckdup(struct cfent *ept1, struct cfent *ept2);
+static int	sortentry(int index);
+
+static void
+procinit(void)
+{
+	errflg = nparts = eptnum = 0;
+
+	if (space != -1) {
+		ar_free(space);
+		space = -1;
+	}
+
+	/*
+	 * initialize dynamic memory used to store
+	 * path information which is read in
+	 */
+	(void) pathdup((char *)0);
+}
+
+/*
+ * This function assigns appropriate values based upon the pkgmap entry
+ * in the cfent structure.
+ */
+static int
+procassign(struct cfent *ept, char **server_local, char **client_local,
+    char **server_path, char **client_path, char **map_path, int mapflag,
+    int nc)
+{
+	int	path_duped = 0;
+	int	local_duped = 0;
+	char	source[PATH_MAX+1];
+
+	if (nc >= 0 && ept->ftype != 'i')
+		if ((ept->pkg_class_idx = cl_idx(ept->pkg_class)) == -1)
+			return (1);
+
+	if (ept->volno > nparts)
+		nparts++;
+
+	/*
+	 * Generate local (delivered source) paths for files
+	 * which need them so that the install routine will know
+	 * where to get the file from the package. Note that we
+	 * do not resolve path environment variables here since
+	 * they won't be resolved in the reloc directory.
+	 */
+	if ((mapflag > 1) && strchr("fve", ept->ftype)) {
+		if (ept->ainfo.local == NULL) {
+			source[0] = '~';
+			(void) strcpy(&source[1], ept->path);
+			ept->ainfo.local = pathdup(source);
+			*server_local = ept->ainfo.local;
+			*client_local = ept->ainfo.local;
+
+			local_duped = 1;
+		}
+	}
+
+	/*
+	 * Evaluate the destination path based upon available
+	 * environment, then produce a client-relative and
+	 * server-relative canonized path.
+	 */
+	if (mapflag && (ept->ftype != 'i')) {
+		mappath(getmapmode(), ept->path); /* evaluate variables */
+		canonize(ept->path);	/* Fix path as necessary. */
+
+		(void) eval_path(server_path,
+		    client_path,
+		    map_path,
+		    ept->path);
+		path_duped = 1;	/* eval_path dup's it */
+		ept->path = *server_path;	/* default */
+	}
+
+	/*
+	 * Deal with source for hard and soft links.
+	 */
+	if (strchr("sl", ept->ftype)) {
+		if (mapflag) {
+			mappath(getmapmode(), ept->ainfo.local);
+			if (!RELATIVE(ept->ainfo.local)) {
+				canonize(ept->ainfo.local);
+
+				/* check for hard link */
+				if (ept->ftype == 'l') {
+					(void) eval_path(
+					    server_local,
+					    client_local,
+					    NULL,
+					    ept->ainfo.local);
+					local_duped = 1;
+
+					/* Default to server. */
+					ept->ainfo.local = *server_local;
+				}
+			}
+		}
+	}
+
+	/*
+	 * For the paths (both source and target) that were too mundane to
+	 * have been copied into dup space yet, do that.
+	 */
+	if (!path_duped) {
+		*server_path = pathdup(ept->path);
+		*client_path = *server_path;
+		ept->path = *server_path;
+
+		path_duped = 1;
+	}
+	if (ept->ainfo.local != NULL)
+		if (!local_duped) {
+			*server_local = pathdup(ept->ainfo.local);
+			ept->ainfo.local = *server_local;
+			*client_local = ept->ainfo.local;
+
+		local_duped = 1;
+	}
+
+	return (0);
+}
+
+/*
+ * This function reads the prototype file and returns a pointer to a list of
+ * struct cfent representing the contents of that file.
+ */
+/*ARGSUSED*/
+struct cfent **
+procmap(VFP_T *vfp, int mapflag, char *ir)
+{
+	struct cfent	*ept = (struct cfent *)NULL;
+	struct cfent	map_entry;
+	struct cfent	**ept_ptr;
+	int	i;
+	int	n;
+	int	nc;
+	static char *server_local, *client_local;
+	static char *server_path, *client_path, *map_path;
+
+	procinit();
+
+	space = ar_create(EPTMALLOC, (unsigned)sizeof (struct cfent),
+	    "prototype object");
+	if (space == -1) {
+		progerr(gettext(ERR_MEMORY));
+		return (NULL);
+	}
+
+	nc = cl_getn();
+	for (;;) {
+		/* Clear the buffer. */
+		(void) memset(&map_entry, '\000', sizeof (struct cfent));
+
+		n = gpkgmapvfp(&map_entry, vfp);
+
+		if (n == 0)
+			break; /* no more entries in pkgmap */
+		else if (n < 0) {
+			char	*errstr = getErrstr();
+			progerr(gettext("bad entry read in pkgmap"));
+			logerr(gettext("pathname=%s"),
+				(ept && ept->path && *ept->path) ?
+				ept->path : "Unknown");
+			logerr(gettext("problem=%s"),
+			    (errstr && *errstr) ? errstr : "Unknown");
+			return (NULL);
+		}
+
+		/*
+		 * A valid entry was found in the map, so allocate an
+		 * official record.
+		 */
+		ept_ptr = (struct cfent **)ar_next_avail(space);
+		if (ept_ptr == NULL || *ept_ptr == NULL) {
+			progerr(gettext(ERR_MEMORY));
+			return (NULL);
+		}
+
+		ept = *ept_ptr;
+
+		/* Transfer what we just read in. */
+		(void) memcpy(ept, &map_entry, sizeof (struct cfent));
+
+		if (procassign(ept, &server_local, &client_local,
+		    &server_path, &client_path, &map_path,
+		    mapflag, nc)) {
+			/* It didn't take. */
+			(void) ar_delete(space, eptnum);
+			continue;
+		}
+
+		eptnum++;
+	}
+
+	/* setup a pointer array to point to malloc'd entries space */
+	eptlist = (struct cfent **)ar_get_head(space);
+	if (eptlist == NULL) {
+		progerr(gettext(ERR_MEMORY));
+		return (NULL);
+	}
+
+	(void) sortentry(-1);
+	for (i = 0; i < eptnum; /* void */) {
+		if (!sortentry(i))
+			i++;
+	}
+	return (errflg ? NULL : eptlist);
+}
+
+/*
+ * This function sorts the final list of cfent entries. If index = -1, the
+ * function is initialized. index = 0 doesn't get us anywhere because this
+ * sorts against index-1. Positive natural index values are compared and
+ * sorted into the array appropriately. Yes, it does seem we should use a
+ * quicksort on the whole array or something. The apparent reason for taking
+ * this approach is that there are enough special considerations to be
+ * applied to each package object that inserting them one-by-one doesn't cost
+ * that much.
+ */
+static int
+sortentry(int index)
+{
+	struct cfent *ept, *ept_i;
+	static int last = 0;
+	int	i, n, j;
+	int	upper, lower;
+
+	if (index == 0)
+		return (0);
+	else if (index < 0) {
+		last = 0;
+		return (0);
+	}
+
+	/*
+	 * Based on the index, this is the package object we're going to
+	 * review. It may stay where it is or it may be repositioned in the
+	 * array.
+	 */
+	ept = eptlist[index];
+
+	/* quick comparison optimization for pre-sorted arrays */
+	if (strcmp(ept->path, eptlist[index-1]->path) > 0) {
+		/* do nothing */
+		last = index-1;
+		return (0);
+	}
+
+	lower = 0;		/* lower bound of the unsorted elements */
+	upper = index;		/* upper bound */
+	i = last;
+	do {
+		/*
+		 * NOTE: This does a binary sort on path. There are lots of
+		 * other worthy items in the array, but path is the key into
+		 * the package database.
+		 */
+		ept_i = eptlist[i];
+
+		n = strcmp(ept->path, ept_i->path);
+		if (n == 0) {
+			if (!ckdup(ept, ept_i)) {
+				progerr(gettext(ERR_DUPPATH),
+				    ept->path);
+				errflg++;
+			}
+			/* remove the entry at index */
+			(void) ar_delete(space, index);
+
+			eptnum--;
+			return (1);	/* Use this index again. */
+		} else if (n < 0) {
+			/*
+			 * The path of interest is smaller than the path
+			 * under test. Move down array using the method of
+			 * division
+			 */
+			upper = i;
+			i = lower + (upper-lower)/2;
+		} else {
+			/* Move up array */
+			lower = i+1;
+			i = upper - (upper-lower)/2 - 1;
+		}
+	} while (upper != lower);
+	last = i = upper;
+
+	/* expand to insert at i */
+	for (j = index; j > i; j--)
+		eptlist[j] = eptlist[j-1];
+
+	eptlist[i] = ept;
+
+	return (0);
+}
+
+/*
+ * Check duplicate entries in the package object list. If it's a directory,
+ * this just merges them, if not, it returns a 0 to force further processing.
+ */
+static int
+ckdup(struct cfent *ept1, struct cfent *ept2)
+{
+	/* ept2 will be modified to contain "merged" entries */
+
+	if (!strchr("?dx", ept1->ftype))
+		return (0);
+
+	if (!strchr("?dx", ept2->ftype))
+		return (0);
+
+	if (ept2->ainfo.mode == BADMODE)
+		ept2->ainfo.mode = ept1->ainfo.mode;
+	if ((ept1->ainfo.mode != ept2->ainfo.mode) &&
+	    (ept1->ainfo.mode != BADMODE))
+		return (0);
+
+	if (strcmp(ept2->ainfo.owner, "?") == 0)
+		(void) strcpy(ept2->ainfo.owner, ept1->ainfo.owner);
+	if (strcmp(ept1->ainfo.owner, ept2->ainfo.owner) &&
+	    strcmp(ept1->ainfo.owner, "?"))
+		return (0);
+
+	if (strcmp(ept2->ainfo.group, "?") == 0)
+		(void) strcpy(ept2->ainfo.group, ept1->ainfo.group);
+	if (strcmp(ept1->ainfo.group, ept2->ainfo.group) &&
+	    strcmp(ept1->ainfo.group, "?"))
+		return (0);
+
+	if (ept1->pinfo) {
+		ept2->npkgs = ept1->npkgs;
+		ept2->pinfo = ept1->pinfo;
+	}
+
+	return (1);
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/usr/src/cmd/svr4pkg/libinst/psvr4ck.c	Fri Jun 05 10:28:40 2009 -0400
@@ -0,0 +1,417 @@
+/*
+ * 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
+ * or http://www.opensolaris.org/os/licensing.
+ * 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 2006 Sun Microsystems, Inc.  All rights reserved.
+ * Use is subject to license terms.
+ */
+
+/* Copyright (c) 1984, 1986, 1987, 1988, 1989 AT&T */
+/* All Rights Reserved */
+
+
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <string.h>
+#include <dirent.h>
+#include <ctype.h>
+#include <sys/utsname.h>
+#include <locale.h>
+#include <libintl.h>
+#include <pkglib.h>
+#include <libinst.h>
+#include <libadm.h>
+
+#ifdef	MAILCMD
+#undef  MAILCMD
+#define	MAILCMD		"/bin/mail"
+#endif	/* MAILCMD */
+#define	ERR_MAIL	"unable to send electronic mail notification"
+#define	ERR_OVERWRITE	"unable to determine overwrite list"
+#define	ERR_PIPE	"unable to open pipe to process <%s>"
+#define	ASK_CONT	"Do you want to continue processing this package"
+#define	MSG_CONFLICT	"The following files are currently being used by " \
+			"other packages on the system, and may be " \
+			"overwritten by the installation of this pre-SVR4 " \
+			"package:"
+#define	HLP_CONFLICT	"If you choose to continue installation, it is " \
+			"possible that you will overwrite files which are " \
+			"part of another package that is already installed " \
+			"on the system.  If you want to assure that the " \
+			"files are not overwritten, answer 'n' to stop the " \
+			"installation process."
+#define	MSG_NOTVER	"The media being processed is in an old (pre-SVR4) " \
+			"format and it is not possible to verify that the " \
+			"inserted media belongs to the <%s> package."
+#define	HLP_NOTVER	"If you choose to continue installation, it is " \
+			"possible that you will install the wrong package.  " \
+			"If you are sure the media being installed contains " \
+			"the package you wish to install, answer 'y' to " \
+			"continue the installation process."
+#define	MSG_CONFIRM	"The media being processed is in an old (pre-SVR4) " \
+			"format and appears to be part of the <%s> package."
+#define	HLP_CONFIRM	"The installation of older-style (pre-SVR4) packages " \
+			"is, in general, not as robust as installing " \
+			"standard packages.  Older packages may attempt " \
+			"things during installation which overwrite existing " \
+			"files or otherwise modify the system without your " \
+			"approval.  If you wish to allow installation of " \
+			"identified pre-SVR4 package, answer 'y' to continue " \
+			"the installation process."
+
+static char	*Rlist[] = {
+	"/install/install/Rlist",
+	"/install/install/RLIST",
+	"/install/install/rlist",
+	NULL
+};
+
+static char	ckcmd[] = "/usr/sbin/pkgchk -L -i %s";
+
+/*
+ * Remove the list & both #defines below for on1095 -- JST
+ * Further, please note :
+ * This is NOT a database (Oh, yeah it looks like it, but it isn't). For that
+ * reason these are in alphabetical order. Any additions must maintain this
+ * order and must not increase the list length beyond 120.
+ */
+#define	TREEHEIGHT	7
+#define	TREEFILL	4	/* number of fill entries per side */
+
+#ifdef	ALLOW_EXCEPTION_PKG_LIST
+static char *x_pkg[] =
+{
+	"AAAA1",	/* fill to avoid constraint tests in loop */
+	"AAAA2",
+	"AAAA3",
+	"AAAA4",
+	/* '+' means packages known to be non-compliant */
+	"SPROcpl",	/* + bugID 1133962 */
+	"SPROlklnt",	/* + SW Lock_Lint */
+	"SPROltool",	/* + SW Loop Profiling Tools */
+	"SPROssbd",	/* + SW ssbd component for SC 3.0 */
+	"SPROtha",	/* + Performance Analzyer */
+	"SUNW3270c",	/* + SunLink Client 3270 */
+	"SUNW3270g",	/* SunLink CG3270 8.0 */
+	"SUNW3270t",	/* + SunLink TN3270*Server */
+	"SUNW86nma",	/* SunNet Manager Core Tools for x86 */
+	"SUNW86nmc",	/* SunNet Manager Agents & Libraries for x86 */
+	"SUNW86nmp",	/* SunNet Manager SNMP daemon for x86 */
+	"SUNWabcg",	/* SunLink CG320 8.0 User's Guide */
+	"SUNWbf",	/* + 2.0 FDDI/S Beta */
+	"SUNWbsccu",	/* SunLink BSC Core Util */
+	"SUNWbscdr",	/* SunLink BSC Drivers */
+	"SUNWcosiA",	/* OSI Core Stack Kernel Files 1 */
+	"SUNWcosiC",	/* Stack Mgmnt Utilities 2 */
+	"SUNWcosia",	/* + OSI Core Stack Kernel Files */
+	"SUNWcosib",	/* OSI Core Stack Configuration Files */
+	"SUNWcosic",	/* OSI Core Stack Utilities */
+	"SUNWcosid",	/* OSI Core Stack Development Kit (new pakage) */
+	"SUNWcosij",	/* OSI Core Stack User Space Utilities */
+	"SUNWdniCU",	/* + SunLink DNI Core Utilities 8.0 */
+	"SUNWdniKR",	/* + SunLink DNI Kernel 8.0 */
+	"SUNWdniMA",	/* SunLink DNI Mail Agent 8.0 */
+	"SUNWflex",	/* + FLEX LM DEVEL PKG */
+	"SUNWftama",	/* OSI FTAM Configuration Files */
+	"SUNWftamb",	/* OSI FTAM Executable, Libraries and Man Pages */
+	"SUNWhsis",	/* SunConnect HSI/S */
+	"SUNWjaCL",	/* + Frances Ho confirms for SUNpics */
+	"SUNWjncmt",	/* SunNet Manager Core Tools(Japan) */
+	"SUNWjnmag",	/* SunNet Manager Agents & Libraries (Japan) */
+	"SUNWjnmpd",	/* SunNet Manager SNMP daemon(Japan) */
+	"SUNWlicsw",	/* + FLEXlm */
+	"SUNWlit",	/* STE LIC INSTALL TOOL */
+	"SUNWllc2a",	/* X.25 LLC2 KRNL MOD, INCLDS FL */
+	"SUNWllc2b",	/* X.25 USR PROG, MAN PAGES */
+	"SUNWmd",	/* + Suhas Patil request 1994-07-12 */
+	"SUNWmhs1a",	/* MHS Message Transfer Agent Configuration Files */
+	"SUNWmhs1b",	/* MHS Message Transfer Agent Executable and Man Pgs */
+	"SUNWomgta",	/* OSI Mgmnt Configuration Files */
+	"SUNWomgtb",	/* OSI Mgmnt Configuration Files */
+	"SUNWomgtc",	/* OSI Mgmnt SunNet Mgr Proxy Agent Executable Files */
+	"SUNWomgtd",	/* OSI Mgmnt SunNet Mgr Proxy Agent Config Files */
+	"SUNWp2pnm",	/* SunLink SNA Peer-to-Peer Network Management */
+	"SUNWprsto",	/* + Varun Mehta request 1994-07-11 */
+	"SUNWrup2p",	/* Sunlink SNA Peer-to-Peer Run Time Environment */
+	"SUNWs3270",	/* + SunLink SNA3270/RJE */
+	"SUNWscmmd",	/* SunLink Comm Daemon */
+	"SUNWsdlc",	/* SunLink IBM SDLC */
+	"SUNWsm-ml",	/* ShowMe Motif Libs */
+	"SUNWsm-ol",	/* ShowMe Online help */
+	"SUNWsmCmg",
+	"SUNWsmap",	/* SunLink Mapper */
+	"SUNWsmaud",	/* ShowMe Audio */
+	"SUNWsmsha",	/* ShowMe SharedApp */
+	"SUNWsmvid",	/* ShowMe Video */
+	"SUNWsmwtb",	/* ShowMe Whiteboard */
+	"SUNWsnmag",	/* + Steve Wong request 1994-02-15 */
+	"SUNWsnmct",	/* + Steve Wong request 1994-02-15 */
+	"SUNWsnmja",	/* SunNet Manager 2.2 Japanese feature */
+	"SUNWsnmpd",	/* SunNet Manager SNMP daemon */
+	"SUNWsnp2p",	/* + SunLink SNA P-to-P */
+	"SUNWspii",	/* 1.0 SPARCprinterII */
+	"SUNWsrjec",	/* + SunLink Client SNA RJE */
+	"SUNWsteCL",	/* + Frances Ho confirms for SUNPics */
+	"SUNWsteNP",	/* 2.5 NeWSprint */
+	"SUNWte320",	/* + TE320 8.0 */
+	"SUNWtris",	/* SunConnect TRI/S */
+	"SUNWvtcfg",	/* OSI Virtual Terminal Configuration Files */
+	"SUNWvtexe",	/* OSI Virtual Terminal User Program and Man Pages */
+	"SUNWx25a",	/* + X.25 KRNL MOD, INCLDS FLS */
+	"SUNWx25b",	/* + X.25 USR PROG AND LIB */
+	"zzzz1",	/* fill to avoid constraint tests in loop */
+	"zzzz2",
+	"zzzz3",
+	"zzzz4"
+};
+#endif
+
+/*
+ * Structure to hold the list of pkg names that are known to not behave
+ * properly when sym link destinations are not followed.
+ */
+
+#ifdef	ALLOW_EXCEPTION_PKG_LIST
+static char *x_pkg_link[] =
+{
+	"AAAA1",	/* fill to avoid constraint tests in loop */
+	"AAAA2",
+	"AAAA3",
+	"AAAA4",
+	/* '+' means packages known to be non-compliant */
+	"SUNWixfta",
+	"SUNWixsna",
+	"zzzz1",	/* fill to avoid constraint tests in loop */
+	"zzzz2",
+	"zzzz3",
+	"zzzz4"
+};
+#endif
+
+/*
+ * This function determines if the package being added is a known old-style
+ * package which requires user interaction during procedure scripts. It is
+ * to be removed for on1095. -- JST
+ * It also is used for the determining if a pkg is known to have symlinks
+ * that need to be processed the old way.
+ */
+
+#ifdef	ALLOW_EXCEPTION_PKG_LIST
+int
+exception_pkg(char *pkginst, int pkg_list)
+{
+	int	retvalue = 0;
+	int	list_sz;
+	int	list_cntr;	/* starting point for binary search */
+	register int	pos;		/* current position */
+	register int	level;		/* current height in the tree */
+	register int 	incr;		/* increment for step */
+	int	result;		/* result of strcmp */
+	register char **x_ptr = x_pkg;
+	register char **x_ptr_link = x_pkg_link;
+	char	*pkgend;
+	char	*pkgname = strdup(pkginst);
+
+	/*
+	 * NOTE : If more structures need to be defined the following if
+	 * statement needs to be revised to handle multiple flags
+	 */
+
+	if (pkg_list)
+		list_sz = (sizeof (x_pkg_link) / sizeof (char *));
+	else
+		list_sz = (sizeof (x_pkg) / sizeof (char *));
+
+	/*
+	 * NOTE : shifts are used instead of integer division to save
+	 * time. Numerous other checks are omitted also. This tree
+	 * contains double nodes but is entirely connected and closed.
+	 */
+
+	list_cntr = list_sz >> 1;
+	incr = list_cntr - TREEFILL;
+
+	pkgend = strchr(pkgname, '.');
+
+	if (pkgend)
+		*pkgend = '\0';	/* terminate the instance to a name */
+
+	for (level = TREEHEIGHT, 	/* start at the top level */
+	    pos = list_cntr;		/*   ... in the middle */
+	    level;		/* for as long as we're in the tree */
+	    level--, pos += (result > 0) ? incr : -incr) {
+
+		if (pkg_list)
+			result = strcmp(pkgname, *(x_ptr_link + pos));
+		else
+			result = strcmp(pkgname, *(x_ptr + pos));
+
+		if (result == 0) {
+			retvalue = 1;
+			break;
+		}
+
+		incr = (incr & 0x0001) | (incr >> 1);	/* halve it & rnd up */
+	}
+
+	free(pkgname);
+
+	return (retvalue);
+}
+
+#endif
+
+void
+psvr4pkg(char	**ppkg)
+{
+	struct dirent *drp;
+	DIR	*dirfp;
+	char	*pt;
+	int	n;
+	char	ans[MAX_INPUT], path[PATH_MAX];
+
+	if (*ppkg) {
+		(void) snprintf(path, sizeof (path),
+				"/install/new/usr/options/%s.name",
+				*ppkg);
+		if (access(path, 0)) {
+			ptext(stderr, gettext(MSG_NOTVER), *ppkg);
+			if (n = ckyorn(ans, NULL, NULL, gettext(HLP_NOTVER),
+			    gettext(ASK_CONT)))
+				quit(n);
+			if (strchr("yY", *ans) == NULL)
+				quit(3);
+		}
+		return;
+	}
+
+	if (dirfp = opendir("/install/new/usr/options")) {
+		while (drp = readdir(dirfp)) {
+			if (drp->d_name[0] == '.')
+				continue;
+			if (pt = strchr(drp->d_name, '.')) {
+				if (strcmp(pt, ".name") == 0) {
+					*pt = '\0';
+					*ppkg = qstrdup(drp->d_name);
+					break;
+				}
+			}
+		}
+		(void) closedir(dirfp);
+	}
+
+	if (*ppkg) {
+		ptext(stderr, gettext(MSG_CONFIRM), *ppkg);
+		if (n = ckyorn(ans, NULL, NULL, gettext(HLP_CONFIRM),
+		    gettext(ASK_CONT)))
+			quit(n);
+	} else {
+		ptext(stderr, gettext(MSG_NOTVER), *ppkg);
+		if (n = ckyorn(ans, NULL, NULL, gettext(HLP_NOTVER),
+		    gettext(ASK_CONT)))
+			quit(n);
+	}
+	if (strchr("yY", *ans) == NULL)
+		quit(3);
+}
+
+void
+psvr4cnflct(void)
+{
+	FILE	*pp;
+	int	n, found;
+	char	*pt,
+		ans[MAX_INPUT],
+		cmd[PATH_MAX+sizeof (ckcmd)],
+		path[PATH_MAX];
+
+	for (n = 0; Rlist[n] != NULL; n++) {
+		if (access(Rlist[n], 0) == 0)
+			break;
+	}
+	if (Rlist[n] == NULL)
+		return; /* Rlist file not found on device */
+
+	(void) sprintf(cmd, ckcmd, Rlist[n]);
+	echo(gettext("## Checking for conflicts with installed packages"));
+	echo(gettext("   (using %s provided by pre-SVR4 package)"), Rlist[n]);
+	if ((pp = popen(cmd, "r")) == NULL) {
+		progerr(gettext(ERR_PIPE), cmd);
+		progerr(gettext(ERR_OVERWRITE));
+		quit(99);
+	}
+
+	found = 0;
+	while (fgets(path, PATH_MAX, pp)) {
+		if (!found++)
+			ptext(stderr, gettext(MSG_CONFLICT));
+		if (pt = strpbrk(path, " \t\n"))
+			*pt = '\0';
+		echo("\t%s", path);
+	}
+	if (pclose(pp)) {
+		progerr(gettext(ERR_OVERWRITE));
+		quit(99);
+	}
+
+	if (found) {
+		if (n = ckyorn(ans, NULL, NULL, gettext(HLP_CONFLICT),
+		    gettext(ASK_CONT)))
+			quit(n);
+		if (strchr("yY", *ans) == NULL)
+			quit(3);
+	}
+}
+
+void
+psvr4mail(char *list, char *msg, int retcode, char *pkg)
+{
+	struct utsname utsbuf;
+	FILE	*pp;
+	char	cmd[BUFSIZ];
+
+	if (list == NULL)
+		return;
+
+	while (isspace(*list))
+		list++;
+	if (*list == '\0')
+		return;
+
+	/* send e-mail notifications */
+	(void) snprintf(cmd, sizeof (cmd), "%s %s", MAILCMD, list);
+	if ((pp = popen(cmd, "w")) == NULL) {
+		progerr(gettext(ERR_PIPE), MAILCMD);
+		progerr(gettext(ERR_MAIL));
+		quit(99);
+	}
+
+	(void) strcpy(utsbuf.nodename, gettext("(unknown)"));
+	(void) uname(&utsbuf);
+	ptext(pp, msg, pkg, utsbuf.nodename, retcode);
+
+	if (pclose(pp)) {
+		progerr(gettext(ERR_MAIL));
+		quit(99);
+	}
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/usr/src/cmd/svr4pkg/libinst/ptext.c	Fri Jun 05 10:28:40 2009 -0400
@@ -0,0 +1,51 @@
+/*
+ * 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
+ * or http://www.opensolaris.org/os/licensing.
+ * 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 1993 Sun Microsystems, Inc.  All rights reserved.
+ * Use is subject to license terms.
+ */
+
+/* Copyright (c) 1984, 1986, 1987, 1988, 1989 AT&T */
+/* All Rights Reserved */
+
+
+
+#include <stdio.h>
+#include <stdarg.h>
+#include "libadm.h"
+
+/*VARARGS*/
+void
+ptext(FILE *fp, char *fmt, ...)
+{
+	va_list ap;
+	char	buffer[2048];
+
+	va_start(ap, fmt);
+
+	(void) vsprintf(buffer, fmt, ap);
+
+	va_end(ap);
+
+	(void) puttext(fp, buffer, 0, 70);
+	(void) putc('\n', fp);
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/usr/src/cmd/svr4pkg/libinst/putparam.c	Fri Jun 05 10:28:40 2009 -0400
@@ -0,0 +1,311 @@
+/*
+ * 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
+ * or http://www.opensolaris.org/os/licensing.
+ * 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 2009 Sun Microsystems, Inc.  All rights reserved.
+ * Use is subject to license terms.
+ */
+
+/* Copyright (c) 1984, 1986, 1987, 1988, 1989 AT&T */
+/* All Rights Reserved */
+
+
+#include <stdio.h>
+#include <limits.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <string.h>
+#include <fcntl.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <signal.h>
+#include <errno.h>
+#include <assert.h>
+#include <pkgdev.h>
+#include <pkginfo.h>
+#include <pkglocs.h>
+#include <locale.h>
+#include <libintl.h>
+#include <instzones_api.h>
+#include <pkglib.h>
+#include <install.h>
+#include <libinst.h>
+#include <libadm.h>
+#include <messages.h>
+
+static char *localeNames[] = {
+	"LC_CTYPE",
+	"LC_NUMERIC",
+	"LC_TIME",
+	"LC_COLLATE",
+	"LC_MESSAGES",
+	"LC_MONETARY",
+	"LC_ALL",
+	"LANG",
+	"TZ",
+	NULL
+};
+
+#define	NUM_LOCALE_TYPES	100
+
+static char	*envPtr[NUM_LOCALE_TYPES];
+
+/*
+ * extern declarations
+ */
+
+extern char	**environ;
+
+/*
+ * this is the initial and incremental allocation used to
+ * populate the environment "environ"
+ */
+
+#define	MALSIZ	64
+
+void
+putparam(char *param, char *value)
+{
+	char	*pt;
+	int	ptlen;
+	int	i, n;
+
+	/*
+	 * If the environment is NULL, allocate space for the
+	 * character pointers.
+	 */
+	if (environ == NULL) {
+		environ = (char **)calloc(MALSIZ, sizeof (char *));
+		if (environ == NULL) {
+			progerr(gettext(ERR_MEMORY), errno);
+			quit(99);
+		}
+	}
+
+	/*
+	 * If this parameter is already in place and it has a different
+	 * value, clear the old value by freeing the memory previously
+	 * allocated. Otherwise, we leave well-enough alone.
+	 */
+	n = strlen(param);
+	for (i = 0; environ[i]; i++) {
+		if (strncmp(environ[i], param, n) == 0 &&
+		    (environ[i][n] == '=')) {
+			if (strcmp((environ[i]) + n + 1, value) == 0)
+				return;
+			else {
+				free(environ[i]);
+				break;
+			}
+		}
+	}
+
+	/* Allocate space for the new environment entry. */
+	ptlen = (strlen(param)+strlen(value)+2)*(sizeof (char));
+	pt = (char *)calloc(strlen(param)+strlen(value)+2, sizeof (char));
+	if (pt == NULL) {
+		progerr(gettext(ERR_MEMORY), errno);
+		quit(99);
+	}
+
+	/*
+	 * Put the statement into the allocated space and point the
+	 * environment entry at it.
+	 */
+	(void) snprintf(pt, ptlen, "%s=%s", param, value);
+	if (environ[i]) {
+		environ[i] = pt;
+		return;
+	}
+
+	/*
+	 * With this parameter in place, if we're at the end of the
+	 * allocated environment then allocate more space.
+	 */
+	environ[i++] = pt;
+	if ((i % MALSIZ) == 0) {
+		environ = (char **)realloc((void *)environ,
+			(i+MALSIZ)*sizeof (char *));
+		if (environ == NULL) {
+			progerr(gettext(ERR_MEMORY), errno);
+			quit(1);
+		}
+	}
+
+	/* Terminate the environment properly. */
+	environ[i] = (char *)NULL;
+}
+
+/* bugid 4279039 */
+void
+getuserlocale(void)
+{
+	int i;
+
+	for (i = 0; (localeNames[i] != NULL) && (i < NUM_LOCALE_TYPES); i++) {
+		envPtr[i] = getenv(localeNames[i]);
+		if (envPtr[i]) {
+			putparam(localeNames[i], envPtr[i]);
+		}
+	}
+}
+
+/* bugid 4279039 */
+void
+putuserlocale(void)
+{
+	int i;
+
+	for (i = 0; (localeNames[i] != NULL) && (i < NUM_LOCALE_TYPES); i++) {
+		if (envPtr[i]) {
+			putparam(localeNames[i], envPtr[i]);
+		}
+	}
+}
+
+/*
+ * Name:	putConditionInfo
+ * Description:	put parent "condition" information to environment
+ * Arguments:	a_parentZoneName - name of the parent zone
+ *			== NULL - no name
+ *		a_parentZoneType - parent zone "type"
+ *			== NULL - no type
+ * Returns:	void
+ */
+
+void
+putConditionInfo(char *a_parentZoneName, char *a_parentZoneType)
+{
+	char	**pp;
+	char	*p;
+	char	*pa;
+	SML_TAG	*tag = SML_TAG__NULL;
+	SML_TAG	*ntag;
+
+	/* entry debugging info */
+
+	echoDebug(DBG_PUTPARAM_PUTCONDINFO_ENTRY);
+
+	/*
+	 * create tag to hold condition information:
+	 * <environmentConditionInformation>
+	 * <parentZone zoneName=<?> zoneType=<?>/>
+	 * <currentZone zoneName=<?> zoneType=<?>/>
+	 * <inheritedFileSystem fileSystemName=<?>/>
+	 * </environmentConditionInformation>
+	 */
+
+	tag = smlNewTag(TAG_COND_TOPLEVEL);
+
+	/*
+	 * information about pkgadd or pkgrm environment
+	 * <parentZone zoneName=<?> zoneType=<?>/>
+	 */
+
+	/* allocate tag for parent info */
+
+	ntag = smlNewTag(TAG_COND_PARENT_ZONE);
+
+	/* parent zone name */
+
+	smlSetParam(ntag, TAG_COND_ZONE_NAME,
+		a_parentZoneName ? a_parentZoneName : "");
+
+	/* parent zone info */
+
+	smlSetParam(ntag, TAG_COND_ZONE_TYPE,
+		a_parentZoneType ? a_parentZoneType : "");
+
+	/* add to top level tag */
+
+	(void) smlAddTag(&tag, -1, ntag);
+	free(ntag);
+
+	/*
+	 * information about pkginstall or pkgremove environment
+	 * <currentZone zoneName=<?> zoneType=<?>/>
+	 */
+
+	/* allocate tag for parent info */
+
+	ntag = smlNewTag(TAG_COND_CURRENT_ZONE);
+
+	/* current zone name */
+
+	p = z_get_zonename();
+	if ((p != NULL) && (*p != '\0')) {
+		smlSetParam(ntag, TAG_COND_ZONE_NAME, p);
+		free(p);
+	}
+
+	/* current zone type */
+
+	smlSetParam(ntag, TAG_COND_ZONE_TYPE,
+		z_running_in_global_zone() == B_TRUE ?
+			TAG_VALUE_GLOBAL_ZONE : TAG_VALUE_NONGLOBAL_ZONE);
+
+	/* add to top level tag */
+
+	(void) smlAddTag(&tag, -1, ntag);
+	free(ntag);
+
+	/*
+	 * describe any inherited file systems:
+	 * <inheritedFileSystem fileSystemName=<?>/>
+	 */
+
+	pp = z_get_inherited_file_systems();
+	if (pp != (char **)NULL) {
+		int n;
+		for (n = 0; pp[n] != (char *)NULL; n++) {
+			/* allocate tag for inherited file system info */
+
+			ntag = smlNewTag(TAG_COND_INHERITED_FS);
+
+			/* inherited file system */
+
+			smlSetParam(ntag, TAG_COND_FS_NAME, pp[n]);
+
+			/* add to top level tag */
+
+			(void) smlAddTag(&tag, -1, ntag);
+			free(ntag);
+		}
+	}
+
+	/*
+	 * done filling in tag - convert to string and place in environment
+	 */
+
+	p = smlConvertTagToString(tag);
+
+	/* convert all new-line characters to space */
+
+	for (pa = p; *pa != '\0'; pa++) {
+		if (*pa == '\n') {
+			*pa = ' ';
+		}
+	}
+
+	echoDebug(DBG_PUTPARAM_PUTCONDINFO_EXIT, p);
+
+	putparam(PKGCOND_GLOBAL_VARIABLE, p);
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/usr/src/cmd/svr4pkg/libinst/qreason.c	Fri Jun 05 10:28:40 2009 -0400
@@ -0,0 +1,428 @@
+/*
+ * 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
+ * or http://www.opensolaris.org/os/licensing.
+ * 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 2004 Sun Microsystems, Inc.  All rights reserved.
+ * Use is subject to license terms.
+ */
+
+/* Copyright (c) 1984, 1986, 1987, 1988, 1989 AT&T */
+/* All Rights Reserved */
+
+
+/*
+ * System includes
+ */
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <locale.h>
+#include <libintl.h>
+
+/*
+ * local pkg command library includes
+ */
+
+#include "libinst.h"
+#include "messages.h"
+
+/*
+ * forward declarations
+ */
+
+static char	*qreasonNoZonename(int caller, int retcode, int started);
+static char	*qreasonWithZonename(int caller, int retcode, int started);
+
+/*
+ * *****************************************************************************
+ * global external (public) functions
+ * *****************************************************************************
+ */
+
+/*
+ * Name:	qreason
+ * Description:	return message describing specified "quit reason"
+ * Arguments:	caller - integer describing the "caller:
+ *			Caller identities:
+ *			0 - pkginstall - pkgask
+ *			1 - pkginstall - pkgadd
+ *			2 - pkginstall - mailmsg
+ *			3 - pkgremove - quitmsg
+ *			4 - pkgremove - mailmsg
+ *		retcode - integer return code describing "reason"
+ *		includeZonename - integer describing zone for reason
+ *			== 0 - do not include a zone name in the message
+ *			!= 0 - include a zone name in the message
+ * Returns:	char *
+ * NOTE:	all messages are returned from static space that does not need
+ *		to be free()ed when no longer needed
+ * NOTE:	imbedded "%s"s in returned messages are consistent with the
+ *		caller and zone name inclusion:
+ *			0 - no %s's
+ *			1 - one %s - package name
+ *			2 - three %s - package name, rootpath, package instance
+ *			3 - one %s - package name
+ *			4 - two %s - package name, rootpath
+ *		If "includeZonename" is true, an extra "%s" is added at the
+ *		end of each message for the zone name to be included.
+ */
+
+char *
+qreason(int caller, int retcode, int started, int includeZonename)
+{
+	if (includeZonename == 0) {
+		return (qreasonNoZonename(caller, retcode, started));
+	}
+
+	return (qreasonWithZonename(caller, retcode, started));
+}
+
+/*
+ * *****************************************************************************
+ * static internal (private) functions
+ * *****************************************************************************
+ */
+
+static char *
+qreasonNoZonename(int caller, int retcode, int started)
+{
+	switch (retcode) {
+	    case  0:
+	    case 10:
+	    case 20:
+		switch (caller) {
+		    case 0: /* pkginstall - pkgask */
+			return (MSG_RE_SUC);
+		    case 1: /* pkginstall - pkgadd */
+			return (MSG_IN_SUC0);
+		    case 2: /* pkginstall - mailmsg */
+			return (MSG_IN_SUC1);
+		    case 3: /* pkgremove - quitmsg */
+			return (MSG_RM_SUC0);
+		    case 4: /* pkgremove - mailmsg */
+			return (MSG_RM_SUC1);
+		    default:
+			return (MSG_UNKREQ);
+		}
+
+	    case  1:
+	    case 11:
+	    case 21:
+		switch (caller) {
+		    case 0: /* pkginstall - pkgask */
+			return (MSG_RE_FAIL);
+		    case 1: /* pkginstall - pkgadd */
+			return (MSG_IN_FAIL0);
+		    case 2: /* pkginstall - mailmsg */
+			return (MSG_IN_FAIL1);
+		    case 3: /* pkgremove - quitmsg */
+			return (MSG_RM_FAIL0);
+		    case 4: /* pkgremove - mailmsg */
+			return (MSG_RM_FAIL1);
+		    default:
+			return (MSG_UNKREQ);
+		}
+
+	    case  2:
+	    case 12:
+	    case 22:
+		switch (caller) {
+		    case 0: /* pkginstall - pkgask */
+			return (MSG_RE_PARFAIL);
+		    case 1: /* pkginstall - pkgadd */
+			return (MSG_IN_PARFAIL0);
+		    case 2: /* pkginstall - mailmsg */
+			return (MSG_IN_PARFAIL1);
+		    case 3: /* pkgremove - quitmsg */
+			return (MSG_RM_PARFAIL0);
+		    case 4: /* pkgremove - mailmsg */
+			return (MSG_RM_PARFAIL1);
+		    default:
+			return (MSG_UNKREQ);
+		}
+
+	    case  3:
+	    case 13:
+	    case 23:
+		switch (caller) {
+		    case 0: /* pkginstall - pkgask */
+			return (MSG_RE_USER);
+		    case 1: /* pkginstall - pkgadd */
+			return (MSG_IN_USER0);
+		    case 2: /* pkginstall - mailmsg */
+			return (MSG_IN_USER1);
+		    case 3: /* pkgremove - quitmsg */
+			return (MSG_RM_USER0);
+		    case 4: /* pkgremove - mailmsg */
+			return (MSG_RM_USER1);
+		    default:
+			return (MSG_UNKREQ);
+		}
+
+	    case  4:
+	    case 14:
+	    case 24:
+		switch (caller) {
+		    case 0: /* pkginstall - pkgask */
+			return (MSG_RE_SUA);
+		    case 1: /* pkginstall - pkgadd */
+			return (MSG_IN_SUA0);
+		    case 2: /* pkginstall - mailmsg */
+			return (MSG_IN_SUA1);
+		    case 3: /* pkgremove - quitmsg */
+			return (MSG_RM_SUA0);
+		    case 4: /* pkgremove - mailmsg */
+			return (MSG_RM_SUA1);
+		    default:
+			return (MSG_UNKREQ);
+		}
+
+	    case  5:
+	    case 15:
+	    case 25:
+		switch (caller) {
+		    case 0: /* pkginstall - pkgask */
+			return (MSG_RE_SUI);
+		    case 1: /* pkginstall - pkgadd */
+			return (MSG_IN_SUI0);
+		    case 2: /* pkginstall - mailmsg */
+			return (MSG_IN_SUI1);
+		    case 3: /* pkgremove - quitmsg */
+			return (MSG_RM_SUI0);
+		    case 4: /* pkgremove - mailmsg */
+			return (MSG_RM_SUI1);
+		    default:
+			return (MSG_UNKREQ);
+		}
+
+	    case 99:
+		if (started) {
+			switch (caller) {
+			    case 0: /* pkginstall - pkgask */
+				return (MSG_RE_IEPI);
+			    case 1: /* pkginstall - pkgadd */
+				return (MSG_IN_IEPI0);
+			    case 2: /* pkginstall - mailmsg */
+				return (MSG_IN_IEPI1);
+			    case 3: /* pkgremove - quitmsg */
+				return (MSG_RM_IEPI0);
+			    case 4: /* pkgremove - mailmsg */
+				return (MSG_RM_IEPI1);
+			    default:
+				return (MSG_UNKREQ);
+			}
+		}
+
+		switch (caller) {
+		    case 0: /* pkginstall - pkgask */
+			return (MSG_RE_IE);
+		    case 1: /* pkginstall - pkgadd */
+			return (MSG_IN_IE0);
+		    case 2: /* pkginstall - mailmsg */
+			return (MSG_IN_IE1);
+		    case 3: /* pkgremove - quitmsg */
+			return (MSG_RM_IE0);
+		    case 4: /* pkgremove - mailmsg */
+			return (MSG_RM_IE1);
+		    default:
+			return (MSG_UNKREQ);
+		}
+
+	    default:
+		switch (caller) {
+		    case 0: /* pkginstall - pkgask */
+			return (MSG_RE_UNK);
+		    case 1: /* pkginstall - pkgadd */
+			return (MSG_IN_UNK0);
+		    case 2: /* pkginstall - mailmsg */
+			return (MSG_IN_UNK1);
+		    case 3: /* pkgremove - quitmsg */
+			return (MSG_RM_UNK0);
+		    case 4: /* pkgremove - mailmsg */
+			return (MSG_RM_UNK1);
+		    default:
+			return (MSG_UNKREQ);
+		}
+	}
+}
+
+static char *
+qreasonWithZonename(int caller, int retcode, int started)
+{
+	switch (retcode) {
+	    case  0:
+	    case 10:
+	    case 20:
+		switch (caller) {
+		    case 0: /* pkginstall - pkgask */
+			return (MSG_RE_SUC_ZONE);
+		    case 1: /* pkginstall - pkgadd */
+			return (MSG_IN_SUC0_ZONE);
+		    case 2: /* pkginstall - mailmsg */
+			return (MSG_IN_SUC1_ZONE);
+		    case 3: /* pkgremove - quitmsg */
+			return (MSG_RM_SUC0_ZONE);
+		    case 4: /* pkgremove - mailmsg */
+			return (MSG_RM_SUC1_ZONE);
+		    default:
+			return (MSG_UNKREQ_ZONE);
+		}
+
+	    case  1:
+	    case 11:
+	    case 21:
+		switch (caller) {
+		    case 0: /* pkginstall - pkgask */
+			return (MSG_RE_FAIL_ZONE);
+		    case 1: /* pkginstall - pkgadd */
+			return (MSG_IN_FAIL0_ZONE);
+		    case 2: /* pkginstall - mailmsg */
+			return (MSG_IN_FAIL1_ZONE);
+		    case 3: /* pkgremove - quitmsg */
+			return (MSG_RM_FAIL0_ZONE);
+		    case 4: /* pkgremove - mailmsg */
+			return (MSG_RM_FAIL1_ZONE);
+		    default:
+			return (MSG_UNKREQ_ZONE);
+		}
+
+	    case  2:
+	    case 12:
+	    case 22:
+		switch (caller) {
+		    case 0: /* pkginstall - pkgask */
+			return (MSG_RE_PARFAIL_ZONE);
+		    case 1: /* pkginstall - pkgadd */
+			return (MSG_IN_PARFAIL0_ZONE);
+		    case 2: /* pkginstall - mailmsg */
+			return (MSG_IN_PARFAIL1_ZONE);
+		    case 3: /* pkgremove - quitmsg */
+			return (MSG_RM_PARFAIL0_ZONE);
+		    case 4: /* pkgremove - mailmsg */
+			return (MSG_RM_PARFAIL1_ZONE);
+		    default:
+			return (MSG_UNKREQ_ZONE);
+		}
+
+	    case  3:
+	    case 13:
+	    case 23:
+		switch (caller) {
+		    case 0: /* pkginstall - pkgask */
+			return (MSG_RE_USER_ZONE);
+		    case 1: /* pkginstall - pkgadd */
+			return (MSG_IN_USER0_ZONE);
+		    case 2: /* pkginstall - mailmsg */
+			return (MSG_IN_USER1_ZONE);
+		    case 3: /* pkgremove - quitmsg */
+			return (MSG_RM_USER0_ZONE);
+		    case 4: /* pkgremove - mailmsg */
+			return (MSG_RM_USER1_ZONE);
+		    default:
+			return (MSG_UNKREQ_ZONE);
+		}
+
+	    case  4:
+	    case 14:
+	    case 24:
+		switch (caller) {
+		    case 0: /* pkginstall - pkgask */
+			return (MSG_RE_SUA_ZONE);
+		    case 1: /* pkginstall - pkgadd */
+			return (MSG_IN_SUA0_ZONE);
+		    case 2: /* pkginstall - mailmsg */
+			return (MSG_IN_SUA1_ZONE);
+		    case 3: /* pkgremove - quitmsg */
+			return (MSG_RM_SUA0_ZONE);
+		    case 4: /* pkgremove - mailmsg */
+			return (MSG_RM_SUA1_ZONE);
+		    default:
+			return (MSG_UNKREQ_ZONE);
+		}
+
+	    case  5:
+	    case 15:
+	    case 25:
+		switch (caller) {
+		    case 0: /* pkginstall - pkgask */
+			return (MSG_RE_SUI_ZONE);
+		    case 1: /* pkginstall - pkgadd */
+			return (MSG_IN_SUI0_ZONE);
+		    case 2: /* pkginstall - mailmsg */
+			return (MSG_IN_SUI1_ZONE);
+		    case 3: /* pkgremove - quitmsg */
+			return (MSG_RM_SUI0_ZONE);
+		    case 4: /* pkgremove - mailmsg */
+			return (MSG_RM_SUI1_ZONE);
+		    default:
+			return (MSG_UNKREQ_ZONE);
+		}
+
+	    case 99:
+		if (started) {
+			switch (caller) {
+			    case 0: /* pkginstall - pkgask */
+				return (MSG_RE_IEPI_ZONE);
+			    case 1: /* pkginstall - pkgadd */
+				return (MSG_IN_IEPI0_ZONE);
+			    case 2: /* pkginstall - mailmsg */
+				return (MSG_IN_IEPI1_ZONE);
+			    case 3: /* pkgremove - quitmsg */
+				return (MSG_RM_IEPI0_ZONE);
+			    case 4: /* pkgremove - mailmsg */
+				return (MSG_RM_IEPI1_ZONE);
+			    default:
+				return (MSG_UNKREQ_ZONE);
+			}
+		}
+
+		switch (caller) {
+		    case 0: /* pkginstall - pkgask */
+			return (MSG_RE_IE_ZONE);
+		    case 1: /* pkginstall - pkgadd */
+			return (MSG_IN_IE0_ZONE);
+		    case 2: /* pkginstall - mailmsg */
+			return (MSG_IN_IE1_ZONE);
+		    case 3: /* pkgremove - quitmsg */
+			return (MSG_RM_IE0_ZONE);
+		    case 4: /* pkgremove - mailmsg */
+			return (MSG_RM_IE1_ZONE);
+		    default:
+			return (MSG_UNKREQ_ZONE);
+		}
+
+	    default:
+		switch (caller) {
+		    case 0: /* pkginstall - pkgask */
+			return (MSG_RE_UNK_ZONE);
+		    case 1: /* pkginstall - pkgadd */
+			return (MSG_IN_UNK0_ZONE);
+		    case 2: /* pkginstall - mailmsg */
+			return (MSG_IN_UNK1_ZONE);
+		    case 3: /* pkgremove - quitmsg */
+			return (MSG_RM_UNK0_ZONE);
+		    case 4: /* pkgremove - mailmsg */
+			return (MSG_RM_UNK1_ZONE);
+		    default:
+			return (MSG_UNKREQ_ZONE);
+		}
+	}
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/usr/src/cmd/svr4pkg/libinst/qstrdup.c	Fri Jun 05 10:28:40 2009 -0400
@@ -0,0 +1,57 @@
+/*
+ * 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
+ * or http://www.opensolaris.org/os/licensing.
+ * 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 2006 Sun Microsystems, Inc.  All rights reserved.
+ * Use is subject to license terms.
+ */
+
+/* Copyright (c) 1984, 1986, 1987, 1988, 1989 AT&T */
+/* All Rights Reserved */
+
+
+
+#include <string.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <errno.h>
+#include <locale.h>
+#include <libintl.h>
+#include <pkglib.h>
+#include <libinst.h>
+
+#define	ERR_MEMORY	"memory allocation failure, errno=%d"
+
+char *
+qstrdup(char *s)
+{
+	register char *pt = NULL;
+
+	if (s && *s) {
+		pt = calloc((strlen(s) + 1), sizeof (char));
+		if (pt == NULL) {
+			progerr(gettext(ERR_MEMORY), errno);
+			quit(99);
+		}
+		(void) strcpy(pt, s);
+	}
+	return (pt);
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/usr/src/cmd/svr4pkg/libinst/scriptvfy.l	Fri Jun 05 10:28:40 2009 -0400
@@ -0,0 +1,786 @@
+/*
+ * 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
+ * or http://www.opensolaris.org/os/licensing.
+ * 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 1995 Sun Microsystems, Inc.  All rights reserved.
+ * Use is subject to license terms.
+ */
+
+
+/*
+ * The purpose of this lex specification is to estimate the
+ * correctness of the various scripts that accompany packages. It
+ * is not flawless, but it is a better review than that of prior
+ * package validators. It looks for indications of interaction,
+ * root calls and attempts to modify locked files.
+ */
+%e 1500
+%p 3500
+%s WHROOT
+%{
+#undef	input
+#undef	unput
+FILE *scr_fp;
+#define	input()		(((yytchar=yysptr>yysbuf?U(*--yysptr):getc(scr_fp))==10?(yylineno++,yytchar):yytchar)==EOF?0:yytchar)
+#define	unput(p)	ungetc(p, scr_fp)
+
+#define	INTERACT_D	0x00000001	/* definitely */
+#define	ROOT_D		0x00000002
+#define	LOCKED_D	0x00000004
+#define	INTERACT_M	0x00010000	/* might be true, or we ... */
+#define	ROOT_M		0x00020000	/* ... might be reading it wrong. */
+#define	LOCKED_M	0x00040000
+#define	WPARM1_M	0x00080000	/* attempt to write to $1 */
+#define	USEPARM1_M	0x00100000	/* other attempt to use $1 */
+#define	ODDPARM_M	0x00200000	/* use of some other parameter */
+#define	PKGDB_M		0x00400000	/* read access to DB */
+#define	INITVAL		0x40000000
+
+/* Abbreviations */
+#define	INTERACT	(INTERACT_D | INTERACT_M)
+#define	ROOT		(ROOT_D | ROOT_M)
+#define	LOCKED		(LOCKED_D | LOCKED_M)
+#define	HASPARM		(WPARM1_M | USEPARM1_M | ODDPARM_M)
+
+/* Things the preinstall and preremove scripts can't do. */
+#define	PRE_MASK	(INTERACT | LOCKED | PKGDB_M | HASPARM)
+/*
+ * Things the class action script can't do. Don't get the impression that
+ * this means the class action script can be interactive; but, it can
+ * legitimately read stdin (which is what INTERACT tests for).
+ */
+#define	CAS_MASK	(LOCKED | PKGDB_M | WPARM1_M | ODDPARM_M)
+/* Things the postinstall and postremove scripts can't do. */
+#define	POST_MASK	(INTERACT | HASPARM)
+/* Things the request script can't do. */
+#define	REQ_MASK	(ROOT | ODDPARM_M)
+/* Things the checkinstall script can't do. */
+#define	CHK_MASK	(INTERACT | ROOT | ODDPARM_M)
+
+/* Nothing definite - not worth returning an error */
+#define	MAYBE_ONLY	~(INTERACT_D | ROOT_D | LOCKED_D)
+
+#define	WRN_INST_F	"WARNING: script <%s> uses installf but no " \
+			    "installf -f was detected."
+#define	WRN_REM_F	"WARNING: script <%s> uses removef but no " \
+			    "removef -f was detected."
+#define	WRN_INTERACT	"WARNING: script <%s> may require " \
+			    "user interaction at line <%d>."
+#define	WRN_LOCKED	"WARNING: script <%s> may seek access to the " \
+			    "transitional package database at line <%d>. " \
+			    "This is safest in the postinstall or " \
+			    "postremove script."
+#define	WRN_ROOT	"WARNING: script <%s> may not have permission " \
+			    "to execute line <%d>."
+#define	WRN_FORM_ARG	"WARNING: not sure where script <%s> gets the "\
+			    "parameter at line <%d>."
+#define	WRN_FORM_USE	"WARNING: script <%s> questionable usage of "\
+			    "parameter at line <%d>."
+#define	WRN_TRANSDB	"WARNING: script <%s> questionable read " \
+			    "of package database at line <%d>. An " \
+			    "intermediate buffer may be appropriate."
+#define	WRN_SPACEACC	"WARNING: script <%s> updates the package database " \
+			    "but provides no space file to account for " \
+			    "the additional package object."
+#define	ERR_INTERACT	"ERROR: script <%s> requires user " \
+			    "interaction at line <%d>."
+#define	ERR_LOCKED	"ERROR: script <%s> attempts to modify locked " \
+			    "package database at line <%d>."
+#define	ERR_ROOT	"ERROR: script <%s> requires root permission at " \
+			    "line <%d>."
+#define	ERR_FOPEN	"ERROR: Cannot evaluate script <%s>, errno=%d."
+#define	ERR_ARGS	"ERROR: scripteval() - no script provided for " \
+			    "evaluation."
+extern int errno;
+
+static int line_no;	/* current line number */
+int pipe_release = 0;	/* loop level for release of pipe */
+int loop_depth = 0;	/* current number of nested loops */
+int case_depth = 0;	/* same for case ... */
+int if_depth = 0;	/* ... and if statements */
+int cur_level = 0;	/* current number of nested anything */
+int braces = 0;		/* depth into a function */
+
+int lock_level = 0;
+int root_level = 0;
+
+struct statstrct {
+	unsigned int in_function:1;
+	unsigned int in_pipe:1;
+	unsigned int in_loop:1;
+	unsigned int in_case:1;
+	unsigned int in_if:1;
+	unsigned int in_awk:1;
+	unsigned int allow_int:1;	/* Allow an interactive function. */
+	unsigned int pkg_rtn_done:1;
+	unsigned int pkgchk_f:1;
+	unsigned int instf:1;
+	unsigned int instf_f:1;
+	unsigned int remf:1;
+	unsigned int remf_f:1;
+	unsigned int nospacefile:1;
+	unsigned int needspacefile:1;
+} status = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0};
+
+%}
+%%
+%{
+/*
+ * Validate a few OK patterns that look like bad patterns. These include:
+ *	1. comments
+ *	2. quoted strings
+ *	3. writes to $1 (request script)
+ *	4. reads from $1 (CAS)
+ *	5. writes to /dev/null
+ */
+%}
+#.*$ 	return INITVAL;
+
+\`	unput(' ');	/* No executable matching */
+
+%{
+/* Anybody can write to /dev/null and anybody can write to /tmp. */
+%}
+\>[ \t]*"/dev/null"	return INITVAL;
+\>[ \t]*"/tmp"		return INITVAL;
+
+%{
+/* If it's escaped, the next entry may as well be a space. */
+%}
+\\	{
+	char ch;
+
+	if ((ch = input()) == '\n')
+		line_no++;
+
+	unput(' ');
+}
+
+%{
+/* In the quotes is OK. */
+%}
+\"	{
+	char ch;
+	while ((ch = input()) != '\"') {
+		if (ch == '\\') {
+			input();	/* Read this into the bit bucket. */
+			continue;
+		}
+		if (ch == '\n')
+			line_no++;
+		else if (ch == '\0')
+			return (0);	/* EOF */
+	}
+}
+
+%{
+/* In the single quotes is OK if they aren't associated with an awk script. */
+%}
+\'	{
+	char ch;
+
+	if (status.in_awk != 0)
+		REJECT;;
+
+	while ((ch = input()) != '\'') {
+		if (ch == '\\') {
+			input();	/* Read this into the bit bucket. */
+			continue;
+		}
+		if (ch == '\n')
+			line_no++;
+		else if (ch == '\0')
+			return (0);	/* EOF */
+	}
+}
+
+%{
+/*
+ * Check for use of parameters passed to the script.
+ *	1. writes to $1 as though it were a file
+ *	2. use of $1 in any capacity
+ *	3. use of other parameters
+ * Within a function or an awk script, these parameters aren't
+ * the one's of interest.
+ */
+%}
+\>[\t ]*\$1/[\t\n ]	{
+	if (status.in_function == 0 && status.in_awk == 0)
+		return (WPARM1_M);
+}
+
+^$1/[\t\n ]	|
+[\t ]$1/[\t\n ]	{
+	if (status.in_function == 0 && status.in_awk == 0)
+		return (USEPARM1_M);
+}
+
+\$[2-9]	|
+\$[0-9][0-9]+ {
+	if (status.in_function == 0 && status.in_awk == 0)
+		return (ODDPARM_M);
+}
+
+%{
+/*
+ * Detect shell function.
+ */
+%}
+"()"[ \t]*\n[ \t]*/\{	{ status.in_function = 1; line_no++; }
+"()"[ ]*/\{	status.in_function = 1;
+
+"{" {
+	if (status.in_function == 1)
+		braces++;
+}
+
+"}" {
+	if (status.in_function == 1) {
+		braces--;
+		if (braces == 0)
+			status.in_function = 0;
+	}
+}
+
+%{
+/*
+ * Detect for or while loop.
+ */
+%}
+^for/[\t\n ]		|
+[\t ]for/[\t\n ]	|
+^while/[\t\n ]		|
+[\t ]while/[\t\n ] {
+	status.in_loop = 1;
+	loop_depth++;
+	cur_level++;
+	REJECT;		/* What's in the argument is important too. */
+}
+
+^done/[\t\n ] 	|
+[\t ]done/[\t\n ]  {
+	if (status.in_loop == 1)
+		loop_depth--;
+		cur_level--;
+		if (loop_depth == 0)
+			status.in_loop = 0;
+}
+
+%{
+/*
+ * Detect case.
+ */
+%}
+^case/[\t\n ]	|
+[\t ]case/[\t\n ] {
+	status.in_case = 1;
+	case_depth++;
+	cur_level++;
+	REJECT;		/* What's in the argument is important too. */
+}
+
+^esac/[\t\n ] 	|
+[\t ]esac/[\t\n ] {
+	if (status.in_case == 1)
+		case_depth--;
+		cur_level--;
+		if (case_depth == 0)
+			status.in_case = 0;
+}
+
+%{
+/*
+ * Detect if.
+ */
+%}
+^if" "*"["	|
+[\t ]if" "*"[" {
+	status.in_if = 1;
+	if_depth++;
+	cur_level++;
+	REJECT;		/* What's in the argument is important too. */
+}
+
+^fi/[\t\n ]	|
+[\t ]fi/[\t\n ]  {
+	if (status.in_if == 1)
+		if_depth--;
+		cur_level--;
+		if (if_depth == 0)
+			status.in_if = 0;
+}
+
+%{
+/*
+ * Detect awk or nawk function. If the function is enclosed in "`"s
+ * the entire line will be grabbed., so we check for that possibility.
+ */
+%}
+^n?awk[^\n^']*\' 	|
+[\t \\\(\/]n?awk[^\n^']*\'	status.in_awk = 1;
+
+
+\' {
+	if (status.in_awk == 1)
+		status.in_awk = 0;
+}
+
+%{
+/* Detect pipe target. */
+%}
+[\$A-Za-z]	{
+	if (status.in_pipe == 1 && pipe_release == cur_level)
+	{
+		status.in_pipe = 0;	/* target located */
+		pipe_release = 0;
+		status.allow_int = 1;	/* this isn't really interactive. */
+		REJECT;	/* put it back */
+	}
+}
+
+%{
+/* If it's a pipe, note that and continue. */
+%}
+"||"		|
+"|"		{
+	if (status.in_pipe == 0) {
+		status.in_pipe = 1;
+		pipe_release = cur_level;
+	}
+}
+
+%{
+/*
+ * Test input for admin-type telltale interactive functions. Definite's
+ * first, maybe's next.
+ */
+%}
+^ckdate/[\t\n ]		|
+[\t \/]ckdate/[\t\n ]	|
+^ckint/[\t\n ]		|
+[\t \/]ckint/[\t\n ]	|
+^ckrange/[\t\n ]	|
+[\t \/]ckrange/[\t\n ]	|
+^cktime/[\t\n ]		|
+[\t \/]cktime/[\t\n ]	|
+^ckyorn/[\t\n ]		|
+[\t \/]ckyorn/[\t\n ]	|
+^ckgid/[\t\n ]		|
+[\t \/]ckgid/[\t\n ]	|
+^ckpath/[\t\n ]		|
+[\t \/]ckpath/[\t\n ]	|
+^ckstr/[\t\n ]		|
+[\t \/]ckstr/[\t\n ]	|
+^ckuid/[\t\n ]		|
+[\t \/]ckuid/[\t\n ]		{
+	if (status.in_pipe == 1 || status.allow_int == 1)
+		return (INITVAL);
+	else
+		return (INTERACT_M);	/* maybe should be _D */
+}
+
+^read/[\t\n ]		|
+[\t ]read/[\t\n ]	|
+"=[ ]+&<"[\t ]	{
+	if (status.in_pipe == 1 || status.allow_int == 1)
+		return (INITVAL);
+	else
+		return (INTERACT_M);
+}
+
+%{
+/* Scan for root authority commands. Definite's first, maybe's next. */
+%}
+^mkdir/[\t\n ]		|
+[\t \/]mkdir/[\t\n ]	|
+^mv/[\t\n ]		|
+[\t \/]mv/[\t\n ]	|
+^cpio/[\t\n ]		|
+[\t \/]cpio/[\t\n ]	|
+^tar/[\t\n ]		|
+[\t \/]tar/[\t\n ]	|
+^(un)?compress/[\t\n ]	|
+[\t \/](un)?compress/[\t\n ]	|
+^rmdir/[\t\n ]		|
+[\t \/]rmdir/[\t\n ]	return (ROOT_D);
+
+^r?cp(dir)?/[\t\n ]	|
+[\t \/]r?cp(dir)?/[\t\n ]	|
+^rm/[\t\n ]	|
+[\t \/]rm/[\t\n ]	|
+\>[ \t]*[\$\/a-zA-Z0-9]	return (ROOT_M);
+
+%{
+/* These root commands may also be locked. */
+
+/* Here we analyze any pkgchk calls. If it's "pkgchk ... -f ..." then that calls for root authority. We then check for a "-R" argument. */
+%}
+^pkgchk[^\n^|^>^;]*"-f"	|
+[\t \/]pkgchk[^\n^|^>^;]*"-f"	{
+	status.pkgchk_f = 1;
+	REJECT;		/* We need the intermediate args. */
+}
+
+%{
+/* If it's "pkgchk ... -R ..." then the local package database is not being tested and no database warning is necessary. */
+%}
+^pkgchk[^\n^|^>^;]*"-R"[ \t][\/\$]/[^ ^\t^\n]		|
+[\t \/]pkgchk[^\n^|^>^;]*"-R"[ \t][\/\$]/[^ ^\t^\n]  {
+	if (status.pkgchk_f)
+		return (ROOT_D);
+	else
+		return (INITVAL);
+}
+
+%{
+/* If it's just "pkgchk ..." then we need to mention something about access to the package database. With Solaris 2.5, an improved locking mechanism is in place, so this message may be something we can drop later. */
+%}
+^pkgchk/[\t\n ]		|
+[\t \/]pkgchk/[\t\n ]  {
+	if (status.pkgchk_f) {
+		status.pkgchk_f = 0;
+		return (ROOT_D | PKGDB_M);
+	} else
+		return (PKGDB_M);
+}
+
+%{
+/* The installf and removef utilities require root authority, they modify the package database and they must be invoked at least once with a "-f" argument. */
+
+/* First test for a "-f" argument. */
+%}
+^installf[^\n^|^>^;]*"-f"	|
+[\t \/]installf[^\n^|^>^;]*"-f"	{
+	status.instf_f = 1;
+
+	REJECT;		/* The whole line needs to be re-reviewed. */
+}
+
+^removef[^\n^|^>^;]*"-f"	|
+[\t \/]removef[^\n^|^>^;]*"-f"	{
+	status.remf_f = 1;
+
+	REJECT;		/* The whole line needs to be re-reviewed. */
+}
+
+^installf/[\t\n ]	|
+[\t \/]installf/[\t\n ]	{
+	status.instf = 1;
+	status.needspacefile = 1;
+
+	root_level = ROOT_D;
+	lock_level = LOCKED_M;
+
+	BEGIN WHROOT;
+}
+
+^removef/[\t\n ]	|
+[\t \/]removef/[\t\n ]	{
+	status.remf = 1;
+
+	root_level = ROOT_D;
+	lock_level = LOCKED_M;
+	BEGIN WHROOT;
+}
+
+%{
+/* There's no question that use of a pkgadd or pkgrm in a script is bound to cause problems unless it is to a different root. */
+%}
+^pkgadd/[\t\n ]	|
+[\t \/]pkgadd/[\t\n ]	|
+^pkgrm/[\t\n ]		|
+[\t \/]pkgrm/[\t\n ] {
+	root_level = ROOT_D;
+	lock_level = LOCKED_D;
+	BEGIN WHROOT;
+}
+
+%{
+/* The only way to get here is if we are in the middle of a pkg command. */
+%}
+<WHROOT>. {
+	if (status.pkg_rtn_done) {
+		status.pkg_rtn_done = 0;
+		BEGIN 0;
+	} else
+		REJECT;
+}
+<WHROOT>[ \t]+"-R"[ \t][\/\$]/[^ ^\t^\n] {
+	status.pkg_rtn_done = 1;
+	return (root_level);		/* "-R" means locking is unlikely. */
+}
+<WHROOT>[\n]		{
+	if (status.pkg_rtn_done) {
+		status.pkg_rtn_done = 0;
+		line_no++;
+		BEGIN 0;
+	} else {
+		status.pkg_rtn_done = 1;
+		unput('\n');
+		return (root_level | lock_level); /* No "-R". */
+	}
+}
+<WHROOT>[;|>]		{
+	status.pkg_rtn_done = 1;
+	return (root_level | lock_level); /* End of command without a "-R". */
+}
+
+\n	{ line_no++; status.allow_int = 0; }
+.	status.allow_int = 0;
+%%
+#include <stdio.h>
+#include <limits.h>
+#include <dirent.h>
+#include <unistd.h>
+#include <libintl.h>
+
+#ifdef DEBUG
+/*
+ * Since this is a lex specification twice removed from the binary,
+ * I strongly recommend leaving the DEBUG portions in place. When new
+ * keywords are added, this will be very important. After modifying
+ * the specification, create an executable to test in this way.
+ *
+ *	lex scriptvfy.l
+ *	cc -o scriptvfy -g lex.yy.c $ROOT/usr/lib/libpkg.a \
+ *	    -DDEBUG [-DVERBOSE] -ll -lintl
+ *	scriptvfy test_directory
+ */
+
+main(int argc, char *argv[])
+{
+	int val;
+
+	line_no = 1;
+
+	if (argc == 1) {
+		printf("No directory provided.\n");
+		exit(1);
+	}
+
+	val = checkscripts(argv[1], 0);
+
+	printf("return code is %d\n", val);
+}
+#endif
+
+/*
+ * This function evaluates the provided script and returns a bit string
+ * describing what patterns were located.
+ */
+static int
+scripteval(char *script_name, char *script_path, int mask, int silent)
+{
+	int val = 0;
+	int error = 0;
+	line_no = 1;
+
+	if ((script_path == NULL) || (*script_path == NULL) ||
+	    (script_name == NULL)) {
+		logerr(gettext(ERR_ARGS));
+		return (0);
+	}
+
+#ifdef VERBOSE
+	printf("Evaluating %s\n", script_path);
+#endif
+
+	if ((scr_fp = fopen(script_path, "r")) == NULL) {
+		logerr(gettext(ERR_FOPEN), script_path, errno);
+		return (0);
+	}
+
+#ifdef VERBOSE
+	printf("Openned script\n");
+#endif
+
+	while (val = yylex()) {
+#ifdef VERBOSE
+		printf("  Match is %s, returned 0x%x at line %d\n",
+		    yytext, val, line_no);
+		printf("    in_function = %d, in_awk = %d, in_loop = %d, " \
+		    "in_case = %d, in_if = %d, in_pipe = %d\n",
+		    status.in_function, status.in_awk, status.in_loop,
+		    status.in_case, status.in_if, status.in_pipe);
+		printf("    loop_depth = %d, case_depth = %d, " \
+		    "if_depth = %d, pipe_release = %d, cur_level = %d\n",
+		    loop_depth, case_depth, if_depth, pipe_release, cur_level);
+#endif
+
+		val &= mask;
+		if (val) {
+			error |= ((val & MAYBE_ONLY) ? 1 : 2);
+
+			/*
+			 * So at this point, val contains all status bits
+			 * appropriate to this script.
+			 */
+			if (!silent) {
+				char *msg_ptr;
+				if (val & INTERACT_D)
+					msg_ptr = gettext(ERR_INTERACT);
+				else if (val & ROOT_D)
+					msg_ptr = gettext(ERR_ROOT);
+				else if (val & LOCKED_D)
+					msg_ptr = gettext(ERR_LOCKED);
+				else if (val & INTERACT_M)
+					msg_ptr = gettext(WRN_INTERACT);
+				else if (val & ROOT_M)
+					msg_ptr = gettext(WRN_ROOT);
+				else if (val & LOCKED_M)
+					msg_ptr = gettext(WRN_LOCKED);
+				else if (val & WPARM1_M)
+					msg_ptr = gettext(WRN_FORM_USE);
+				else if (val & USEPARM1_M)
+					msg_ptr = gettext(WRN_FORM_USE);
+				else if (val &  ODDPARM_M)
+					msg_ptr = gettext(WRN_FORM_ARG);
+				else if (val &  PKGDB_M)
+					msg_ptr = gettext(WRN_TRANSDB);
+				else
+					msg_ptr = gettext("unknown error");
+
+				logerr(msg_ptr, script_name, line_no);
+			}
+		}
+	}
+
+	/* Warn if required about missing "-f" calls. */
+	if (status.instf && !(status.instf_f))
+		logerr(gettext(WRN_INST_F), script_name);
+
+	if (status.remf && !(status.remf_f))
+		logerr(gettext(WRN_REM_F), script_name);
+
+	status.instf = status.instf_f = status.remf = status.remf_f = 0;
+
+	/* Warn if installf was used but no space file is in place. */
+	if (status.nospacefile && status.needspacefile) {
+		logerr(gettext(WRN_SPACEACC), script_name);
+		status.needspacefile = 0;
+	}
+
+	status.in_pipe = 0;	/* Pipes may dangle. */
+	fclose(scr_fp);
+
+	if (error == 3)
+		error = 2;
+
+	return (error);
+}
+
+/* Test a preinstall or preremove script for validity. */
+int
+pre_valid(char *script_name, char *script_path, int silent)
+{
+	return (scripteval(script_name, script_path, PRE_MASK, silent));
+}
+
+/* Test a class action script for validity. */
+int
+cas_valid(char *script_name, char *script_path, int silent)
+{
+	return (scripteval(script_name, script_path, CAS_MASK, silent));
+}
+
+/* Test a postinstall or postremove script for validity. */
+int
+post_valid(char *script_name, char *script_path, int silent)
+{
+	return (scripteval(script_name, script_path, POST_MASK, silent));
+}
+
+/* Test a class action script for validity. */
+int
+req_valid(char *script_name, char *script_path, int silent)
+{
+	return (scripteval(script_name, script_path, REQ_MASK, silent));
+}
+
+
+/* Test a class action script for validity. */
+int
+chk_valid(char *script_name, char *script_path, int silent)
+{
+	return (scripteval(script_name, script_path, CHK_MASK, silent));
+}
+
+/* This tests all of the scripts in the provided directory. */
+int
+checkscripts(char *inst_dir, int silent)
+{
+	DIR *dirfp;
+	struct dirent *dp;
+	char path[PATH_MAX];
+	int retval = 0;
+
+	/* For future reference, determine if a space file is present. */
+	sprintf(path, "%s/%s", inst_dir, "space");
+	if (access(path, F_OK) != 0)
+		status.nospacefile = 1;
+
+	if ((dirfp = opendir(inst_dir)) == NULL)
+		return (0);
+
+	while ((dp = readdir(dirfp)) != NULL) {
+#ifdef VERBOSE
+		printf("Looking at file %s\n", dp->d_name);
+#endif
+		if (dp->d_name[0] == '.')
+			continue;
+
+		if ((strcmp(dp->d_name, "preinstall") == 0) ||
+		    (strcmp(dp->d_name, "preremove") == 0)) {
+			sprintf(path, "%s/%s", inst_dir, dp->d_name);
+			retval |= pre_valid(dp->d_name, path, silent);
+			continue;
+		}
+
+		if ((strncmp(dp->d_name, "i.", 2) == 0) ||
+		    (strncmp(dp->d_name, "r.", 2) == 0)) {
+			sprintf(path, "%s/%s", inst_dir, dp->d_name);
+			retval |= cas_valid(dp->d_name, path, silent);
+			continue;
+		}
+
+		if ((strcmp(dp->d_name, "postinstall") == 0) ||
+		    (strcmp(dp->d_name, "postremove") == 0)) {
+			sprintf(path, "%s/%s", inst_dir, dp->d_name);
+			retval |= post_valid(dp->d_name, path, silent);
+			continue;
+		}
+
+		if (strcmp(dp->d_name, "request") == 0) {
+			sprintf(path, "%s/%s", inst_dir, dp->d_name);
+			retval |= req_valid(dp->d_name, path, silent);
+			continue;
+		}
+		if (strcmp(dp->d_name, "checkinstall") == 0) {
+			sprintf(path, "%s/%s", inst_dir, dp->d_name);
+			retval |= chk_valid(dp->d_name, path, silent);
+			continue;
+		}
+	}
+
+	(void) closedir(dirfp);
+
+	return (retval);
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/usr/src/cmd/svr4pkg/libinst/setadmin.c	Fri Jun 05 10:28:40 2009 -0400
@@ -0,0 +1,336 @@
+/*
+ * 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
+ * or http://www.opensolaris.org/os/licensing.
+ * 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 2006 Sun Microsystems, Inc.  All rights reserved.
+ * Use is subject to license terms.
+ */
+
+/* Copyright (c) 1984, 1986, 1987, 1988, 1989 AT&T */
+/* All Rights Reserved */
+
+
+#include <stdio.h>
+#include <limits.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <string.h>
+#include <errno.h>
+#include <pkglocs.h>
+#include <locale.h>
+#include <libintl.h>
+#include <pkglib.h>
+#include <pkgerr.h>
+#include <pkgweb.h>
+#include <install.h>
+#include <libinst.h>
+#include <libadm.h>
+#include <messages.h>
+
+#define	DEFMAIL	"root"
+
+extern struct admin	adm;		/* holds info about install admin */
+extern int		warnflag;	/* != 0 non-fatal error occurred 2 */
+
+static struct {
+	char	**memloc;
+	char	*tag;
+} admlist[] = {
+	&adm.action,		"action",
+	&adm.authentication,	"authentication",
+	&adm.basedir,		"basedir",
+	&adm.conflict,		"conflict",
+	&adm.idepend,		"idepend",
+	&adm.instance,		"instance",
+	&adm.keystore,		"keystore",
+	&adm.mail,		"mail",
+	&adm.networkretries,	"networkretries",
+	&adm.networktimeout,	"networktimeout",
+	&adm.partial,		"partial",
+	&adm.proxy,		"proxy",
+	&adm.rdepend,		"rdepend",
+	&adm.RSCRIPTALT,	RSCRIPTALT_KEYWORD,
+	&adm.runlevel,		"runlevel",
+	&adm.setuid,		"setuid",
+	&adm.space,		"space",
+	/* MUST BE LAST ENTRY IN LIST */
+	(char **)NULL,		(char *)NULL
+};
+
+/*
+ * Name:	setadminSetting
+ * Description:	set one administration parameter setting
+ * Arguments:	a_paramName - pointer to string representing the name of
+ *			the administration parameter to set
+ *		a_paramValue - pointer to string representing the value
+ *			to set the specified administration parameter to
+ * Returns:	char *
+ *			- old value the parameter had before being set
+ *			== NULL - the old paramter was not set
+ */
+
+char *
+setadminSetting(char *a_paramName, char *a_paramValue)
+{
+	char	*oldValue = (char *)NULL;
+	int	i;
+
+	/* locate and update the specified admin setting */
+
+	for (i = 0; admlist[i].memloc; i++) {
+		if (strcmp(a_paramName, admlist[i].tag) == 0) {
+			oldValue = *admlist[i].memloc;
+			*admlist[i].memloc = a_paramValue;
+			break;
+		}
+	}
+
+	if (admlist[i].memloc == (char **)NULL) {
+		logerr(WRN_UNKNOWN_ADM_PARAM, a_paramName);
+	}
+
+	return (oldValue);
+}
+
+/*
+ * Name:	setadminFile
+ * Description:	read and remember settings from administration file
+ * Arguments:	file - pointer to string representing the path to the
+ *			administration file to read - if this is NULL
+ *			then the name "default" is used - if this is
+ *			the string "none" then the admin "basedir"
+ *			setting is set to "ask" so that the location
+ *			of the administration file will be interactively
+ *			asked at the appropriate time
+ * Returns:	void
+ */
+
+void
+setadminFile(char *file)
+{
+	FILE	*fp;
+	int	i;
+	char	param[MAX_PKG_PARAM_LENGTH];
+	char	*value;
+	char	path[PATH_MAX];
+	int	mail = 0;
+
+	if (file == NULL)
+		file = "default";
+	else if (strcmp(file, "none") == 0) {
+		adm.basedir = "ask";
+		return;
+	}
+
+	if (file[0] == '/')
+		(void) strcpy(path, file);
+	else {
+		(void) snprintf(path, sizeof (path), "%s/admin/%s",
+				get_PKGADM(), file);
+		if (access(path, R_OK)) {
+			(void) snprintf(path, sizeof (path), "%s/admin/%s",
+				PKGADM, file);
+		}
+	}
+
+	if ((fp = fopen(path, "r")) == NULL) {
+		progerr(ERR_OPEN_ADMIN_FILE, file, strerror(errno));
+		quit(99);
+	}
+
+	param[0] = '\0';
+	while (value = fpkgparam(fp, param)) {
+		if (strcmp(param, "mail") == 0) {
+			mail = 1;
+		}
+		if (value[0] == '\0') {
+			param[0] = '\0';
+			continue; /* same as not being set at all */
+		}
+		for (i = 0; admlist[i].memloc; i++) {
+			if (strcmp(param, admlist[i].tag) == 0) {
+				*admlist[i].memloc = value;
+				break;
+			}
+		}
+		if (admlist[i].memloc == NULL) {
+			logerr(WRN_UNKNOWN_ADM_PARAM, param);
+			free(value);
+		}
+		param[0] = '\0';
+	}
+
+	(void) fclose(fp);
+
+	if (!mail) {
+		adm.mail = DEFMAIL; 	/* if we don't assign anything to it */
+	}
+}
+
+
+/*
+ * Function:	web_ck_retries
+ * Description:	Reads admin file setting for networkretries, or uses default
+ * Parameters:	None
+ * Returns:	admin file setting for networkretries, or the default if no
+ *		admin file setting exists or if it is outside the
+ *		allowable range.
+ */
+int
+web_ck_retries(void)
+{
+	int retries = NET_RETRIES_DEFAULT;
+
+	if (ADMSET(networkretries)) {
+		/* Make sure value is within valid range */
+		if ((retries = atoi(adm.networkretries)) == 0) {
+			return (NET_RETRIES_DEFAULT);
+		} else if (retries <= NET_RETRIES_MIN ||
+			retries > NET_RETRIES_MAX) {
+			return (NET_RETRIES_DEFAULT);
+		}
+	}
+	return (retries);
+}
+
+/*
+ * Function:	web_ck_authentication
+ * Description:	Retrieves admin file setting for authentication
+ * Parameters:	None
+ * Returns:	admin file policy for authentication - AUTH_QUIT
+ *		or AUTH_NOCHECK.
+ *		non-zero failure
+ */
+int
+web_ck_authentication(void)
+{
+	if (ADM(authentication, "nocheck"))
+		return (AUTH_NOCHECK);
+
+	return (AUTH_QUIT);
+}
+
+/*
+ * Function:	web_ck_timeout
+ * Description:	Retrieves admin file policy for networktimeout's
+ * Parameters:	NONE
+ * Returns:	Admin file setting for networktimeout, or default
+ *		timeout value if admin file does not specify one,
+ *		or specifies one that is out of the allowable range.
+ */
+int
+web_ck_timeout(void)
+{
+	int timeout = NET_TIMEOUT_DEFAULT;
+
+	if (ADMSET(networktimeout)) {
+		/* Make sure value is within valid range */
+		if ((timeout = atoi(adm.networktimeout)) == 0) {
+			return (NET_TIMEOUT_DEFAULT);
+		} else if (timeout <= NET_TIMEOUT_MIN ||
+			timeout > NET_TIMEOUT_MAX) {
+			return (NET_TIMEOUT_DEFAULT);
+		}
+	}
+	return (timeout);
+}
+
+/*
+ * Function:	check_keystore_admin
+ * Description:	Retrieves security keystore setting from admin file,
+ *		or validates user-supplied keystore policy.
+ * Parameters:	keystore - Where to store resulting keystore policy
+ * Returns:	B_TRUE - admin file contained valid keystore, or
+ *		user-supplied keystore passed in "keystore" was
+ *		valid.  Resulting keystore stored in "keystore"
+ *
+ *		B_FALSE - No location supplied to store result,
+ *		or user-supplied keystore was not valid.
+ */
+boolean_t
+check_keystore_admin(char **keystore)
+{
+
+	if (!keystore) {
+		/* no location to store keystore */
+		return (B_FALSE);
+	}
+
+	if (*keystore != NULL) {
+	    if (!path_valid(*keystore)) {
+		    /* the given keystore is invalid */
+		    return (B_FALSE);
+	    }
+
+	    /* the user-supplied keystore was valid */
+	    return (B_TRUE);
+	}
+
+	/* no user-supplied, so use default */
+	if ((*keystore = set_keystore_admin()) == NULL) {
+		*keystore = PKGSEC;
+	}
+	return (B_TRUE);
+}
+
+/*
+ * Function:	get_proxy_port_admin
+ * Description:	Retrieves proxy setting from admin file
+ * Parameters:	proxy - where to store resulting proxy (host:port or URL)
+ *		port - Where to store resulting proxy port
+ * Returns:	B_TRUE - admin file had a valid proxy setting,
+ *		and it is stored in "proxy".
+ *		B_FALSE - no proxy setting in admin file, or
+ *		invalid setting in admin file.
+ */
+boolean_t
+get_proxy_port_admin(char **proxy, ushort_t *port)
+{
+	if (ADMSET(proxy) && !path_valid(adm.proxy)) {
+		/* admin file has bad keystore */
+		return (B_FALSE);
+	} else if (ADMSET(proxy)) {
+		*proxy = strdup(adm.proxy);
+		*port = strip_port(adm.proxy);
+	}
+	return (B_TRUE);
+}
+
+/*
+ * Function:	set_keystore_admin
+ * Description:	Retrieves security keystore setting from admin file,
+ * Parameters:	NONE
+ * Returns:	Keystore file policy from admin file, if set
+ *		and valid.  NULL otherwise.
+ */
+char *
+set_keystore_admin(void)
+{
+	if (ADMSET(keystore) && !path_valid(adm.keystore)) {
+		return (NULL);
+	}
+
+	if (!ADMSET(keystore)) {
+		return (NULL);
+	}
+
+	return (adm.keystore);
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/usr/src/cmd/svr4pkg/libinst/setlist.c	Fri Jun 05 10:28:40 2009 -0400
@@ -0,0 +1,437 @@
+/*
+ * 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
+ * or http://www.opensolaris.org/os/licensing.
+ * 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 2006 Sun Microsystems, Inc.  All rights reserved.
+ * Use is subject to license terms.
+ */
+
+/* Copyright (c) 1984, 1986, 1987, 1988, 1989 AT&T */
+/* All Rights Reserved */
+
+
+
+#include <stdio.h>
+#include <errno.h>
+#include <string.h>
+#include <ctype.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <locale.h>
+#include <libintl.h>
+#include <pkglocs.h>
+#include <pkglib.h>
+#include "libinst.h"
+
+int	cl_NClasses = -1;
+static int	cl_handle = -1;	/* list array handle */
+
+struct cl_attr	**cl_Classes = NULL;
+
+static int new_order;
+static struct cl_attr	*new_cl_attr(char *cl_name);
+
+static unsigned	s_verify(char *class_name), d_verify(char *class_name);
+static unsigned	s_pathtype(char *class_name);
+
+#define	MALSIZ	64
+#define	ERR_MEMORY	"memory allocation failure"
+
+static struct cl_attr *
+new_cl_attr(char *cl_name)
+{
+	struct cl_attr *class, **class_ptr;
+
+	if (cl_handle == -1) {
+		cl_handle = ar_create(MALSIZ, sizeof (struct cl_attr),
+		    "package class");
+		if (cl_handle == -1) {
+			progerr(gettext(ERR_MEMORY));
+			return (NULL);
+		}
+	}
+
+	class_ptr = (struct cl_attr **)ar_next_avail(cl_handle);
+
+	if (class_ptr == NULL || *class_ptr == NULL) {
+		progerr(gettext(ERR_MEMORY));
+		return (NULL);
+	}
+
+	class = *class_ptr;
+
+	strcpy(class->name, cl_name);
+	class->inst_script = NULL;
+	class->rem_script = NULL;
+	class->src_verify = s_verify(cl_name);
+	class->dst_verify = d_verify(cl_name);
+	class->relpath_2_CAS = s_pathtype(cl_name);
+
+	return (class);
+}
+
+/* Insert a single class into the list. */
+void
+addlist(struct cl_attr ***listp, char *item)
+{
+	int	i;
+
+	/* If the list is already there, scan for this item */
+	if (*listp) {
+		for (i = 0; (*listp)[i]; i++)
+			if (strcmp(item, (*listp)[i]->name) == 0)
+				return;
+	} else {
+		i = 0;
+	}
+
+	/* Insert the new entry */
+	if (new_cl_attr(item) == NULL)
+		quit(99);
+
+	/* Point the passed pointer to the head of the list. */
+	(*listp) = (struct cl_attr **)ar_get_head(cl_handle);
+}
+
+/*
+ * Create a list of all classes involved in this installation as well as
+ * their attributes.
+ */
+int
+setlist(struct cl_attr ***plist, char *slist)
+{
+	struct cl_attr	**list, *struct_ptr;
+	char	*pt;
+	int	n;
+	int	i;
+	int	sn = -1;
+
+	/* Initialize the environment scanners. */
+	(void) s_verify(NULL);
+	(void) d_verify(NULL);
+	(void) s_pathtype(NULL);
+
+	n = 0;
+
+	/*
+	 * This looks like a serious memory leak, however pkgmk depends on
+	 * this creating a second list and forgetting any prior ones. The
+	 * pkgmk utility does a reasonable job of keeping track of a prior
+	 * list constructed from the prototype file using addlist() above.
+	 * Perhaps this should be reviewed later, but I do not believe this
+	 * to be a problem from what I've seen. - JST
+	 */
+	cl_handle = -1;		/* forget other lists */
+
+	/* Isolate the first token. */
+	pt = strtok(slist, " \t\n");
+	while (pt) {
+		if (sn == -1 && strcmp(pt, "none") == 0)
+			sn = n;
+
+		/* Add new class to list. */
+		if ((struct_ptr = new_cl_attr(pt)) == NULL)
+			quit(99);
+
+		/* Next token. */
+		n++;
+		pt = strtok(NULL, " \t\n");
+		if (pt && sn != -1)
+			if (strcmp(pt, "none") == 0)
+				pt = strtok(NULL, " \t\n");
+	}
+	/*
+	 * According to the ABI, if there is a class "none", it will be
+	 * the first class to be installed.  This insures that iff there
+	 * is a class "none", it will be the first to be installed.
+	 * If there is no class "none", nothing happens!
+	 */
+	new_order = 0;
+
+	/* Get the head of the array. */
+	list = (struct cl_attr **)ar_get_head(cl_handle);
+
+	if (sn > 0) {
+		struct_ptr = list[sn];
+		for (i = sn; i > 0; i--)
+			list[i] = list[i - 1];
+		list[0] = struct_ptr;
+		new_order++;	/* the order is different now */
+	}
+
+	/* Point the passed pointer to the head of the list. */
+	*plist = list;
+
+	return (n);
+}
+
+/* Process the class list from the caller. */
+void
+cl_sets(char *slist)
+{
+	char *list_ptr;
+
+	/* If there is a list, process it; else skip it */
+	if (slist && *slist) {
+		list_ptr = qstrdup(slist);
+
+		if (list_ptr && *list_ptr) {
+			cl_NClasses = setlist(&cl_Classes, list_ptr);
+			if (new_order)		/* if list order changed ... */
+				/* ... tell the environment. */
+				cl_putl("CLASSES", cl_Classes);
+		}
+	}
+}
+
+int
+cl_getn(void)
+{
+	return (cl_NClasses);
+}
+
+/*
+ * Since the order may have changed, this puts the CLASSES list back into
+ * the environment in the precise order to be used.
+ */
+void
+cl_putl(char *parm_name, struct cl_attr **list)
+{
+	int i;
+	size_t j;
+	char *pt = NULL;
+
+	if (list && *list) {
+		j = 1; /* room for ending null */
+		for (i = 0; list[i]; i++)
+			j += strlen(list[i]->name) + 1;
+		pt = calloc(j, sizeof (char));
+		(void) strcpy(pt, list[0]->name);
+		for (i = 1; list[i]; i++) {
+			(void) strcat(pt, " ");
+			(void) strcat(pt, list[i]->name);
+		}
+		if (parm_name && *parm_name)
+			putparam(parm_name, pt);
+		free(pt);
+	}
+}
+
+
+int
+cl_idx(char *cl_nam)
+{
+	int	n;
+
+	for (n = 0; n < cl_NClasses; n++)
+		if (strcmp(cl_Classes[n]->name, cl_nam) == 0)
+			return (n);
+	return (-1);
+}
+
+/* Return source verification level for this class */
+unsigned
+cl_svfy(int idx)
+{
+	if (cl_Classes && idx >= 0 && idx < cl_NClasses)
+		return (cl_Classes[idx]->src_verify);
+	return (0);
+}
+
+/* Return destination verify level for this class */
+unsigned
+cl_dvfy(int idx)
+{
+	if (cl_Classes && idx >= 0 && idx < cl_NClasses)
+		return (cl_Classes[idx]->dst_verify);
+	return (0);
+}
+
+/* Return path argument type for this class. */
+unsigned
+cl_pthrel(int idx)
+{
+	if (cl_Classes && idx >= 0 && idx < cl_NClasses)
+		return (cl_Classes[idx]->relpath_2_CAS);
+	return (0);
+}
+
+/* Return the class name associated with this class index */
+char *
+cl_nam(int idx)
+{
+	if (cl_Classes && idx >= 0 && idx < cl_NClasses)
+		return (cl_Classes[idx]->name);
+	return (NULL);
+}
+
+void
+cl_setl(struct cl_attr **cl_lst)
+{
+	int	i;
+	int	sn = -1;
+	struct cl_attr	*pt;
+
+	if (cl_lst) {
+		for (cl_NClasses = 0; cl_lst[cl_NClasses]; cl_NClasses++)
+			if (strcmp(cl_lst[cl_NClasses]->name, "none") == 0)
+				if (sn == -1)
+					sn = cl_NClasses;
+		if (sn > 0) {
+			pt = cl_lst[sn];
+			for (i = sn; i > 0; i--)
+				cl_lst[i] = cl_lst[i - 1];
+			cl_lst[0] = pt;
+		}
+		i = 1;
+		while (i < cl_NClasses) {
+			if (strcmp(cl_lst[i]->name, "none") == 0)
+				for (sn = i; sn < (cl_NClasses - 1); sn++)
+					cl_lst[sn] = cl_lst[sn + 1];
+			i++;
+		}
+		cl_Classes = cl_lst;
+	} else {
+		cl_Classes = NULL;
+		cl_NClasses = -1;
+	}
+}
+
+/*
+ * Scan the given environment variable for an occurrance of the given
+ * class name. Return 0 if not found or 1 if found.
+ */
+static unsigned
+is_in_env(char *class_name, char *paramname, char **paramvalue, int *noentry)
+{
+	unsigned retval = 0;
+	char *test_class;
+
+	if (class_name && *class_name) {
+		/*
+		 * If a prior getenv() has not failed and there is no
+		 * environment string then get environment info on
+		 * this parameter.
+		 */
+		if (!(*noentry) && *paramvalue == NULL) {
+			*paramvalue = getenv(paramname);
+			if (*paramvalue == NULL)
+				(*noentry)++;
+		}
+
+		/* If there's something there, evaluate it. */
+		if (!(*noentry)) {
+			int n;
+
+			n = strlen(class_name);	/* end of class name */
+			test_class = *paramvalue;	/* environ ptr */
+
+			while (test_class = strstr(test_class, class_name)) {
+				/*
+				 * At this point we have a pointer to a
+				 * substring within param that matches
+				 * class_name for its length, but class_name
+				 * may be a substring of the test_class, so
+				 * we check that next.
+				 */
+				if (isspace(*(test_class + n)) ||
+				    *(test_class + n) == '\0') {
+					retval = 1;
+					break;
+				}
+				if (*(++test_class) == '\0')
+					break;
+			}
+		}
+	}
+	return (retval);
+}
+
+/* Assign source path verification level to this class */
+static unsigned
+s_verify(char *class_name)
+{
+	static int noentry;
+	static char *noverify;
+
+	if (class_name == NULL) {	/* initialize */
+		noentry = 0;
+		noverify = NULL;
+	} else {
+		if (is_in_env(class_name, "PKG_SRC_NOVERIFY", &noverify,
+		    &noentry))
+			return (NOVERIFY);
+		else
+			return (DEFAULT);
+	}
+	return (0);
+}
+
+/*
+ * Set destination verify to default. This is usually called by pkgdbmerg()
+ * in order to correct verification conflicts.
+ */
+void
+cl_def_dverify(int idx)
+{
+	if (cl_Classes && idx >= 0 && idx < cl_NClasses)
+		cl_Classes[idx]->dst_verify = DEFAULT;
+}
+
+/* Assign destination path verification level to this path. */
+static unsigned
+d_verify(char *class_name)
+{
+	static int noentry;
+	static char *qkverify;
+
+	if (class_name == NULL) {	/* initialize */
+		noentry = 0;
+		qkverify = NULL;
+	} else {
+		if (is_in_env(class_name, "PKG_DST_QKVERIFY", &qkverify,
+		    &noentry))
+			return (QKVERIFY);
+		else
+			return (DEFAULT);
+	}
+	return (0);
+}
+
+/* Assign CAS path type to this class */
+static unsigned
+s_pathtype(char *class_name)
+{
+	static int noentry;
+	static char *type_list;
+
+	if (class_name == NULL) {	/* initialize */
+		noentry = 0;
+		type_list = NULL;
+	} else {
+		if (is_in_env(class_name, "PKG_CAS_PASSRELATIVE", &type_list,
+		    &noentry))
+			return (REL_2_CAS);
+		else
+			return (DEFAULT);
+	}
+	return (0);
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/usr/src/cmd/svr4pkg/libinst/setup_temporary_directory.c	Fri Jun 05 10:28:40 2009 -0400
@@ -0,0 +1,111 @@
+/*
+ * 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
+ * or http://www.opensolaris.org/os/licensing.
+ * 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 2004 Sun Microsystems, Inc.  All rights reserved.
+ * Use is subject to license terms.
+ */
+
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <sys/stat.h>
+#include <assert.h>
+#include <fcntl.h>
+#include <errno.h>
+#include <ctype.h>
+#include <string.h>
+#include <locale.h>
+#include <libintl.h>
+#include <pwd.h>
+
+/*
+ * consolidation pkg command library includes
+ */
+
+#include <pkglib.h>
+
+/*
+ * local pkg command library includes
+ */
+
+#include "install.h"
+#include "libinst.h"
+#include "libadm.h"
+#include "messages.h"
+
+/*
+ * Name:	setup_temporary_directory
+ * Description:	create a temporary directory from specified components
+ *		and return full path to the directory created
+ * Arguments:	r_dirname - pointer to handle to string - on success,
+ *			the full path to the temporary directory created
+ *			is returned in this handle
+ *		a_tmpdir - pointer to string representing the directory into
+ *			which the new temporary directory should be created
+ *		a_suffix - pointer to string representing the 5-character
+ *			suffix to be used as the first part of the temporary
+ *			directory name invented
+ * Returns:	boolean_t
+ *			== B_TRUE - temporary directory created, path returned
+ *			== B_FALSE - failed to create temporary directory
+ *				'errno' is set to the failure reason
+ * NOTE:    	Any path returned is placed in new storage for the
+ *		calling function. The caller must use 'free' to dispose
+ *		of the storage once the path is no longer needed.
+ */
+
+boolean_t
+setup_temporary_directory(char **r_dirname, char *a_tmpdir, char *a_suffix)
+{
+	char	*dirname;
+
+	/* entry assertions */
+
+	assert(a_tmpdir != (char *)NULL);
+
+	/* error if no pointer provided to return temporary name in */
+
+	if (r_dirname == (char **)NULL) {
+		errno = EFAULT;			/* bad address */
+		return (B_FALSE);
+	}
+
+	/* generate temporary directory name */
+
+	dirname = tempnam(a_tmpdir, a_suffix);
+	if (dirname == (char *)NULL) {
+		return (B_FALSE);
+	}
+
+	/* create the temporary directory */
+
+	if (mkdir(dirname, 0755) != 0) {
+		return (B_FALSE);
+	}
+
+	echoDebug(DBG_SETUP_TEMPDIR, dirname);
+
+	*r_dirname = dirname;
+
+	return (B_TRUE);
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/usr/src/cmd/svr4pkg/libinst/sml.c	Fri Jun 05 10:28:40 2009 -0400
@@ -0,0 +1,3327 @@
+/*
+ * 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
+ * or http://www.opensolaris.org/os/licensing.
+ * 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 2005 Sun Microsystems, Inc.  All rights reserved.
+ * Use is subject to license terms.
+ */
+
+
+/*
+ * Module:	sml.c
+ * Synopsis:	simplified markup language (SML) support
+ * Taxonomy:	project private
+ * Debug flag:	sml
+ * Description:
+ *
+ *   This module implements methods that support the processing of a
+ *   simplified markup language (SML). Objects that contain SML data
+ *   can be created and manipulated, and SML can be imported into
+ *   internal SML objects or exported from internal SML objects.
+ *
+ * Public Methods:
+ *
+ *   smlAddTag - Add new tag object into existing tag object
+ *   smlConvertStringToTag - Convert string into tag object
+ *   smlConvertTagToString - Convert a tag object into a string
+ *		representation of the XML
+ *   smlDbgPrintTag - Print a representation of an XML tag if debugging
+ *   smlDelParam - Delete a parameter from a tag object
+ *   smlDelTag - Delete element from tag object
+ *   smlDup - Duplicate a tag object
+ *   smlFindAndDelTag - Delete a tag if found in tag object
+ *   smlFreeTag - Free a tag object and all its contents when no
+ *		longer needed
+ *   smlFstatCompareEq - Compare file status information
+ *   smlGetElementName - Return a tag's element name
+ *   smlGetNumParams - Get number of parameters set in tag
+ *   smlGetParam - Get a parameter from a tag
+ *   smlGetParamF - Get a formatted parameter from a tag
+ *   smlGetParamByTag - Get a parameter by tag and index
+ *   smlGetParamByTagParam Get parameter given tag name, index,
+ *		parameter name, and value
+ *   smlGetParamName - Get the name of a tag parameter given its index
+ *   smlGetParam_r - Get a parameter from a tag into fixed buffer
+ *   smlGetTag - Get an element from a tag
+ *   smlGetTagByName - Get an element given a name and an index
+ *   smlGetTagByTagParam - Get element given tag name, index, parameter name,
+ *		and value
+ *   smlGetVerbose - get current verbose mode setting
+ *   smlLoadTagFromFile - Load a file into a tag object
+ *   smlNewTag - Create a new (empty) tag object
+ *   smlParamEq - Determine if parameter is equal to a specified value
+ *   smlParamEqF - Determine if parameter is equal to a specified value
+ *   smlPrintTag - Print a simple XML representation of a tag to stderr
+ *   smlReadOneTag - read one complete tag from a datastream
+ *   smlReadTagFromDs - read tag object from datastream
+ *   smlSetFileStatInfo - encode file status information into tag
+ *   smlSetVerbose - set/clear verbose mode for debugging output
+ *   smlSetParam - Set parameter value in tag object
+ *   smlSetParamF - Set parameter value in tag object
+ *   smlWriteTagToDs - Write an XML representation of a tag to a datastream
+ *   smlWriteTagToFd - Write an XML representation of a tag to an open file
+ *		descriptor
+ *   smlWriteTagToFile - Write an XML representation of a tag to a file
+ */
+
+/*
+ * Unix includes
+ */
+
+#include <locale.h>
+#include <signal.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <libintl.h>
+#include <stdarg.h>
+#include <string.h>
+#include <unistd.h>
+#include <sys/statvfs.h>
+#include <errno.h>
+#include <assert.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+#include <limits.h>
+#include <strings.h>
+
+/*
+ * liblu Includes
+ */
+
+#include "libinst.h"
+#include "messages.h"
+
+/* Should be defined by cc -D */
+#if	!defined(TEXT_DOMAIN)
+#define	TEXT_DOMAIN "SYS_TEST"
+#endif
+
+/*
+ * Private Method Forward Declarations
+ */
+
+/*PRINTFLIKE2*/
+static void	_smlLogMsg(LogMsgType a_type, const char *a_format, ...);
+
+static int	_smlReadTag(SML_TAG **r_tag, char **a_str, char *parent);
+
+static int	_smlWriteSimpleTag(char **a_str,
+				SML_TAG *tag);
+
+static int	_smlWriteParamValue(char **a_str, char *value);
+
+static void		_smlFreeTag(SML_TAG *tag);
+
+static char		*_sml_fileStatInfoTag = "File-Stat-Info";
+
+static boolean_t	verbose = B_FALSE;
+
+/*
+ *
+ * This definition controls the maximum size of any individual sml
+ * component, such as a tag name, tag *value*, etc. The code should
+ * someday be revised to dynamically allocate whatever memory is needed
+ * to hold such components while parsing, but that exercise is left for
+ * another day. Any component that exceeds this length is silently
+ * truncated...
+ */
+
+#define	MAX_SML_COMPONENT_LENGTH	16384
+
+/*
+ * Public Methods
+ */
+
+/*
+ * Name:	smlAddTag
+ * Description:	Add new tag object into existing tag object
+ * Arguments:	r_tag - [RO, *RW] - (SML_TAG **)
+ *			Pointer to handle to the tag object to update
+ *			The handle may be updated if the tag object is
+ *			moved in memory
+ *		a_index - [RO] - (int)
+ *			Add the tag after the "n"th tag in the tag object
+ *			-1 == add the tag to the end of the tag object
+ *			0 == add the tag to the beginning of the tag object
+ *		a_subTag - [RO, *RW] - (SML_TAG *)
+ *			The tag to add to 'tag'
+ * Returns:	SML_TAG *
+ *			The location within "r_tag" where "a_subTag"
+ *			has been added - this is the handle into the r_tag
+ *			object to the tag that was just added
+ * Errors:	If the tag object cannot be updated, the process exits
+ */
+
+SML_TAG *
+smlAddTag(SML_TAG **r_tag, int a_index, SML_TAG *a_subTag)
+{
+	SML_TAG	*tag;
+
+	/* entry assertions */
+
+	assert(SML_TAG__ISVALID(a_subTag));
+	assert(SML_TAG__R_ISVALID(r_tag));
+
+	/* if no tag to update specified, ignore request */
+
+	tag = *r_tag;
+	if (tag == SML_TAG__NULL) {
+		return (tag);
+	}
+
+	/* entry debugging info */
+
+	_smlLogMsg(LOG_MSG_DEBUG, DBG_SML_ADD_TAG,
+		a_subTag->name, tag->name);
+
+	/* if index is out of range or -1, append to tag object */
+
+	if ((a_index > tag->tags_num) || (a_index == -1)) {
+		a_index = tag->tags_num;
+	}
+
+	/* bump number of tags in tag object */
+
+	tag->tags_num++;
+
+	/* expand tag object to hold new subtag */
+
+	tag->tags = (SML_TAG *)realloc(tag->tags,
+		sizeof (SML_TAG) * tag->tags_num);
+
+	/* if not appending, adjust tag object to hold new subtag */
+
+	if (a_index < (tag->tags_num - 1)) {
+		(void) memmove(&(tag->tags[a_index + 1]), &(tag->tags[a_index]),
+			sizeof (SML_TAG) * (tag->tags_num - a_index - 1));
+	}
+
+	/* copy new subtag into correct location in tag object */
+
+	(void) memcpy(&(tag->tags[a_index]), a_subTag,
+		sizeof (SML_TAG));
+
+	return (&(tag->tags[a_index]));
+}
+
+/*
+ * Name:	smlDelTag
+ * Description:	Delete element from tag object
+ * Arguments:	tag - [RO, *RW] - (SML_TAG *)
+ *			The tag object to update
+ *		sub_tag - [RO, *RW] - (SML_TAG *)
+ *			Element to be removed from the tag object
+ * Returns:	void
+ *			The sub_tag is removed from the tag object
+ * NOTE:	The sub-tag and all elements contained within it are deallocated
+ *		the sub-tag is no longer valid when this method returns
+ */
+
+void
+smlDelTag(SML_TAG *tag, SML_TAG *sub_tag)
+{
+	int	index;
+
+	/* entry assertions */
+
+	assert(SML_TAG__ISVALID(sub_tag));
+
+	/* if no tag to update specified, ignore request */
+
+	if (tag == SML_TAG__NULL) {
+		return;
+	}
+
+	/* entry debugging info */
+
+	_smlLogMsg(LOG_MSG_DEBUG, DBG_SML_DEL_TAG,
+		sub_tag->name, tag->name);
+
+	/* if tag object is empty, ignore request */
+
+	if (tag->tags_num == 0) {
+		return;
+	}
+
+	/* determine index into tag object of element to remove */
+	for (index = 0; index < tag->tags_num; index++) {
+		if (sub_tag == &tag->tags[index]) {
+			break;
+		}
+	}
+
+	/* if element not found in tag, ignore request */
+
+	if (index >= tag->tags_num) {
+		return;
+	}
+
+	/* free up the subtag to be deleted */
+
+	_smlFreeTag(sub_tag);
+
+	/*
+	 * if not removing last element, collapse tag object removing
+	 * target element
+	 */
+
+	if (index < (tag->tags_num - 1)) {
+		(void) memmove(&(tag->tags[index]), &(tag->tags[index + 1]),
+			sizeof (SML_TAG) *(tag->tags_num - index - 1));
+	}
+
+	/* one less tag object in tag */
+
+	tag->tags_num --;
+
+	/*
+	 * If only one tag left, then delete entire tag structure
+	 * otherwise reallocate removing unneeded entry
+	 */
+
+	if (tag->tags_num > 0) {
+		/* realloc removing last element in tag object */
+
+		tag->tags = (SML_TAG *)realloc(tag->tags,
+			sizeof (SML_TAG) *tag->tags_num);
+	} else {
+		tag->tags = SML_TAG__NULL;
+	}
+}
+
+/*
+ * Name:	smlFreeTag
+ * Description:	Free a tag object and all its contents when no longer needed
+ * Arguments:	tag - [RO, *RW] - (SML_TAG *)
+ *			The tag object to be deleted
+ * Returns:	void
+ *			The tag object and all its contents are deallocated
+ */
+
+void
+smlFreeTag(SML_TAG *tag)
+{
+	/* entry assertions */
+
+	assert(SML_TAG__ISVALID(tag));
+
+	/* entry debugging info */
+
+	if (tag->name != (char *)NULL) {
+		_smlLogMsg(LOG_MSG_DEBUG, DBG_SML_FREE_TAG,
+			(unsigned long)tag, tag->name);
+	}
+
+	/* free the tag object contents */
+
+	_smlFreeTag(tag);
+
+	/* free the tag object handle */
+
+	bzero(tag, sizeof (SML_TAG));
+	free(tag);
+}
+
+/*
+ * Name:	smlGetNumParams
+ * Synopsis:	Get number of parameters set in tag
+ * Description:	Return the number of parameters set in a tag
+ * Arguments:	a_tag - [RO, *RO] - (SML_TAG *)
+ *			The tag object to obtain the # params from
+ * Returns:	int
+ *			Number of parameters set in tag
+ *			0 = no parameters are set
+ */
+
+int
+smlGetNumParams(SML_TAG *a_tag)
+{
+	return (a_tag ? a_tag->params_num : 0);
+}
+
+
+/*
+ * Name:	smlGetParam_r
+ * Description:	Get a parameter from a tag into a buffer of fixed size
+ * Arguments:	tag - [RO, *RO] - (SML_TAG *)
+ *			The tag object to obtain the parameter from
+ *		name - [RO, *RO] - (char *)
+ *			Name of the parameter to retrieve
+ *		buf - [RO, *RW] - (char *)
+ *			Location of buffer to contain results
+ *		bufLen - [RO, *RO] - (int)
+ *			Maximum bytes available in buffer to contain results
+ * Returns:	void
+ */
+
+void
+smlGetParam_r(SML_TAG *tag, char *name, char *buf, int bufLen)
+{
+	int	k;
+
+	/* entry assertions */
+
+	assert(name != (char *)NULL);
+	assert(*name != '\0');
+	assert(buf != (char *)NULL);
+	assert(bufLen > 0);
+
+	/* terminate the buffer */
+
+	buf[0] = '\0';
+	buf[bufLen-1] = '\0';
+
+	bzero(buf, bufLen);
+
+	/* if no tag specified, return NULL */
+
+	if (tag == SML_TAG__NULL) {
+		return;
+	}
+
+	/* if no parameters in tag, return NULL */
+
+	if (tag->params == NULL) {
+		return;
+	}
+
+	/* entry debugging info */
+
+	_smlLogMsg(LOG_MSG_DEBUG, DBG_SML_GET_PARAM,
+		name, tag->name);
+
+	/* scan tag object looking for specified parameter */
+
+	for (k = 0; k < tag->params_num; k++) {
+		assert(tag->params[k].name != (char *)NULL);
+		assert(tag->params[k].value != (char *)NULL);
+		if (streq(tag->params[k].name, name)) {
+			_smlLogMsg(LOG_MSG_DEBUG,
+				DBG_SML_GOT_PARAM,
+				tag->name, name, tag->params[k].value);
+			(void) strncpy(buf, tag->params[k].value, bufLen-1);
+			return;
+		}
+	}
+
+	/* parameter not found - return */
+}
+
+/*
+ * Name:	smlGetParam
+ * Description:	Get a parameter from a tag
+ * Arguments:	tag - [RO, *RO] - (SML_TAG *)
+ *			The tag object to obtain the parameter from
+ *		name - [RO, *RO] - (char *)
+ *			Name of the parameter to retrieve
+ * Returns:	char *
+ *			Value of the specified parameter
+ *			== (char *)NULL if the parameter does not exist
+ * NOTE:    	Any parameter returned is placed in new storage for the
+ *		calling method. The caller must use 'free' to dispose
+ *		of the storage once the parameter is no longer needed.
+ */
+
+char *
+smlGetParam(SML_TAG *tag, char *name)
+{
+	int	k;
+
+	/* entry assertions */
+
+	assert(name != (char *)NULL);
+	assert(*name != '\0');
+
+	/* entry debugging info */
+
+	_smlLogMsg(LOG_MSG_DEBUG, "get param param <%s>", name);
+
+	/* if no tag specified, return NULL */
+
+	if (tag == SML_TAG__NULL) {
+		return ((char *)NULL);
+	}
+
+	/* if no parameters in tag, return NULL */
+
+	if (tag->params == NULL) {
+		return ((char *)NULL);
+	}
+
+	/* entry debugging info */
+
+	_smlLogMsg(LOG_MSG_DEBUG, DBG_SML_GET_PARAM,
+		name, tag->name);
+
+	/* scan tag object looking for specified parameter */
+
+	for (k = 0; k < tag->params_num; k++) {
+		assert(tag->params[k].name != (char *)NULL);
+		assert(tag->params[k].value != (char *)NULL);
+		if (streq(tag->params[k].name, name)) {
+			_smlLogMsg(LOG_MSG_DEBUG,
+				DBG_SML_GOT_PARAM,
+				tag->name, name, tag->params[k].value);
+			return (strdup(tag->params[k].value));
+		}
+	}
+
+	/* parameter not found - return NULL */
+
+	return ((char *)NULL);
+}
+
+/*
+ * Name:	smlGetParamName
+ * Description:	Get the name of a tag parameter given its index
+ * Arguments:	tag - [RO, *RO] - (SML_TAG *)
+ *			The tag object to obtain the parameter name from
+ *		index - [RO] - (int)
+ *			Index of parameter name to return
+ * Returns:	char *
+ *			Name of 'index'th parameter
+ *			== (char *)NULL if no such parameter exists in tag
+ * NOTE:    	Any parameter name returned is placed in new storage for the
+ *		calling method. The caller must use 'free' to dispose
+ *		of the storage once the parameter name is no longer needed.
+ */
+
+char *
+smlGetParamName(SML_TAG *tag, int index)
+{
+	/* if no tag specified, return NULL */
+
+	if (tag == NULL) {
+		return ((char *)NULL);
+	}
+
+	/* entry debugging info */
+
+	_smlLogMsg(LOG_MSG_DEBUG, DBG_SML_GET_PARAM_NAME,
+		tag->name, index);
+
+	/* if no parameters in tag, return NULL */
+
+	if (tag->params == NULL) {
+		return ((char *)NULL);
+	}
+
+	/* if index not within range, return NULL */
+
+	if (index >= tag->params_num) {
+		return ((char *)NULL);
+	}
+
+	/* index within range - return parameter name */
+
+	assert(tag->params[index].name != (char *)NULL);
+	assert(tag->params[index].value != (char *)NULL);
+
+	_smlLogMsg(LOG_MSG_DEBUG, DBG_SML_GOT_PARAM_NAME,
+		tag->name, index, tag->params[index].name);
+
+	return (strdup(tag->params[index].name));
+}
+
+/*
+ * Name:	smlGetParamByTag
+ * Synopsis:	Get a parameter value from a tag by name and index
+ * Description:	Call to look for a parameter value from a tag with
+ *		a given name with a parameter of a given name
+ * Arguments:	tag - [RO, *RO] - (SML_TAG *)
+ *			The tag object to obtain the parameter
+ *		index - [RO] - (int)
+ *			Index of nth tag by name to look for
+ *		tagName - [RO, *RO] - (char *)
+ *			Name of tag to look for
+ *		paramName - [RO, *RO] - (char *)
+ *			Name of parameter to return value of
+ * Returns:	char *
+ *			== (char *)NULL - no parameter value set
+ *			!= (char *)NULL - value of parameter set
+ */
+
+char *
+smlGetParamByTag(SML_TAG *tag, int index,
+	char *tagName, char *paramName)
+{
+	SML_TAG	*rtag;
+
+	/* entry assertions */
+
+	assert(SML_TAG__ISVALID(tag));
+	assert(tagName != (char *)NULL);
+	assert(*tagName != '\0');
+	assert(paramName != (char *)NULL);
+	assert(*paramName != '\0');
+
+	/* entry debugging info */
+
+	_smlLogMsg(LOG_MSG_DEBUG, DBG_SML_GET_PARAM_BY_TAG,
+		tagName, index, paramName);
+
+	/* find the requested tag by name  and index */
+
+	rtag = smlGetTagByName(tag, index, tagName);
+	if (rtag == SML_TAG__NULL) {
+		return ((char *)NULL);
+	}
+
+	return (smlGetParam(rtag, paramName));
+}
+
+/*
+ * Name:	smlGetTagByTagParam
+ * Synopsis:	Get element given tag name, index, parameter name, and value
+ * Description:	Call to look for a tag with a given nae, that has a parameter
+ *		of a given name with a specified value
+ * Arguments:	tag - [RO, *RO] - (SML_TAG *)
+ *			The tag object to obtain the element from
+ *		index - [RO] - (int)
+ *			Index of nth name to return
+ *		tagName - [RO, *RO] - (char *)
+ *			Tag name to look up
+ *		paramName - [RO, *RO] - (char *)
+ *			Parameter name to look up
+ *		paramValue - [RO, *RO] - (char *)
+ *			Parameter value to match
+ * Returns:	SML_TAG *
+ *			The 'index'th occurance of element 'name' with
+ *			a parameter 'name' with value specified
+ *			== SML_TAG__NULL if no such element exists
+ */
+
+SML_TAG *
+smlGetTagByTagParam(SML_TAG *tag, int index,
+	char *tagName, char *paramName, char *paramValue)
+{
+	int		ti;		/* tag structure index */
+
+	/* entry assertions */
+
+	assert(SML_TAG__ISVALID(tag));
+	assert(tagName != (char *)NULL);
+	assert(*tagName != '\0');
+	assert(paramName != (char *)NULL);
+	assert(*paramName != '\0');
+	assert(paramValue != (char *)NULL);
+	assert(*paramValue != '\0');
+
+	/* if tag has no elements, return NULL */
+
+	if (tag->tags == NULL) {
+		return (SML_TAG__NULL);
+	}
+
+	/*
+	 * Search algorithm:
+	 *  -> search tag structure; for each tag with element == "tagName":
+	 *  -> search tag parameters; if parameter name == "paramName"
+	 *  -> if parameter value != "paramValue"; to next tag
+	 *  -> if parameter value == "paramValue":
+	 *  -> if not the "index"th paramValue found; to next tag
+	 *  -> return tag found
+	 */
+
+	for (ti = 0; ti < tag->tags_num; ti++) {
+		int	pi;	/* parameter structure index */
+
+		/* if tag element does not match, go on to next tag */
+
+		if (strcmp(tag->tags[ti].name, tagName)) {
+			continue;
+		}
+
+		/* element matches: search for specified parameter name/value */
+
+		for (pi = 0; pi < tag->tags[ti].params_num; pi++) {
+			assert(tag->tags[ti].params[pi].name != (char *)NULL);
+			assert(tag->tags[ti].params[pi].value != (char *)NULL);
+
+			/* if parameter name doesnt match to next parameter */
+
+			if (strcmp(tag->tags[ti].params[pi].name, paramName)) {
+				continue;
+			}
+
+			/* if parameter value doesnt match to next tag */
+
+			if (strcmp(tag->tags[ti].params[pi].value,
+				paramValue)) {
+				break;
+			}
+
+			/*
+			 * found element/paramname/paramvalue:
+			 * -> if this is not the 'index'th one, go to next tag
+			 */
+
+			if (index-- != 0) {
+				break;
+			}
+
+			/*
+			 * found specified element/paramname/paramvalue:
+			 * -> return the tag found
+			 */
+
+			return (&tag->tags[ti]);
+		}
+
+	}
+
+	/* no such element found - return NULL */
+
+	return (SML_TAG__NULL);
+}
+
+/*
+ * Name:	smlGetParamByTagParam
+ * Synopsis:	Get parameter given tag name, index, parameter name, and value
+ * Description:	Call to return the value of a parameter from a tag of a
+ *		given name, with a parameter of a given name with a
+ *		specified value
+ * Arguments:	tag - [RO, *RO] - (SML_TAG *)
+ *			The tag object to obtain the element from
+ *		index - [RO] - (int)
+ *			Index of nth name to return
+ *		tagName - [RO, *RO] - (char *)
+ *			Tag name to look up
+ *		paramName - [RO, *RO] - (char *)
+ *			Parameter name to look up
+ *		paramValue - [RO, *RO] - (char *)
+ *			Parameter value to match
+ *		paramReturn - [RO, *RO] - (char *)
+ *			Parameter name to return the value of
+ * Returns:	char *
+ *			The value of parameter 'paramReturn' from the
+ *			The 'index'th occurance of element 'name' with
+ *			a parameter 'name' with value specified
+ *			== (char *)NULL if no such parameter exists
+ */
+
+char *
+smlGetParamByTagParam(SML_TAG *tag, int index,
+	char *tagName, char *paramName, char *paramValue, char *paramReturn)
+{
+	int		ti;		/* tag structure index */
+
+	/* entry assertions */
+
+	assert(SML_TAG__ISVALID(tag));
+	assert(tagName != (char *)NULL);
+	assert(*tagName != '\0');
+	assert(paramName != (char *)NULL);
+	assert(*paramName != '\0');
+	assert(paramValue != (char *)NULL);
+	assert(*paramValue != '\0');
+	assert(paramReturn != (char *)NULL);
+	assert(*paramReturn != '\0');
+
+	/* if tag has no elements, return NULL */
+
+	if (tag->tags == NULL) {
+		return ((char *)NULL);
+	}
+
+	/*
+	 * Search algorithm:
+	 *  -> search tag structure; for each tag with element == "tagName":
+	 *  -> search tag parameters; if parameter name == "paramName"
+	 *  -> if parameter value != "paramValue"; to next tag
+	 *  -> if parameter value == "paramValue":
+	 *  -> if not the "index"th paramValue found; to next tag
+	 *  -> return value of "paramReturn"
+	 */
+
+	for (ti = 0; ti < tag->tags_num; ti++) {
+		int	pi;	/* parameter structure index */
+
+		/* if tag element does not match, go on to next tag */
+
+		if (strcmp(tag->tags[ti].name, tagName)) {
+			continue;
+		}
+
+		/* element matches: search for specified parameter name/value */
+
+		for (pi = 0; pi < tag->tags[ti].params_num; pi++) {
+			assert(tag->tags[ti].params[pi].name != (char *)NULL);
+			assert(tag->tags[ti].params[pi].value != (char *)NULL);
+
+			/* if parameter name doesnt match to next parameter */
+
+			if (strcmp(tag->tags[ti].params[pi].name, paramName)) {
+				continue;
+			}
+
+			/* if parameter value doesnt match to next tag */
+
+			if (strcmp(tag->tags[ti].params[pi].value,
+				paramValue)) {
+				break;
+			}
+
+			/*
+			 * found element/paramname/paramvalue:
+			 * -> if this is not the 'index'th one, go to next tag
+			 */
+
+			if (index-- != 0) {
+				break;
+			}
+
+			/*
+			 * found specified element/paramname/paramvalue:
+			 * -> return parameter requested
+			 */
+
+			return (smlGetParam(&tag->tags[ti], paramReturn));
+		}
+
+	}
+
+	/* no such element found - return NULL */
+
+	return ((char *)NULL);
+}
+
+/*
+ * Name:	smlGetElementName
+ * Description:	Return the name of a given tag
+ * Arguments:	a_tag - [RO, *RO] - (SML_TAG *)
+ *			The tag object to obtain the element name from
+ * Returns:	char *
+ *			Value of name of specified tag
+ * NOTE:    	Any name string returned is placed in new storage for the
+ *		calling method. The caller must use 'free' to dispose
+ *		of the storage once the name string is no longer needed.
+ */
+
+char *
+smlGetElementName(SML_TAG *a_tag)
+{
+	/* entry assertions */
+
+	assert(SML_TAG__ISVALID(a_tag));
+	assert(a_tag->name != (char *)NULL);
+	assert(*a_tag->name != '\0');
+
+	/* return the tag name */
+
+	return (strdup(a_tag->name));
+}
+
+/*
+ * Name:	smlGetTag
+ * Description:	Get an element from a tag
+ * Arguments:	tag - [RO, *RO] - (SML_TAG *)
+ *			The tag object to obtain the element from
+ *		index - [RO] - (int)
+ *			Index of element to return
+ * Returns:	SML_TAG *
+ *			The 'index'th element from the specified tag
+ *			== SML_TAG__NULL if no such tag or element
+ */
+
+SML_TAG *
+smlGetTag(SML_TAG *tag, int index)
+{
+	/* if no tag specified, return NULL */
+
+	if (tag == NULL) {
+		return (SML_TAG__NULL);
+	}
+
+	/* if tag has no elements, return NULL */
+
+	if (tag->tags == NULL) {
+		return (SML_TAG__NULL);
+	}
+
+	/* if index not within range, return NULL */
+
+	if (tag->tags_num <= index) {
+		return (SML_TAG__NULL);
+	}
+
+	/* index within range, return element specified */
+
+	assert(SML_TAG__ISVALID(&tag->tags[index]));
+
+	return (&tag->tags[index]);
+}
+
+/*
+ * Name:	smlGetTagByName
+ * Description:	Get an element given a name and an index
+ * Arguments:	tag - [RO, *RO] - (SML_TAG *)
+ *			The tag object to obtain the element from
+ *		index - [RO] - (int)
+ *			Index of nth name to return
+ *		name - [RO, *RO] - (char *)
+ *			Tag name to look up
+ * Returns:	SML_TAG *
+ *			The 'index'th occurance of element 'name'
+ *			== SML_TAG__NULL if no such element exists
+ */
+
+SML_TAG *
+smlGetTagByName(SML_TAG *tag, int index, char *name)
+{
+	int k;
+
+	_smlLogMsg(LOG_MSG_DEBUG, DBG_SML_GET_TAG_BY_NAME, name, index);
+
+	/* if no tag specified, return NULL */
+
+	if (tag == NULL) {
+		return (SML_TAG__NULL);
+	}
+
+	/* if this tag is the one mentioned, return it */
+
+	if (streq(tag->name, name) && (index == 0)) {
+		return (tag);
+	}
+
+	/* if tag has no elements, return NULL */
+
+	if (tag->tags == NULL) {
+		return (SML_TAG__NULL);
+	}
+
+	/* if index out of range, return NULL */
+
+	if (tag->tags_num <= index) {
+		return (SML_TAG__NULL);
+	}
+
+	/* index within range - search for specified element */
+
+	for (k = 0; k < tag->tags_num; k++) {
+		if (streq(tag->tags[k].name, name)) {
+			if (index == 0) {
+				assert(SML_TAG__ISVALID(&tag->tags[k]));
+				return (&tag->tags[k]);
+			} else {
+				index--;
+			}
+		}
+	}
+
+	/* no such element found - return NULL */
+
+	return (SML_TAG__NULL);
+}
+
+/*
+ * Name:	smlConvertStringToTag
+ * Description:	Convert string into tag object
+ * Arguments:	err - [RO, *RW] (LU_ERR)
+ *			Error object - used to contain any errors encountered
+ *			and return those errors to this methods caller
+ *		r_tag - [RW, *RW] - (SML_TAG **)
+ *			Pointer to handle to place new tag object
+ *		str - [RO, *RO] - (char *)
+ *			String object to convert to tag object
+ * Returns:	int
+ *			RESULT_OK - string converted to tag object
+ *			RESULT_ERR - problem converting string to tag object
+ * NOTE:    	Any tag object returned is placed in new storage for the
+ *		calling method. The caller must use 'smlFreeTag' to dispose
+ *		of the storage once the tag object name is no longer needed.
+ */
+
+int
+smlConvertStringToTag(SML_TAG **r_tag, char *str)
+{
+	int	r;
+	SML_TAG	*tag = SML_TAG__NULL;
+	SML_TAG	*tmp_tag;
+
+	/* entry assertions */
+
+	assert(SML_TAG__R_ISVALID(r_tag));
+	assert(str != (char *)NULL);
+	assert(*str != '\0');
+
+	tag = smlNewTag("tagfile");
+
+	for (;;) {
+		r = _smlReadTag(&tmp_tag, &str, NULL);
+		if (r != RESULT_OK) {
+			smlFreeTag(tag);
+			return (r);
+		}
+		if (tmp_tag == SML_TAG__NULL) {
+			if (*str != '\0') {
+				continue;
+			}
+			_smlLogMsg(LOG_MSG_DEBUG,
+				DBG_SML_LOADED_TAGS_FROM_STR,
+				(unsigned long)tag, tag->name);
+			*r_tag = tag;
+			return (RESULT_OK);
+		}
+		_smlLogMsg(LOG_MSG_DEBUG, DBG_SML_READ_IN_TOP_TAG,
+			tmp_tag->name);
+		tag->tags_num++;
+		tag->tags = (SML_TAG *)realloc(tag->tags,
+			sizeof (SML_TAG) *tag->tags_num);
+		(void) memcpy(&(tag->tags[tag->tags_num - 1]), tmp_tag,
+			sizeof (SML_TAG));
+	}
+}
+
+/*
+ * Name:	smlReadOneTag
+ * Description:	read one complete tag from a datastream
+ * Arguments:	err - [RO, *RW] (LU_ERR)
+ *			Error object - used to contain any errors encountered
+ *			and return those errors to this methods caller
+ *		r_tag - [RW, *RW] - (SML_TAG **)
+ *			Pointer to handle to place new tag object
+ *			== SML_TAG__NULL if empty tag found (not an error)
+ *		ds - [RO, *RO] - (LU_DS)
+ *			Handle to datastream to read tag from
+ * Returns:	int
+ *			RESULT_OK - tag successfully read
+ *			RESULT_ERR - problem reading tag
+ * NOTE:    	Any tag object returned is placed in new storage for the
+ *		calling method. The caller must use 'smlFreeTag' to dispose
+ *		of the storage once the tag object name is no longer needed.
+ */
+
+int
+smlReadOneTag(SML_TAG **r_tag, char *a_str)
+{
+	int	r;
+
+	/* entry assertions */
+
+	assert(SML_TAG__R_ISVALID(r_tag));
+	assert(a_str != (char *)NULL);
+
+	/* entry debugging info */
+
+	_smlLogMsg(LOG_MSG_DEBUG, DBG_SML_READ_ONE_TAG, a_str);
+
+	/* reset return tag */
+
+	*r_tag = SML_TAG__NULL;
+
+	/* read tag from datastream, no parent tag to attach it to */
+
+	r = _smlReadTag(r_tag, &a_str, NULL);
+	if (r != RESULT_OK) {
+		_smlLogMsg(LOG_MSG_ERR, ERR_SML_CANNOT_READ_TAG);
+		return (r);
+	}
+
+	if (*r_tag != SML_TAG__NULL) {
+		_smlLogMsg(LOG_MSG_DEBUG, DBG_SML_ONE_TAG_READ,
+			(unsigned long)*r_tag,
+			(*r_tag)->name ? (*r_tag)->name : "<no name>");
+	} else {
+		_smlLogMsg(LOG_MSG_DEBUG, DBG_SML_READ_ONE_TAG_NOTAG);
+	}
+
+	/* exit debugging info */
+
+	return (RESULT_OK);
+}
+
+/*
+ * Name:	smlNewTag
+ * Description:	Create a new (empty) tag object
+ * Arguments:	name - [RO, *RO] - (char *)
+ *			Name of tag; NULL to give the tag no name
+ * Returns:	SML_TAG *
+ *			Tag object created
+ * NOTE:    	Any tag object returned is placed in new storage for the
+ *		calling method. The caller must use 'smlFreeTag' to dispose
+ *		of the storage once the tag object name is no longer needed.
+ * Errors:	If the tag object cannot be created, the process exits
+ */
+
+SML_TAG *
+smlNewTag(char *name)
+{
+	SML_TAG	*tag;
+
+	/* entry assertions */
+
+	assert((name == (char *)NULL) || (*name != '\0'));
+
+	/* entry debugging info */
+
+	_smlLogMsg(LOG_MSG_DEBUG, DBG_SML_CREATE_NEW_TAG_OBJECT,
+		name ? name : "<no name>");
+
+	/* allocate zeroed storage for the tag object */
+
+	tag = (SML_TAG *)calloc(1, sizeof (SML_TAG));
+	assert(tag != SML_TAG__NULL);
+
+	/* if name is provided, duplicate and assign it */
+
+	if (name != (char *)NULL) {
+		tag->name = strdup(name);
+	}
+
+	/* exit assertions */
+
+	assert(SML_TAG__ISVALID(tag));
+
+	/* exit debugging info */
+
+	_smlLogMsg(LOG_MSG_DEBUG, DBG_SML_CREATED_NEW_TAG_OBJECT,
+		(unsigned long)tag, name ? name : "<no name>");
+
+	return (tag);
+}
+
+/*
+ * Name:	smlConvertTagToString
+ * Description:	Convert a tag object into a string representation of the XML
+ * Arguments:	tag - [RO, *RO] - (SML_TAG *)
+ *			The tag object to convert to a string
+ * Returns:	char *
+ *			String representation (in XML) of tag object
+ *			== (char *)NULL if conversion is not possible
+ * NOTE:    	Any string returned is placed in new storage for the
+ *		calling method. The caller must use 'free' to dispose
+ *		of the storage once the string is no longer needed.
+ */
+
+char *
+smlConvertTagToString(SML_TAG *tag)
+{
+	char		*str = (char *)NULL;
+
+	/* entry assertions */
+
+	assert(SML_TAG__ISVALID(tag));
+
+	/* convert the tag object into the datastream */
+
+	(void) _smlWriteSimpleTag(&str, tag);
+
+	assert(str != (char *)NULL);
+	assert(*str != '\0');
+
+	/* return the results */
+
+	return (str);
+}
+
+/*
+ * Name:	smlDbgPrintTag
+ * Synopsis:	Print a representation of an XML tag if debugging
+ * Arguments:	a_tag - [RO, *RO] - (SML_TAG *)
+ *			Pointer to tag structure to dump
+ *		a_format - [RO, RO*] (char *)
+ *			printf-style format for debugging message to be output
+ *		VARG_LIST - [RO] (?)
+ *			arguments as appropriate to 'format' specified
+ * Returns:	void
+ *			If one of the debugging flags is set, the hexdump
+ *			is output.
+ */
+
+/*PRINTFLIKE2*/
+void
+smlDbgPrintTag(SML_TAG *a_tag, char *a_format, ...)
+{
+	va_list	ap;
+	size_t		vres = 0;
+	char		bfr[1];
+	char		*rstr = (char *)NULL;
+
+	/* entry assertions */
+
+	assert(a_format != (char *)NULL);
+	assert(*a_format != '\0');
+	assert(SML_TAG__ISVALID(a_tag));
+
+	/*
+	 * output the message header
+	 */
+
+	/* determine size of the message in bytes */
+
+	va_start(ap, a_format);
+	vres = vsnprintf(bfr, 1, a_format, ap);
+	va_end(ap);
+
+	assert(vres > 0);
+
+	/* allocate storage to hold the message */
+
+	rstr = (char *)calloc(1, vres+2);
+	assert(rstr != (char *)NULL);
+
+	/* generate the results of the printf conversion */
+
+	va_start(ap, a_format);
+	vres = vsnprintf(rstr, vres+1, a_format, ap);
+	va_end(ap);
+
+	assert(vres > 0);
+	assert(*rstr != '\0');
+
+	_smlLogMsg(LOG_MSG_DEBUG, "%s", rstr);
+	free(rstr);
+
+	/* convert the tag into a string to be printed */
+
+	rstr = smlConvertTagToString(a_tag);
+	if (rstr != (char *)NULL) {
+		_smlLogMsg(LOG_MSG_DEBUG, DBG_SML_PRINTTAG, a_tag->name,
+			strlen(rstr), rstr);
+	}
+	free(rstr);
+}
+
+/*
+ * Name:	smlDelParam
+ * Description:	Delete a parameter from a tag object
+ * Arguments:	tag - [RO, *RW] - (SML_TAG *)
+ *			The tag object to delete the parameter from
+ *		name - [RO, *RO] - (char *)
+ *			The parameter to delete from the tag object
+ * Returns:	void
+ *			If the parameter exists, it is deleted from the tag
+ */
+
+void
+smlDelParam(SML_TAG *tag, char *name)
+{
+	int k;
+
+	/* entry assertions */
+
+	assert(SML_TAG__ISVALID(tag));
+	assert(tag->name != (char *)NULL);
+	assert(name != NULL);
+	assert(*name != '\0');
+
+	/* entry debugging info */
+
+	_smlLogMsg(LOG_MSG_DEBUG, DBG_SML_DELETE_PARAM,
+		tag->name, name);
+
+	/* if tag has no parameters, nothing to delete */
+
+	if (tag->params == NULL) {
+		_smlLogMsg(LOG_MSG_DEBUG,
+			DBG_SML_DELETE_PARAM_NO_PARAMS);
+		return;
+	}
+
+	assert(tag->params_num > 0);
+
+	/* search the tag for the parameter */
+
+	for (k = 0; k < tag->params_num; k++) {
+		if (streq(tag->params[k].name, name)) {
+			break;
+		}
+	}
+
+	/* if the parameter was not found, nothing to delete */
+
+	if (k >= tag->params_num) {
+		_smlLogMsg(LOG_MSG_DEBUG,
+			DBG_SML_DELETE_PARAM_NOT_FOUND,
+			name);
+		return;
+	}
+
+	/* parameter found - indicate deleted */
+
+	assert(tag->params[k].name != (char *)NULL);
+	assert(tag->params[k].value != (char *)NULL);
+
+	_smlLogMsg(LOG_MSG_DEBUG,
+		DBG_SML_DELETE_PARAM_FOUND,
+		name, tag->params[k].value);
+
+	/* free up storage fro parameter */
+
+	free(tag->params[k].name);
+	free(tag->params[k].value);
+
+	/* if not at end, compact parameter storage */
+
+	if (k < (tag->params_num -1)) {
+		(void) memmove(&(tag->params[k]), &(tag->params[k + 1]),
+			sizeof (SML_PARAM) *(tag->params_num - k - 1));
+	}
+
+	/* one less parameter object in tag */
+
+	tag->params_num --;
+
+	/*
+	 * If only one parameter left, then delete entire parameter storage,
+	 * otherwise reallocate removing unneeded entry
+	 */
+
+	if (tag->params_num > 0) {
+		/* realloc removing last element in tag object */
+
+		tag->params = (SML_PARAM *)
+			realloc(tag->params,
+			sizeof (SML_PARAM) *tag->params_num);
+	} else {
+		tag->params = (SML_PARAM *)NULL;
+	}
+}
+
+/*
+ * Name:	smlSetParamF
+ * Description:	Set formatted parameter value in tag object
+ * Arguments:	tag - [RO, *RW] - (SML_TAG *)
+ *			The tag object to set the parameter in
+ *		name - [RO, *RO] - (char *)
+ *			The parameter to add to the tag object
+ *		format - [RO, RO*] (char *)
+ *			printf-style format to create parameter value from
+ *		... - [RO] (?)
+ *			arguments as appropriate to 'format' specified
+ * Returns:	void
+ *			The parameter value is set in the tag object
+ *			according to the results of the format string
+ *			and arguments
+ */
+
+/*PRINTFLIKE3*/
+void
+smlSetParamF(SML_TAG *tag, char *name, char *format, ...)
+{
+	va_list	ap;
+	size_t		vres = 0;
+	char		*bfr = NULL;
+	char		fbfr[1];
+
+	/* entry assertions */
+
+	assert(SML_TAG__ISVALID(tag));
+	assert(name != (char *)NULL);
+	assert(*name != '\0');
+	assert(format != NULL);
+	assert(*format != '\0');
+
+	/* determine size of the parameter name in bytes */
+
+	va_start(ap, format);
+	vres = vsnprintf(fbfr, 1, format, ap);
+	va_end(ap);
+
+	assert(vres > 0);
+
+	/* allocate storage to hold the message */
+
+	bfr = (char *)calloc(1, vres+2);
+	assert(bfr != (char *)NULL);
+
+	/* generate the parameter name and store it in the allocated storage */
+
+	va_start(ap, format);
+	vres = vsnprintf(bfr, vres+1, format, ap);
+	va_end(ap);
+
+	assert(vres > 0);
+	assert(*bfr != '\0');
+
+	/* add the parameter to the tag */
+
+	smlSetParam(tag, name, bfr);
+
+	/* free up temporary storage and return */
+
+	free(bfr);
+}
+
+/*
+ * Name:	smlGetParam
+ * Description:	Get a format-generated parameter from a tag
+ * Arguments:	tag - [RO, *RO] - (SML_TAG *)
+ *			The tag object to obtain the parameter from
+ *		format - [RO, RO*] (char *)
+ *			printf-style format for parameter name to be
+ *			looked up to be formatted
+ *		... - [RO] (?)
+ *			arguments as appropriate to 'format' specified
+ * Returns:	char *
+ *			Value of the specified parameter
+ *			== (char *)NULL if the parameter does not exist
+ * NOTE:    	Any parameter returned is placed in new storage for the
+ *		calling method. The caller must use 'free' to dispose
+ *		of the storage once the parameter is no longer needed.
+ */
+
+/*PRINTFLIKE2*/
+char *
+smlGetParamF(SML_TAG *tag, char *format, ...)
+{
+	va_list	ap;
+	size_t		vres = 0;
+	char		*bfr = NULL;
+	char		fbfr[1];
+	char		*p;
+
+	/* entry assertions */
+
+	assert(SML_TAG__ISVALID(tag));
+	assert(format != NULL);
+	assert(*format != '\0');
+
+	/* determine size of the parameter name in bytes */
+
+	va_start(ap, format);
+	vres = vsnprintf(fbfr, 1, format, ap);
+	va_end(ap);
+
+	assert(vres > 0);
+
+	/* allocate storage to hold the message */
+
+	bfr = (char *)calloc(1, vres+2);
+	assert(bfr != (char *)NULL);
+
+	/* generate the parameter name and store it in the allocated storage */
+
+	va_start(ap, format);
+	vres = vsnprintf(bfr, vres+1, format, ap);
+	va_end(ap);
+
+	assert(vres > 0);
+	assert(*bfr != '\0');
+
+	/* add the parameter to the tag */
+
+	p = smlGetParam(tag, bfr);
+
+	/* free up temporary storage and return */
+
+	free(bfr);
+
+	return (p);
+}
+
+/*
+ * Name:	smlSetParam
+ * Description:	Set parameter value in tag object
+ * Arguments:	tag - [RO, *RW] - (SML_TAG *)
+ *			The tag object to set the parameter in
+ *		name - [RO, *RO] - (char *)
+ *			The parameter to add to the tag object
+ *		value - [RO, *RO] - (char *)
+ *			The value of the parameter to set in the tag object
+ * Returns:	void
+ *			The parameter value is set in the tag object
+ */
+
+void
+smlSetParam(SML_TAG *tag, char *name, char *value)
+{
+	SML_PARAM *parameter;
+
+	/* entry assertions */
+
+	assert(SML_TAG__ISVALID(tag));
+	assert(name != (char *)NULL);
+	assert(*name != '\0');
+	assert(value != (char *)NULL);
+
+	/* entry debugging info */
+
+	_smlLogMsg(LOG_MSG_DEBUG, DBG_SML_SET_PARAM,
+		tag->name, name, value);
+
+	/* if parameters exist, see if modifying existing parameter */
+
+	if (tag->params != NULL) {
+		int k;
+		for (k = 0; k < tag->params_num; k++) {
+			assert(tag->params[k].name != (char *)NULL);
+			assert(tag->params[k].value != (char *)NULL);
+
+			/* if name does not match, skip */
+
+			if (!streq(tag->params[k].name, name)) {
+				continue;
+			}
+
+			/* found parameter - if value is same, leave alone */
+
+			if (streq(tag->params[k].value, value)) {
+				_smlLogMsg(LOG_MSG_DEBUG,
+					DBG_SML_SET_PARAM_LEAVE_ALONE,
+					tag->params[k].value);
+				return;
+			}
+
+			/* exists and has different value - change */
+
+			_smlLogMsg(LOG_MSG_DEBUG,
+				DBG_SML_SET_PARAM_MODIFY,
+				tag->params[k].value);
+				free(tag->params[k].value);
+				tag->params[k].value = strdup(value);
+				return;
+		}
+	}
+
+	/* not modifying existing - add new parameter */
+
+	_smlLogMsg(LOG_MSG_DEBUG,
+		DBG_SML_SET_PARAM_CREATE_NEW);
+
+	parameter = (SML_PARAM *)calloc(1, sizeof (SML_PARAM));
+	bzero(parameter, sizeof (SML_PARAM));
+	parameter->name = strdup(name);
+	parameter->value = strdup(value);
+
+	tag->params_num++;
+	tag->params = (SML_PARAM *)realloc(tag->params,
+			sizeof (SML_PARAM) *tag->params_num);
+	(void) memcpy(&(tag->params[tag->params_num - 1]), parameter,
+			sizeof (SML_PARAM));
+	free(parameter);
+}
+
+/*
+ * Name:	smlParamEqF
+ * Description:	Determine if parameter is equal to a specified formatted value
+ * Arguments:	tag - [RO, *RO] - (SML_TAG *)
+ *			The tag object to look for the parameter to compare
+ *		findTag - [RO, *RO] - (char *)
+ *			Tag within tag object to look for the parameter in
+ *		findParam - [RO, *RO] - (char *)
+ *			Parameter within tag to look for
+ *		format - [RO, RO*] (char *)
+ *			printf-style format for value to be compared against
+ *			parameter value
+ *		... - [RO] (?)
+ *			arguments as appropriate to 'format' specified to
+ *			generate the value to compare parameter with
+ * Returns:	boolean_t
+ *			B_TRUE - the parameter exists and matches given value
+ *			B_FALSE - parameter does not exist or does not match
+ */
+
+/*PRINTFLIKE4*/
+boolean_t
+smlParamEqF(SML_TAG *tag, char *findTag, char *findParam, char *format, ...)
+{
+	va_list	ap;
+	size_t		vres = 0;
+	char		*bfr = NULL;
+	char		fbfr[1];
+	boolean_t	b;
+
+	/* entry assertions */
+
+	assert(SML_TAG__ISVALID(tag));
+	assert(format != NULL);
+	assert(*format != '\0');
+
+	/* determine size of the parameter value in bytes */
+
+	va_start(ap, format);
+	vres = vsnprintf(fbfr, 1, format, ap);
+	va_end(ap);
+
+	assert(vres > 0);
+
+	/* allocate storage to hold the message */
+
+	bfr = (char *)calloc(1, vres+2);
+	assert(bfr != (char *)NULL);
+
+	/* generate the parameter value and store it in the allocated storage */
+
+	va_start(ap, format);
+	vres = vsnprintf(bfr, vres+1, format, ap);
+	va_end(ap);
+
+	assert(vres > 0);
+	assert(*bfr != '\0');
+
+	/* add the parameter to the tag */
+
+	b = smlParamEq(tag, findTag, findParam, bfr);
+
+	/* free up temporary storage and return */
+
+	free(bfr);
+
+	return (b);
+}
+
+/*
+ * Name:	smlParamEq
+ * Description:	Determine if parameter is equal to a specified value
+ * Arguments:	tag - [RO, *RO] - (SML_TAG *)
+ *			The tag object to look for the parameter to compare
+ *		findTag - [RO, *RO] - (char *)
+ *			Tag within tag object to look for the parameter in
+ *		findParam - [RO, *RO] - (char *)
+ *			Parameter within tag to look for
+ *		str - [RO, *RO] - (char *)
+ *			Value to compare parameter with
+ * Returns:	boolean_t
+ *			B_TRUE - the parameter exists and matches given value
+ *			B_FALSE - parameter does not exist or does not match
+ */
+
+boolean_t
+smlParamEq(SML_TAG *tag, char *findTag, char *findParam, char *str)
+{
+	SML_TAG	*rtag;
+	char		*rparm;
+	boolean_t	answer;
+
+	/* entry assertions */
+
+	assert(str != (char *)NULL);
+	assert(findParam != (char *)NULL);
+	assert(findTag != (char *)NULL);
+	assert(SML_TAG__ISVALID(tag));
+
+	/* look for the specified tag - if not found, return false */
+
+	rtag = smlGetTagByName(tag, 0, findTag);
+	if (rtag == SML_TAG__NULL) {
+		return (B_FALSE);
+	}
+
+	/* look for the specified parameter - if not found, return false */
+
+	rparm = smlGetParam(rtag, findParam);
+	if (rparm == (char *)NULL) {
+		return (B_FALSE);
+	}
+
+	/* parameter found - compare against given value */
+
+	answer = strcasecmp(str, rparm);
+
+	/* free up parameter storage */
+
+	free(rparm);
+
+	/* return results of comparison */
+
+	return (answer == 0 ? B_TRUE : B_FALSE);
+}
+
+/*
+ * Name:	smlFindAndDelTag
+ * Description:	Delete a tag if found in tag object
+ * Arguments:	tag - [RO, *RW] - (SML_TAG *)
+ *			The tag object to delete the tag from
+ *		findTag - [RO, *RO] - (char *)
+ *			Tag within tag object to delete
+ * Returns:	boolean_t
+ *			B_TRUE - tag found and deleted
+ *			B_FALSE - tag not found
+ */
+
+boolean_t
+smlFindAndDelTag(SML_TAG *tag, char *findTag)
+{
+	SML_TAG	*rtag = SML_TAG__NULL;
+
+	/* entry assertions */
+
+	assert(SML_TAG__ISVALID(tag));
+	assert(findTag != (char *)NULL);
+	assert(*findTag != '\0');
+
+	/* find the specified tag - if not found, return false */
+
+	rtag = smlGetTagByName(tag, 0, findTag);
+	if (rtag == SML_TAG__NULL) {
+		return (B_FALSE);
+	}
+
+	/* tag found - delete it and return true */
+
+	smlDelTag(tag, rtag);
+
+	return (B_TRUE);
+}
+
+/*
+ * Name:	smlDup
+ * Description:	Duplicate a tag object
+ * Arguments:	tag - [RO, *RO] - (SML_TAG *)
+ *			The tag object to duplicate
+ * Returns:	SML_TAG *
+ *			A handle to a complete duplicate of the tag provided
+ * NOTE:    	Any tag object returned is placed in new storage for the
+ *		calling method. The caller must use 'smlFreeTag' to dispose
+ *		of the storage once the tag object name is no longer needed.
+ * Errors:	If the tag object cannot be duplicated, the process exits
+ */
+
+SML_TAG *
+smlDup(SML_TAG *tag)
+{
+	SML_TAG	*rtag = SML_TAG__NULL;
+	int		i;
+
+	/* entry assertions */
+
+	assert(SML_TAG__ISVALID(tag));
+
+	/* allocate zeroed storage for the tag object */
+
+	rtag = (SML_TAG *)calloc(1, sizeof (SML_TAG));
+	assert(rtag != SML_TAG__NULL);
+
+	/* duplicate all parameters of the tag */
+
+	rtag->name = (tag->name ? strdup(tag->name) : (char *)NULL);
+	rtag->params_num = tag->params_num;
+	if (tag->params != (SML_PARAM *)NULL) {
+		rtag->params = (SML_PARAM *)
+			calloc(1, sizeof (SML_PARAM)*rtag->params_num);
+		bzero(rtag->params, sizeof (SML_PARAM)*rtag->params_num);
+		for (i = 0; i < rtag->params_num; i++) {
+			rtag->params[i].name = tag->params[i].name ?
+				strdup(tag->params[i].name) :
+					(char *)NULL;
+			rtag->params[i].value = tag->params[i].value ?
+				strdup(tag->params[i].value) :
+					(char *)NULL;
+		}
+	}
+
+	/* duplicate all elements of the tag */
+
+	rtag->tags_num = tag->tags_num;
+
+	if (tag->tags != SML_TAG__NULL) {
+		rtag->tags = (SML_TAG *)
+			calloc(1, sizeof (SML_TAG)*rtag->tags_num);
+		bzero(rtag->tags, sizeof (SML_TAG)*rtag->tags_num);
+		for (i = 0; i < rtag->tags_num; i++) {
+			SML_TAG *stag;
+			stag = smlDup(&tag->tags[i]);
+			(void) memcpy(&rtag->tags[i], stag,
+				sizeof (SML_TAG));
+			free(stag);
+		}
+	}
+
+	/* exit assertions */
+
+	assert(SML_TAG__ISVALID(rtag));
+
+	/* return */
+
+	return (rtag);
+}
+
+/*
+ * Name:	smlSetFileStatInfo
+ * Description;	Given a file status structure and path name, encode the
+ *		structure and place it and the name into the specified tag
+ *		in a "_sml_fileStatInfoTag" (private) element
+ * Arguments:	tag - [RO, *RO] - (SML_TAG *)
+ *			The tag object to deposit the information into
+ *		statbuf - [RO, *RO] - (struct stat *)
+ *			Pointer to file status structure to encode
+ *		path - [RO, *RO] - (char *)
+ *			Pointer to path name of file to encode
+ * Returns:	void
+ *			The information is placed into the specified tag object
+ */
+
+void
+smlSetFileStatInfo(SML_TAG **tag, struct stat *statbuf, char *path)
+{
+	SML_TAG	*rtag;
+
+	/* entry assertions */
+
+	assert(SML_TAG__R_ISVALID(tag));
+	assert(SML_TAG__ISVALID(*tag));
+	assert(statbuf != (struct stat *)NULL);
+
+	/* if stat info exists, delete it */
+
+	(void) smlFindAndDelTag(*tag, _sml_fileStatInfoTag);
+
+	/* create the file stat info inside of the top level tag */
+
+	assert(smlGetTagByName(*tag, 0, _sml_fileStatInfoTag)
+							== SML_TAG__NULL);
+	rtag = smlNewTag(_sml_fileStatInfoTag);
+	assert(SML_TAG__ISVALID(rtag));
+	(void) smlAddTag(tag, 0, rtag);
+	free(rtag);
+
+	/* obtain handle on newly created file stat info tag */
+
+	rtag = smlGetTagByName(*tag, 0, _sml_fileStatInfoTag);
+	assert(SML_TAG__ISVALID(rtag));
+
+	/* add file info as parameters to the tag */
+
+	if (path != (char *)NULL) {
+		smlSetParam(rtag, "st_path", path);
+	}
+
+	smlSetParamF(rtag, "st_ino", "0x%llx",
+		(unsigned long long)statbuf->st_ino);
+	smlSetParamF(rtag, "st_mode", "0x%llx",
+		(unsigned long long)statbuf->st_mode);
+	smlSetParamF(rtag, "st_mtime", "0x%llx",
+		(unsigned long long)statbuf->st_mtime);
+	smlSetParamF(rtag, "st_ctime", "0x%llx",
+		(unsigned long long)statbuf->st_ctime);
+	smlSetParamF(rtag, "st_size", "0x%llx",
+		(unsigned long long)statbuf->st_size);
+}
+
+/*
+ * Name:	smlFstatCompareEQ
+ * Description:	Given a file status structure and path name, look for the
+ *		information placed into a tag object via smlSetFileStatInfo
+ *		and if present compare the encoded information with the
+ *		arguments provided
+ * Arguments:	statbuf - [RO, *RO] - (struct stat *)
+ *			Pointer to file status structure to compare
+ *		tag - [RO, *RO] - (SML_TAG *)
+ *			The tag object to compare against
+ *		path - [RO, *RO] - (char *)
+ *			Pointer to path name of file to compare
+ * Returns:	boolean_t
+ *			B_TRUE - both status structures are identical
+ *			B_FALSE - the status structures are not equal
+ */
+
+boolean_t
+smlFstatCompareEq(struct stat *statbuf, SML_TAG *tag, char *path)
+{
+	if (tag == SML_TAG__NULL) {
+		return (B_FALSE);
+	}
+
+	assert(SML_TAG__ISVALID(tag));
+
+	if (statbuf == (struct stat *)NULL) {
+		return (B_FALSE);
+	}
+
+	if (path != (char *)NULL) {
+		if (smlParamEq(tag,
+			_sml_fileStatInfoTag, "st_path", path) != B_TRUE) {
+			return (B_FALSE);
+		}
+	}
+
+	if (smlParamEqF(tag, _sml_fileStatInfoTag, "st_ino",
+		"0x%llx", (unsigned long long)statbuf->st_ino) != B_TRUE) {
+		return (B_FALSE);
+	}
+
+	if (smlParamEqF(tag, _sml_fileStatInfoTag, "st_mode",
+		"0x%llx", (unsigned long long)statbuf->st_mode) != B_TRUE) {
+		return (B_FALSE);
+	}
+
+	if (smlParamEqF(tag, _sml_fileStatInfoTag, "st_mtime",
+		"0x%llx", (unsigned long long)statbuf->st_mtime) != B_TRUE) {
+		return (B_FALSE);
+	}
+
+	if (smlParamEqF(tag, _sml_fileStatInfoTag, "st_ctime",
+		"0x%llx", (unsigned long long)statbuf->st_ctime) != B_TRUE) {
+		return (B_FALSE);
+	}
+
+	if (smlParamEqF(tag, _sml_fileStatInfoTag, "st_size",
+		"0x%llx", (unsigned long long)statbuf->st_size) != B_TRUE) {
+		return (B_FALSE);
+	}
+
+	return (B_TRUE);
+}
+
+/*
+ * Name:	set_verbose
+ * Description:	Turns on verbose output
+ * Scope:	public
+ * Arguments:	verbose = B_TRUE indicates verbose mode
+ * Returns:	none
+ */
+void
+smlSetVerbose(boolean_t setting)
+{
+	verbose = setting;
+}
+
+/*
+ * Name:	get_verbose
+ * Description:	Returns whether or not to output verbose messages
+ * Scope:	public
+ * Arguments:	none
+ * Returns:	B_TRUE - verbose messages should be output
+ */
+boolean_t
+smlGetVerbose()
+{
+	return (verbose);
+}
+
+/*
+ * Name:	sml_strPrintf
+ * Synopsis:	Create string from printf style format and arguments
+ * Description:	Call to convert a printf style format and arguments into a
+ *		string of characters placed in allocated storage
+ * Arguments:	format - [RO, RO*] (char *)
+ *			printf-style format for string to be formatted
+ *		... - [RO] (?)
+ *			arguments as appropriate to 'format' specified
+ * Returns:	char *
+ *			A string representing the printf conversion results
+ * NOTE:    	Any string returned is placed in new storage for the
+ *		calling method. The caller must use 'free' to dispose
+ *		of the storage once the string is no longer needed.
+ * Errors:	If the string cannot be created, the process exits
+ */
+
+/*PRINTFLIKE1*/
+char *
+sml_strPrintf(char *a_format, ...)
+{
+	va_list	ap;
+	size_t		vres = 0;
+	char		bfr[1];
+	char		*rstr = (char *)NULL;
+
+	/* entry assertions */
+
+	assert(a_format != (char *)NULL);
+	assert(*a_format != '\0');
+
+	/* determine size of the message in bytes */
+
+	va_start(ap, a_format);
+	vres = vsnprintf(bfr, 1, a_format, ap);
+	va_end(ap);
+
+	assert(vres > 0);
+
+	/* allocate storage to hold the message */
+
+	rstr = (char *)calloc(1, vres+2);
+	assert(rstr != (char *)NULL);
+
+	/* generate the results of the printf conversion */
+
+	va_start(ap, a_format);
+	vres = vsnprintf(rstr, vres+1, a_format, ap);
+	va_end(ap);
+
+	assert(vres > 0);
+	assert(*rstr != '\0');
+
+	/* return the results */
+
+	return (rstr);
+}
+
+/*
+ * Name:	sml_strPrintf_r
+ * Synopsis:	Create string from printf style format and arguments
+ * Description:	Call to convert a printf style format and arguments into a
+ *		string of characters placed in allocated storage
+ * Arguments:	a_buf - [RO, *RW] - (char *)
+ *			- Pointer to buffer used as storage space for the
+ *			  returned string created
+ *		a_bufLen - [RO, *RO] - (int)
+ *			- Size of 'a_buf' in bytes - a maximum of 'a_bufLen-1'
+ *			  bytes will be placed in 'a_buf' - the returned
+ *			  string is always null terminated
+ *		a_format - [RO, RO*] (char *)
+ *			printf-style format for string to be formatted
+ *		VARG_LIST - [RO] (?)
+ *			arguments as appropriate to 'format' specified
+ * Returns:	void
+ */
+
+/*PRINTFLIKE3*/
+void
+sml_strPrintf_r(char *a_buf, int a_bufLen, char *a_format, ...)
+{
+	va_list	ap;
+	size_t		vres = 0;
+
+	/* entry assertions */
+
+	assert(a_format != (char *)NULL);
+	assert(*a_format != '\0');
+	assert(a_buf != (char *)NULL);
+	assert(a_bufLen > 1);
+
+	/* generate the results of the printf conversion */
+
+	va_start(ap, a_format);
+	vres = vsnprintf(a_buf, a_bufLen-1, a_format, ap);
+	va_end(ap);
+
+	assert(vres > 0);
+	assert(vres < a_bufLen);
+
+	a_buf[a_bufLen-1] = '\0';
+}
+
+/*
+ * Name:	sml_XmlEncodeString
+ * Description:	Given a plain text string, convert that string into one that
+ *		encoded using the XML character reference encoding format.
+ * Arguments:	a_plain_text_string	- [RO, *RO] (char *)
+ *			The plain text string to convert (encode)
+ * Returns:	char *
+ *			The encoded form of the plain text string provided
+ * NOTE:    	Any string returned is placed in new storage for the
+ *		calling method. The caller must use 'lu_memFree' to dispose
+ *		of the storage once the string is no longer needed.
+ */
+
+char *
+sml_XmlEncodeString(char *a_plainTextString)
+{
+	char *stringHead;	/* -> start of string containing encoded data */
+	long stringTail;	/* byte pos of first free byte in stringHead */
+	long stringLength;	/* total bytes allocd starting at stringHead */
+	char *p;		/* temp -> to retrieve bytes from src string */
+	long textLength = 0;	/* length of the string to convert */
+
+	/* entry assertions */
+
+	assert(a_plainTextString != (char *)NULL);
+
+	textLength = strlen(a_plainTextString);
+
+	/* Allocate initial string buffer to hold results */
+
+	stringLength = textLength*2;
+	stringTail = 0;
+	stringHead = (char *)calloc(1, (size_t)stringLength+2);
+	assert(stringHead != (char *)NULL);
+
+	/* Add in the encoded message text */
+
+	for (p = a_plainTextString; textLength > 0; p++, textLength--) {
+		/*
+		 * Must have at least 12 bytes: this must be at least the
+		 * maximum number of bytes that can be added for a single
+		 * byte as the last byte of the stream. Assuming the byte
+		 * needs to be encoded, it could be:
+		 * &#xxxxxxxx;\0
+		 * If not that many bytes left, grow the buffer.
+		 */
+
+		if ((stringLength-stringTail) < 12) {
+			stringLength += (textLength*2)+12;
+			stringHead =
+				realloc(stringHead,
+					(size_t)stringLength+2);
+			assert(stringHead != (char *)NULL);
+		}
+
+		/*
+		 * See if this byte is a 'printable 7-bit ascii value'.
+		 * If so just add it to the new string; otherwise, must
+		 * output an XML character value encoding for the byte.
+		 */
+
+		switch (*p) {
+		case '!':
+		case '#':
+		case '%':
+		case '\'':
+		case '(':
+		case ')':
+		case '*':
+		case '+':
+		case ',':
+		case '-':
+		case '.':
+		case '/':
+		case '0':
+		case '1':
+		case '2':
+		case '3':
+		case '4':
+		case '5':
+		case '6':
+		case '7':
+		case '8':
+		case '9':
+		case ':':
+		case ';':
+		case '<':
+		case '=':
+		case '>':
+		case '?':
+		case '@':
+		case 'A':
+		case 'B':
+		case 'C':
+		case 'D':
+		case 'E':
+		case 'F':
+		case 'G':
+		case 'H':
+		case 'I':
+		case 'J':
+		case 'K':
+		case 'L':
+		case 'M':
+		case 'N':
+		case 'O':
+		case 'P':
+		case 'Q':
+		case 'R':
+		case 'S':
+		case 'T':
+		case 'U':
+		case 'V':
+		case 'W':
+		case 'X':
+		case 'Y':
+		case 'Z':
+		case '[':
+		case ']':
+		case '^':
+		case '_':
+		case 'a':
+		case 'b':
+		case 'c':
+		case 'd':
+		case 'e':
+		case 'f':
+		case 'g':
+		case 'h':
+		case 'i':
+		case 'j':
+		case 'k':
+		case 'l':
+		case 'm':
+		case 'n':
+		case 'o':
+		case 'p':
+		case 'q':
+		case 'r':
+		case 's':
+		case 't':
+		case 'u':
+		case 'v':
+		case 'w':
+		case 'x':
+		case 'y':
+		case 'z':
+		case '{':
+		case '|':
+		case '}':
+		case '~':
+		case ' ':
+			/*
+			 * It is a printable 7-bit ascii character:
+			 * just add it to the end of the new string.
+			 */
+
+			stringHead[stringTail++] = *p;
+			break;
+		default:
+			/*
+			 * It is not a printable 7-bit ascii character:
+			 * add it as an xml character value encoding.
+			 */
+
+			stringTail += sprintf(&stringHead[stringTail], "&#%x;",
+					(*p)&0xFF);
+			break;
+		}
+	}
+
+	/* Terminate the new string */
+
+	stringHead[stringTail] = '\0';
+
+	/* realloc the string so it is only as big as it needs to be */
+
+	stringHead = realloc(stringHead, stringTail+1);
+	assert(stringHead != (char *)NULL);
+
+	return (stringHead);
+}
+
+/*
+ * Name:	sml_XmlDecodeString
+ * Description:	Given a string encoded using the XML character reference format,
+ *		convert that string into a plain text (unencoded) string.
+ * Arguments:	a_xml_encoded_string	- [RO, *RO] (char *)
+ *			The XML encoded string to convert to plain text
+ * Returns:	char *
+ *			The unencoded (plain text) form of the encoded string
+ * NOTE:    	Any string returned is placed in new storage for the
+ *		calling method. The caller must use 'lu_memFree' to dispose
+ *		of the storage once the string is no longer needed.
+ */
+
+char *
+sml_XmlDecodeString(char *a_xmlEncodedString)
+{
+	char *s = NULL;		/* -> index into encoded bytes string */
+	char *d = NULL;		/* -> index into decoded bytes string */
+	char *rs = NULL;	/* -> string holding ref bytes allocated */
+	char *ri = NULL;	/* -> index into string holding reference */
+	long textLength = 0;	/* length of encoded string to decode */
+	unsigned long rv = 0;	/* temp to hold scanf results of byte conv */
+	char *i = NULL;		/* temp to hold strchr results */
+	char *stringHead = NULL;	/* -> plain test buffer */
+	ptrdiff_t tmpdiff;
+
+	/*
+	 * A finite state machine is used to convert the xml encoded string
+	 * into plain text. The states of the machine are defined below.
+	 */
+
+	int fsmsState = -1;	/* Finite state machine state */
+#define	fsms_text	0	/* Decoding plain text */
+#define	fsms_seenAmp	1	/* Found & */
+#define	fsms_seenPound	2	/* Found # following & */
+#define	fsms_collect	3	/* Collecting character reference bytes */
+
+	/* entry assertions */
+
+	assert(a_xmlEncodedString != (char *)NULL);
+
+	textLength = strlen(a_xmlEncodedString);
+
+	/*
+	 * Allocate string that can contain the decoded string.
+	 * Since decoding always results in a shorter string (bytes encoded
+	 * using the XML character reference are larger in the encoded form)
+	 * we can allocate a string the same size as the encoded string.
+	 */
+
+	stringHead = (char *)calloc(1, textLength+1);
+	assert(stringHead != (char *)NULL);
+
+	/*
+	 * Convert all bytes.
+	 */
+
+	/* Decoding plain text */
+	fsmsState = fsms_text;
+
+	for (s = a_xmlEncodedString, d = stringHead; textLength > 0;
+		s++, textLength--) {
+		switch (fsmsState) {
+		case fsms_text:	/* Decoding plain text */
+			if (rs != NULL) {
+				free(rs);
+				rs = NULL;
+				ri = NULL;
+			}
+			if (*s == '&') {
+				/* Found & */
+				fsmsState = fsms_seenAmp;
+				continue;
+			}
+			*d++ = *s;
+			continue;
+
+		case fsms_seenAmp:	/* Found & */
+			if (*s == '#') {
+				/* Found # following & */
+				fsmsState = fsms_seenPound;
+				continue;
+			}
+			fsmsState = fsms_text;	/* Decoding plain text */
+			*d++ = '&';
+			*d++ = *s;
+			continue;
+
+		case fsms_seenPound:		/* Found # following & */
+			i = strchr(s, ';');
+			if (i == NULL) {
+				/* Decoding plain text */
+				fsmsState = fsms_text;
+				*d++ = '&';
+				*d++ = '#';
+				*d++ = *s;
+				continue;
+			}
+			tmpdiff = (ptrdiff_t)i - (ptrdiff_t)s;
+			rs = (char *)calloc(1, tmpdiff + 1);
+			assert(rs != (char *)NULL);
+			ri = rs;
+			/* Collecting character reference bytes */
+			fsmsState = fsms_collect;
+
+			/*FALLTHRU*/
+
+		/* Collecting character reference bytes */
+		case fsms_collect:
+			if (*s != ';') {
+				switch (*s) {
+				case '0':
+				case '1':
+				case '2':
+				case '3':
+				case '4':
+				case '5':
+				case '6':
+				case '7':
+				case '8':
+				case '9':
+				case 'a':
+				case 'b':
+				case 'c':
+				case 'd':
+				case 'e':
+				case 'f':
+				case 'A':
+				case 'B':
+				case 'C':
+				case 'D':
+				case 'E':
+				case 'F':
+					*ri++ = *s;
+					break;
+				default:
+					*ri = '\0';
+					*d++ = '&';
+					*d++ = '#';
+					tmpdiff = (ptrdiff_t)ri - (ptrdiff_t)rs;
+					(void) strncpy(d, rs, tmpdiff-1);
+					*d++ = *s;
+					/* Decoding plain text */
+					fsmsState = fsms_text;
+					break;
+				}
+				continue;
+			}
+			*ri = '\0';
+			if (sscanf(rs, "%lx", &rv) != 1) {
+				*d++ = '?';
+			} else {
+				*d++ = (rv & 0xFF);
+			}
+			/* Decoding plain text */
+			fsmsState = fsms_text;
+		}
+	}
+
+	/* Done converting bytes - deallocate reference byte storage */
+
+	free(rs);
+
+	/* terminate the converted (plain text) string */
+
+	*d = '\0';
+
+	/* exit assertions */
+
+	assert(stringHead != (char *)NULL);
+
+	return (stringHead);
+}
+
+/*
+ * Private Methods
+ */
+
+/*
+ * Name:	_smlReadTag
+ * Description:	read complete tag from a datastream
+ * Arguments:	err - [RO, *RW] (LU_ERR)
+ *			Error object - used to contain any errors encountered
+ *			and return those errors to this methods caller
+ *		r_tag - [RW, *RW] - (SML_TAG **)
+ *			Pointer to handle to place new tag object
+ *			== SML_TAG__NULL if empty tag found (not an error)
+ *		ds - [RO, *RO] - (LU_DS)
+ *			Handle to datastream to read tag from
+ *		parent - [RO, *RO] - (char *)
+ *			Name for parent of tag (NONE if top of tag)
+ * Returns:	int
+ *			RESULT_OK - tag successfully read
+ *			RESULT_ERR - problem reading tag
+ * NOTE:    	Any tag object returned is placed in new storage for the
+ *		calling method. The caller must use 'smlFreeTag' to dispose
+ *		of the storage once the tag object name is no longer needed.
+ * Errors:	If the tag object cannot be duplicated, the process exits
+ */
+
+static int
+_smlReadTag(SML_TAG **r_tag, char **a_str, char *parent)
+{
+	int	r;
+	SML_TAG	*tag;
+	SML_TAG	*tmp_tag;
+	char	name[MAX_SML_COMPONENT_LENGTH];
+	int	pos = 0;
+	int	c;
+	char	*p = *a_str;
+
+	/* entry assertions */
+
+	assert(SML_TAG__R_ISVALID(r_tag));
+	assert(a_str != (char **)NULL);
+
+	/* entry debugging info */
+
+	_smlLogMsg(LOG_MSG_DEBUG, DBG_SML_READ_TAG,
+		parent ? parent : "<<TOP TAG>>");
+
+	/* reset return tag */
+
+	*r_tag = SML_TAG__NULL;
+
+	/* allocate zeroed storage for the tag object */
+
+	tag = (SML_TAG *)calloc(1, sizeof (SML_TAG));
+	assert(tag != SML_TAG__NULL);
+
+	/* reset name accumulator storage */
+
+	bzero(name, sizeof (name));
+
+	/* ignore delimters before tag */
+
+	for (;;) {
+		/* read tag character - handle failure/EOF */
+
+		if ((*p == '\0') || ((c = (*p++)) == '\0')) {
+			if (parent == NULL) {
+				_smlLogMsg(LOG_MSG_DEBUG,
+					DBG_SML_READTAG_EXPECTED_EOF,
+					p ? p : "?");
+				smlFreeTag(tag);
+				*a_str = p;
+				return (RESULT_OK);
+			}
+
+			/* EOF in middle of processing tag */
+
+			_smlLogMsg(LOG_MSG_ERR,
+				DBG_SML_READTAG_UNEXPECTED_EOF,
+				p ? p : "?");
+			smlFreeTag(tag);
+			*a_str = p;
+			return (RESULT_ERR);
+		}
+
+		/* if beginning of tag, break out */
+
+		if (c == '<') {
+			break;
+		}
+
+		/* not tag beginning: ignore delimiters if not inside tag yet */
+
+		if (parent == (char *)NULL) {
+			/* ignore delimters */
+
+			if (strchr(" \t", c) != (char *)NULL) {
+				continue;
+			}
+
+			/* on blank lines, return no tag object */
+
+			if (c == '\n') {
+				_smlLogMsg(LOG_MSG_DEBUG,
+					DBG_SML_READTAG_BLANKLINE,
+					p ? p : "?");
+				smlFreeTag(tag);
+				*a_str = p;
+				return (RESULT_OK);
+			}
+
+			/* invalid character before tag start */
+
+			_smlLogMsg(LOG_MSG_ERR, ERR_SML_READTAG_BAD_START_CHAR,
+				c, (unsigned int)c);
+			*a_str = p;
+			return (RESULT_ERR);
+		}
+	}
+
+	/*
+	 * all delimiters have been ignored and opening tag character seen;
+	 * process tag
+	 */
+
+	assert(c == '<');
+
+	c = *p;
+	if (*p != '\0') {
+		p++;
+	}
+
+	/* handle EOF after tag opening character found */
+
+	if (c == '\0') {
+		_smlLogMsg(LOG_MSG_ERR,
+			ERR_SML_EOF_BEFORE_TAG_NAME,
+			parent ? parent : "<<NONE>>");
+		smlFreeTag(tag);
+		*a_str = p;
+		return (RESULT_ERR);
+	}
+
+	/* is this a tag closure? */
+
+	if (c == '/') {
+		_smlLogMsg(LOG_MSG_DEBUG, DBG_SML_START_CLOSE_TAG,
+			parent ? parent : "<<NONE>>");
+
+		for (;;) {
+			/* get next character of tag name */
+
+			c = *p;
+			if (*p != '\0') {
+				p++;
+			}
+
+			/* EOF inside tag name? */
+
+			if (c == '\0') {
+				_smlLogMsg(LOG_MSG_ERR,
+					ERR_SML_READTAG_CLOSE_TAG_EOF,
+					parent ? parent : "<<NONE>>");
+				smlFreeTag(tag);
+				*a_str = p;
+				return (RESULT_ERR);
+			}
+
+			/* tag close: break out of collection loop */
+
+			if (c == '>') {
+				break;
+			}
+
+			/* see if illegal character in tag name */
+
+			/* CSTYLED */
+			if (strchr("/ \t\n\":<?$'\\`!@#%^&*()+=|[]{};,", c)
+				!= NULL) {
+				_smlLogMsg(LOG_MSG_ERR,
+					ERR_SML_READTAG_CLOSE_TAG_ILLCHAR,
+					c, (unsigned int)c, name);
+				smlFreeTag(tag);
+				*a_str = p;
+				return (RESULT_ERR);
+			}
+
+			/* valid character - add to name if room left */
+
+			if (pos < sizeof (name)-1) {
+				name[pos] = (c&0xFF);
+				pos++;
+			}
+
+			assert(pos < sizeof (name));
+		}
+
+		/* close of tag found */
+
+		assert(c == '>');
+
+		/* is the tag empty? If so that's an error */
+
+		if (*name == '\0') {
+			_smlLogMsg(LOG_MSG_ERR,
+				ERR_SML_READTAG_CLOSE_EMPTY_TAG);
+			smlFreeTag(tag);
+			*a_str = p;
+			return (RESULT_ERR);
+		}
+
+		/* if no parent, a close tag outside of any open tag */
+
+		if (parent == (char *)NULL) {
+			_smlLogMsg(LOG_MSG_ERR,
+				ERR_SML_READTAG_CLOSE_NO_PARENT,
+				name);
+			smlFreeTag(tag);
+			*a_str = p;
+			return (RESULT_ERR);
+		}
+
+		/* if not close to current parent, error */
+
+		if (!streq(parent, name)) {
+			_smlLogMsg(LOG_MSG_ERR,
+				ERR_SML_READTAG_CLOSE_WRONG_TAG,
+				name, parent);
+			smlFreeTag(tag);
+			*a_str = p;
+			return (RESULT_ERR);
+		}
+
+		/* close of current tag found - success */
+
+		_smlLogMsg(LOG_MSG_DEBUG, DBG_SML_READTAG_CLOSE_TAG,
+			name);
+		smlFreeTag(tag);
+		*a_str = p;
+		return (RESULT_OK);
+	}
+
+	/* not starting a close tag */
+
+	assert(c != '/');
+	assert(c != '<');
+
+	/* at start of tag - input tag name */
+
+	bzero(name, sizeof (name));
+	pos = 0;
+
+	for (;;) {
+
+		/* EOF inside of tag name? */
+
+		if (c == '\0') {
+			_smlLogMsg(LOG_MSG_ERR,
+				ERR_SML_READTAG_TAG_EOF,
+				name, parent ? parent : "<<NONE>>");
+			smlFreeTag(tag);
+			*a_str = p;
+			return (RESULT_ERR);
+		}
+
+		/* if separator or end of line then tag name collected */
+
+		if (strchr(" >\t\n", c) != NULL) {
+			break;
+		}
+
+		/* see if illegal character in tag name */
+
+		/*CSTYLED*/
+		if (strchr("\":<>?$'\\`!@#%^&*()+=|[]{};,", c) != NULL) {
+			_smlLogMsg(LOG_MSG_ERR,
+				ERR_SML_READTAG_TAG_ILLCHAR,
+				c, (unsigned int)c, name);
+			smlFreeTag(tag);
+			*a_str = p;
+			return (RESULT_ERR);
+		}
+
+		/* close current tag? */
+
+		if (c == '/') {
+			/* get next character of tag name */
+
+			c = *p;
+			if (*p != '\0') {
+				p++;
+			}
+
+			/* tag close not found? */
+
+			if (c != '>') {
+				_smlLogMsg(LOG_MSG_ERR,
+					ERR_SML_READTAG_BADTAG_CLOSE,
+					name, parent ? parent : "<<NONE>>");
+				smlFreeTag(tag);
+				*a_str = p;
+				return (RESULT_ERR);
+			}
+
+			/* is the tag empty? If so that's an error */
+
+			if (*name == '\0') {
+				_smlLogMsg(LOG_MSG_ERR,
+					ERR_SML_READTAG_EMPTY_TAG,
+					parent ? parent : "<<NONE>>");
+				smlFreeTag(tag);
+				*a_str = p;
+				return (RESULT_ERR);
+			}
+
+			/* tag closed */
+
+			_smlLogMsg(LOG_MSG_DEBUG,
+				DBG_SML_READTAG_CLOSED_TAG,
+				name, parent ? parent : "<<NONE>>");
+
+			tag->name = strdup(name);
+			*r_tag = tag;
+			*a_str = p;
+			return (RESULT_OK);
+		}
+
+		/* valid character - add to name if room left */
+
+		if (pos < sizeof (name)-1) {
+			name[pos] = (c&0xFF);
+			pos++;
+		}
+
+		assert(pos < sizeof (name));
+
+		/* get next character to parse */
+
+		c = *p;
+		if (*p != '\0') {
+			p++;
+		}
+	}
+
+	/* have a valid tag name: <tagname */
+
+	_smlLogMsg(LOG_MSG_DEBUG, DBG_SML_HAVE_TAG_NAME,
+		name, parent ? parent : "<<NONE>>");
+
+	assert(*name != '\0');
+
+	/* place tag name inside of tag object */
+
+	tag->name = strdup(name);
+
+	/* clear out name accumulator to get parameters */
+
+	bzero(name, sizeof (name));
+	pos = 0;
+
+	/* input parameters */
+
+	if (c != '>')
+		for (;;) {
+
+		char *pname;
+		char *pvalue;
+		SML_PARAM *parameter;
+
+		/* pass spaces before parameter name */
+
+		for (;;) {
+
+			/* get next character of parameter name */
+
+			c = *p;
+			if (*p != '\0') {
+				p++;
+			}
+
+			/* EOF inside parameter name? */
+
+			if (c == '\0') {
+				_smlLogMsg(LOG_MSG_ERR,
+					ERR_SML_READTAG_PARM_EOF,
+					tag->name,
+					parent ? parent : "<<NONE>>");
+				smlFreeTag(tag);
+				*a_str = p;
+				return (RESULT_ERR);
+			}
+
+			/* if separator/end of line tag parameter collected */
+
+			if (strchr(" \t\n", c) != NULL) {
+				continue;
+			}
+
+			/* see if illegal character in parameter name */
+
+			/*CSTYLED*/
+			if (strchr("\":<?$'\\`!@#%^&*()+=|[]{};,.", c) !=
+				(char *)NULL) {
+				_smlLogMsg(LOG_MSG_ERR,
+					ERR_SML_READTAG_PARMNAME_ILLCHAR,
+					c, (unsigned int)c, name, tag->name,
+					parent ? parent : "<<NONE>>");
+				smlFreeTag(tag);
+				*a_str = p;
+				return (RESULT_ERR);
+			}
+
+			/* tag close found? */
+
+			if (c == '>') {
+				break;
+			}
+
+			/* close tag found ? */
+
+			if (c == '/') {
+				c = *p;
+				if (*p != '\0') {
+					p++;
+				}
+				if (c == '>') {
+					_smlLogMsg(LOG_MSG_DEBUG,
+						DBG_SML_TAG_ONLY,
+						tag->name);
+					*r_tag = tag;
+					*a_str = p;
+					return (RESULT_OK);
+				}
+
+				/* / not followed by > */
+				_smlLogMsg(LOG_MSG_ERR,
+					ERR_SML_READTAG_BADPARMNAME_CLOSE,
+					name, tag->name,
+					parent ? parent : "<<NONE>>");
+				smlFreeTag(tag);
+				*a_str = p;
+				return (RESULT_ERR);
+			}
+
+			/* valid character - add to name if room left */
+
+			if (pos < sizeof (name)-1) {
+				name[pos] = (c&0xFF);
+				pos++;
+			}
+
+			assert(pos < sizeof (name));
+			break;
+		}
+
+		if (c == '>') {
+			break;
+		}
+
+		/* input parameter name */
+
+		for (;;) {
+			c = *p;
+			if (*p != '\0') {
+				p++;
+			}
+
+			/* EOF inside of parameter name? */
+
+			if (c == '\0') {
+				_smlLogMsg(LOG_MSG_ERR,
+					ERR_SML_READTAG_PARM_EOF,
+					tag->name,
+					parent ? parent : "<<NONE>>");
+				smlFreeTag(tag);
+				*a_str = p;
+				return (RESULT_ERR);
+			}
+
+			/*CSTYLED*/
+			if (strchr("\t \n\":<>?$'\\`!@%^*()+|[]{},./", c) != NULL) {
+				_smlLogMsg(LOG_MSG_ERR,
+					ERR_SML_READTAG_PARMNAME_ILLCHAR,
+					c, (unsigned int)c, name, tag->name,
+					parent ? parent : "<<NONE>>");
+				smlFreeTag(tag);
+				*a_str = p;
+				return (RESULT_ERR);
+			}
+
+			/* name - value separator found ? */
+
+			if (c == '=') {
+				break;
+			}
+
+			/* valid character - add to name if room left */
+
+			if (pos < sizeof (name)-1) {
+				name[pos] = (c&0xFF);
+				pos++;
+			}
+
+			assert(pos < sizeof (name));
+		}
+
+		/* is the parameter name empty? If so that's an error */
+
+		if (*name == '\0') {
+			_smlLogMsg(LOG_MSG_ERR,
+				ERR_SML_READTAG_EMPTY_PARMNAME,
+				tag->name, parent ? parent : "<<NONE>>");
+			smlFreeTag(tag);
+			*a_str = p;
+			return (RESULT_ERR);
+		}
+
+		/* have a parameter name */
+
+		_smlLogMsg(LOG_MSG_DEBUG, DBG_SML_HAVE_PARM_NAME,
+			name, tag->name);
+
+		/* duplicate (save) parameter name */
+
+		pname = strdup(name);
+
+		/* clear out name accumulator to get parameters */
+
+		bzero(name, sizeof (name));
+		pos = 0;
+
+		c = *p;
+		if (*p != '\0') {
+			p++;
+		}
+
+		if (c != '"') {
+			_smlLogMsg(LOG_MSG_ERR,
+				ERR_SML_PARM_SEP_BAD,
+				c, (unsigned int)c);
+			free(pname);
+			smlFreeTag(tag);
+			*a_str = p;
+			return (RESULT_ERR);
+		}
+
+		/* input parameter value */
+
+		for (;;) {
+			c = *p;
+			if (*p != '\0') {
+				p++;
+			}
+
+			/* EOF inside of parameter value? */
+
+			if (c == '\0') {
+				_smlLogMsg(LOG_MSG_ERR,
+					ERR_SML_READTAG_PARMVAL_EOF,
+					pname, tag->name,
+					parent ? parent : "<<NONE>>");
+				smlFreeTag(tag);
+				free(pname);
+				*a_str = p;
+				return (RESULT_ERR);
+			}
+
+			/* close of parameter value? */
+
+			if (c == '"') {
+				break;
+			}
+
+			if (strchr("\n", c) != NULL) {
+				_smlLogMsg(LOG_MSG_ERR,
+					ERR_SML_READTAG_PARMVAL_NL,
+					pname, tag->name,
+					parent ? parent : "<<NONE>>");
+				free(pname);
+				smlFreeTag(tag);
+				*a_str = p;
+				return (RESULT_ERR);
+			}
+
+			/* valid character - add to value if room left */
+
+			if (pos < sizeof (name)-1) {
+				name[pos] = (c&0xFF);
+				pos++;
+			}
+
+			assert(pos < sizeof (name));
+		}
+
+		/* got the value */
+
+		_smlLogMsg(LOG_MSG_DEBUG, DBG_SML_HAVE_PARM_VALUE,
+			pname, name, tag->name);
+
+		pvalue = sml_XmlDecodeString(name);
+		bzero(name, sizeof (name));
+		pos = 0;
+
+		parameter = (SML_PARAM *)calloc(1, sizeof (SML_PARAM));
+		bzero(parameter, sizeof (SML_PARAM));
+		parameter->name = pname;
+		parameter->value = pvalue;
+		tag->params_num++;
+		tag->params = (SML_PARAM *)
+			realloc(tag->params,
+				sizeof (SML_PARAM) *tag->params_num);
+		(void) memcpy(&(tag->params[tag->params_num - 1]), parameter,
+			sizeof (SML_PARAM));
+
+		free(parameter);
+		if (c == '>') {
+			break;
+		}
+	}
+
+	/* finished processing this tag element entry */
+
+	_smlLogMsg(LOG_MSG_DEBUG, DBG_SML_TAG_HEAD_DONE,
+		tag->name, parent ? parent : "<<NULL>>");
+
+	tag->tags = NULL;
+
+	while (((r = _smlReadTag(&tmp_tag, &p, tag->name))
+		== RESULT_OK) && (tmp_tag != NULL)) {
+		tag->tags_num++;
+		tag->tags = (SML_TAG *)realloc(tag->tags,
+			sizeof (SML_TAG) *tag->tags_num);
+		(void) memcpy(&(tag->tags[tag->tags_num - 1]), tmp_tag,
+			sizeof (SML_TAG));
+		free(tmp_tag);
+	}
+
+	c = *p;
+	if (*p != '\0') {
+		p++;
+	}
+
+	*r_tag = tag;
+	*a_str = p;
+	return (r);
+}
+
+/*
+ * Name:	_smlWriteParamValue
+ * Description:	XML Encode a plain text parameter value and write to datastream
+ * Arguments:	ds - [RO, *RO] - (LU_DS)
+ *			Handle to datastream to write parameter value to
+ *		value - [RO, *RO] - (char *)
+ *			Parameter value to be encoded and written
+ * Returns:	int
+ *			RESULT_OK - tag successfully read
+ *			RESULT_ERR - problem reading tag
+ */
+
+static int
+_smlWriteParamValue(char **a_str, char *value)
+{
+	char		*ns;
+	char		*p;
+
+	/* entry assertions */
+
+	assert(a_str != (char **)NULL);
+	assert(value != (char *)NULL);
+
+	/* xml encode the plain text string */
+
+	p = sml_XmlEncodeString(value);
+	assert(p != (char *)NULL);
+
+	/* write the xml encoded parameter value to the datastream */
+
+	ns = sml_strPrintf("%s\"%s\"", *a_str ? *a_str : "", p);
+
+	/* free up xml encoded value storage */
+
+	free(p);
+
+	if (ns == NULL) {
+		return (RESULT_ERR);
+	}
+
+	if (*a_str != NULL) {
+		free(*a_str);
+	}
+	*a_str = ns;
+
+	/* return results */
+
+	return (RESULT_OK);
+}
+
+static int
+_smlWriteSimpleTag(char **a_str, SML_TAG *tag)
+{
+	int	r;
+	int 	k;
+	char	*ns;
+	char	*np0;
+	char	*np1;
+
+	if (tag == NULL) {
+		return (RESULT_OK);
+	}
+
+	if (*a_str == NULL) {
+		*a_str = strdup("");
+	}
+
+	if (tag->params_num == 0) {
+		if (tag->tags_num == 0) {
+			ns = sml_strPrintf("%s<%s/>\n", *a_str, tag->name);
+			free(*a_str);
+			*a_str = ns;
+			return (RESULT_OK);
+		} else {
+			ns = sml_strPrintf("%s<%s>\n", *a_str, tag->name);
+			if (ns == NULL) {
+				return (RESULT_ERR);
+			}
+			free(*a_str);
+			*a_str = ns;
+		}
+	} else {
+		ns = sml_strPrintf("%s<%s %s=", *a_str ? *a_str : "", tag->name,
+				tag->params[0].name);
+		if (ns == NULL) {
+			return (RESULT_ERR);
+		}
+		free(*a_str);
+		*a_str = ns;
+
+		np0 = NULL;
+		r = _smlWriteParamValue(&np0, tag->params[0].value);
+		if ((np0 == NULL) || (r != RESULT_OK)) {
+			return (RESULT_ERR);
+		}
+
+		ns = sml_strPrintf("%s%s", *a_str, np0);
+		if (ns == NULL) {
+			return (RESULT_ERR);
+		}
+
+		free(np0);
+		free(*a_str);
+		*a_str = ns;
+
+		for (k = 1; k < tag->params_num; k++) {
+			np0 = sml_strPrintf(" %s=", tag->params[k].name);
+			if (np0 == NULL) {
+				return (RESULT_ERR);
+			}
+			np1 = NULL;
+			r = _smlWriteParamValue(&np1, tag->params[k].value);
+			if ((np1 == NULL) || (r != RESULT_OK)) {
+				return (RESULT_ERR);
+			}
+
+			ns = sml_strPrintf("%s%s%s", *a_str, np0, np1);
+			if (ns == NULL) {
+				return (RESULT_ERR);
+			}
+
+			free(np0);
+			free(np1);
+			free(*a_str);
+			*a_str = ns;
+		}
+
+		if (tag->tags_num == 0) {
+			np0 = sml_strPrintf("/>\n");
+			if (np0 == NULL) {
+				return (RESULT_ERR);
+			}
+			ns = sml_strPrintf("%s%s", *a_str, np0);
+			if (ns == NULL) {
+				return (RESULT_ERR);
+			}
+			free(np0);
+			free(*a_str);
+			*a_str = ns;
+		} else {
+			np0 = sml_strPrintf(">\n");
+			if (np0 == NULL) {
+				return (RESULT_ERR);
+			}
+			ns = sml_strPrintf("%s%s", *a_str, np0);
+			if (ns == NULL) {
+				return (RESULT_ERR);
+			}
+			free(np0);
+			free(*a_str);
+			*a_str = ns;
+		}
+	}
+
+	for (k = 0; k < tag->tags_num; k++) {
+		r = _smlWriteSimpleTag(a_str, &(tag->tags[k]));
+		if (r != RESULT_OK) {
+			return (r);
+		}
+	}
+
+	if (tag->tags_num > 0) {
+		np0 = sml_strPrintf("</%s>\n", tag->name);
+		if (np0 == NULL) {
+			return (RESULT_ERR);
+		}
+		ns = sml_strPrintf("%s%s", *a_str ? *a_str : "", np0);
+		if (ns == NULL) {
+			return (RESULT_ERR);
+		}
+		free(np0);
+		free(*a_str);
+		*a_str = ns;
+	}
+
+	return (RESULT_OK);
+}
+
+static void
+_smlFreeTag(SML_TAG *tag)
+{
+	int k;
+
+	/* entry assertions */
+
+	assert(tag != SML_TAG__NULL);
+
+	/* entry debugging info */
+
+	_smlLogMsg(LOG_MSG_DEBUG, DBG_SML_INT_FREE_TAG,
+		(unsigned long)tag,
+		tag->name ? tag->name : "<<NONE>>",
+		tag->params_num, tag->tags_num);
+
+	for (k = 0; k < tag->params_num; k++) {
+		_smlLogMsg(LOG_MSG_DEBUG, DBG_SML_INT_FREE_PARAM_NAME,
+			(unsigned long)(&tag->params[k]),
+			(unsigned long)(tag->params[k].name),
+			tag->params[k].name);
+		free(tag->params[k].name);
+		tag->params[k].name = (char *)NULL;
+		_smlLogMsg(LOG_MSG_DEBUG, DBG_SML_INT_FREE_PARAM_VALUE,
+			(unsigned long)(&tag->params[k]),
+			(unsigned long)(tag->params[k].value),
+			tag->params[k].value);
+		free(tag->params[k].value);
+		tag->params[k].value = (char *)NULL;
+	}
+
+	for (k = 0; k < tag->tags_num; k++) {
+		_smlFreeTag(&tag->tags[k]);
+	}
+
+	if (tag->name != NULL) {
+		_smlLogMsg(LOG_MSG_DEBUG, DBG_SML_INT_FREE_TAG_NAME,
+			(unsigned long)tag->name, tag->name);
+		free(tag->name);
+		tag->name = NULL;
+	}
+
+
+	if (tag->params != NULL) {
+		assert(tag->params_num > 0);
+		bzero(tag->params, sizeof (SML_PARAM)*tag->params_num);
+		_smlLogMsg(LOG_MSG_DEBUG, DBG_SML_INT_FREE_PARAMS,
+					(unsigned long)tag->params);
+		free(tag->params);
+		tag->params = NULL;
+		tag->params_num = 0;
+	}
+
+	if (tag->tags != NULL) {
+		assert(tag->tags_num > 0);
+		bzero(tag->tags, sizeof (SML_TAG)*tag->tags_num);
+		_smlLogMsg(LOG_MSG_DEBUG, DBG_SML_INT_FREE_TAGS,
+			(unsigned long)tag->tags);
+		free(tag->tags);
+		tag->tags = NULL;
+		tag->tags_num = 0;
+	}
+}
+
+/*
+ * Name:	log_msg
+ * Description:	Outputs messages to logging facility.
+ * Scope:	public
+ * Arguments:	type - the severity of the message
+ *		out - where to output the message.
+ *		fmt - the printf format, plus its arguments
+ * Returns:	none
+ */
+
+/*PRINTFLIKE2*/
+static void
+_smlLogMsg(LogMsgType a_type, const char *a_format, ...)
+{
+	va_list	ap;
+	size_t		vres = 0;
+	char		bfr[1];
+	char		*rstr = (char *)NULL;
+	FILE	*out;
+	char	*prefix;
+
+	switch (a_type) {
+	case LOG_MSG_ERR:
+	default:
+		out = stderr;
+		prefix = MSG_LOG_ERROR;
+		break;
+	case LOG_MSG_WRN:
+		out = stderr;
+		prefix = MSG_LOG_WARNING;
+		break;
+	case LOG_MSG_INFO:
+		out = stdout;
+		prefix = NULL;
+		break;
+	case LOG_MSG_DEBUG:
+		if (!smlGetVerbose()) {
+			/* no debug messages if not verbose mode */
+			return;
+		}
+		out = stderr;
+		prefix = MSG_LOG_DEBUG;
+		break;
+	}
+
+	if (prefix != NULL) {
+		(void) fprintf(out, "%s: ", prefix);
+	}
+
+	/* determine size of the message in bytes */
+
+	va_start(ap, a_format);
+	vres = vsnprintf(bfr, 1, a_format, ap);
+	va_end(ap);
+
+	/* allocate storage to hold the message */
+
+	rstr = (char *)malloc(vres+2);
+
+	/* generate the results of the printf conversion */
+
+	va_start(ap, a_format);
+	vres = vsnprintf(rstr, vres+1, a_format, ap);
+	va_end(ap);
+
+	if (fprintf(out, "%s\n", rstr) < 0) {
+		/*
+		 * nothing output, try stderr as a
+		 * last resort
+		 */
+		(void) fprintf(stderr, ERR_LOG_FAIL, a_format);
+	}
+
+	free(rstr);
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/usr/src/cmd/svr4pkg/libinst/srcpath.c	Fri Jun 05 10:28:40 2009 -0400
@@ -0,0 +1,69 @@
+/*
+ * 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
+ * or http://www.opensolaris.org/os/licensing.
+ * 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 2007 Sun Microsystems, Inc.  All rights reserved.
+ * Use is subject to license terms.
+ */
+
+/* Copyright (c) 1984, 1986, 1987, 1988, 1989 AT&T */
+/* All Rights Reserved */
+
+
+
+#include <limits.h>
+#include <string.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <fcntl.h>
+
+char *
+srcpath(char *dir, char *src, int part, int nparts)
+{
+	static char tmppath[PATH_MAX];
+	char	*copy;
+	size_t	copyLen;
+
+	copy = tmppath;
+
+	if (dir != NULL) {
+		size_t theLen = strlen(dir);
+
+		(void) strcpy(copy, dir);
+		copy += theLen;
+		copyLen = (sizeof (tmppath) - theLen);
+	} else {
+		copy[0] = '\0';
+		copyLen = sizeof (tmppath);
+	}
+
+	if (nparts > 1) {
+		(void) snprintf(copy, copyLen,
+			((src[0] == '/') ? "/root.%d%s" : "/reloc.%d/%s"),
+			part, src);
+	} else {
+		(void) snprintf(copy, copyLen,
+			((src[0] == '/') ? "/root%s" : "/reloc/%s"), src);
+	}
+
+	return (tmppath);
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/usr/src/cmd/svr4pkg/libinst/stub.c	Fri Jun 05 10:28:40 2009 -0400
@@ -0,0 +1,46 @@
+/*
+ * 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
+ * or http://www.opensolaris.org/os/licensing.
+ * 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 1993 Sun Microsystems, Inc.  All rights reserved.
+ * Use is subject to license terms.
+ */
+
+/* Copyright (c) 1984, 1986, 1987, 1988, 1989 AT&T */
+/* All Rights Reserved */
+
+
+
+#ifdef PRESVR4
+int
+rename(char *x, char *y)
+{
+	return (link(x, y) || unlink(x));
+}
+#else
+static int dummy;  /* used to make compillor warning go away */
+#ifdef lint
+_______a()
+{
+	return (dummy++);
+}
+#endif	/* lint */
+#endif
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/usr/src/cmd/svr4pkg/libinst/unpack_package_from_stream.c	Fri Jun 05 10:28:40 2009 -0400
@@ -0,0 +1,158 @@
+/*
+ * 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
+ * or http://www.opensolaris.org/os/licensing.
+ * 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 2007 Sun Microsystems, Inc.  All rights reserved.
+ * Use is subject to license terms.
+ */
+
+
+/*
+ * System includes
+ */
+
+#include <stdio.h>
+#include <time.h>
+#include <wait.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <ulimit.h>
+#include <sys/stat.h>
+#include <sys/statvfs.h>
+#include <assert.h>
+#include <fcntl.h>
+#include <errno.h>
+#include <ctype.h>
+#include <dirent.h>
+#include <string.h>
+#include <signal.h>
+#include <locale.h>
+#include <libintl.h>
+#include <pkgstrct.h>
+#include <pkginfo.h>
+#include <pkgdev.h>
+#include <pkglocs.h>
+#include <pwd.h>
+
+
+/*
+ * consolidation pkg command library includes
+ */
+
+#include <pkglib.h>
+
+/*
+ * local pkg command library includes
+ */
+
+#include "libinst.h"
+#include "libadm.h"
+#include "messages.h"
+
+/*
+ * Name:	unpack_package_from_stream
+ * Description:	unpack a package from a stream into a temporary directory
+ * Arguments:	a_idsName - pointer to string representing the input data
+ *			stream containing the package to unpack
+ *		a_pkginst - pointer to string representing the name of
+ *			the package to unpack from the specified stream
+ *		a_tempDir - pointer to string representing the path to a
+ *			directory into which the package will be unpacked
+ * Returns:	boolean_t
+ *			== B_TRUE - package successfully unpacked from stream
+ *			== B_FALSE - failed to unpack package from stream
+ */
+
+boolean_t
+unpack_package_from_stream(char *a_idsName, char *a_pkginst, char *a_tempDir)
+{
+	int		dparts;
+	char		instdir[PATH_MAX];
+
+	/* entry assertions */
+
+	assert(a_idsName != (char *)NULL);
+	assert(a_pkginst != (char *)NULL);
+	assert(a_tempDir != (char *)NULL);
+
+	/* entry debug information */
+
+	echoDebug(DBG_UNPACKSTRM_ENTRY);
+	echoDebug(DBG_UNPACKSTRM_ARGS, a_pkginst, a_idsName, a_tempDir);
+
+	/* find the specified package in the datastream */
+
+	dparts = ds_findpkg(a_idsName, a_pkginst);
+	if (dparts < 1) {
+		progerr(gettext(ERR_DSARCH), a_pkginst);
+		return (B_FALSE);
+		/*NOTREACHED*/
+	}
+
+	/*
+	 * read in next part from stream, even if we decide
+	 * later that we don't need it
+	 */
+
+	/* create directory to hold this package instance */
+
+	if (snprintf(instdir, sizeof (instdir), "%s/%s", a_tempDir, a_pkginst)
+	    >= PATH_MAX) {
+		progerr(ERR_CREATE_PATH_2, a_tempDir, a_pkginst);
+		return (B_FALSE);
+	}
+
+	switch (fmkdir(instdir, 0755)) {
+	case 0:	/* directory created */
+		break;
+	case 1: /* could not remove existing non-directory node */
+		progerr(ERR_REMOVE, instdir, strerror(errno));
+		return (B_FALSE);
+	case 2: /* could not create specified new directory */
+	default:
+		progerr(ERR_UNPACK_FMKDIR, instdir, strerror(errno));
+		return (B_FALSE);
+	}
+
+	/* unpack package instance from stream to dir created */
+
+	echoDebug(DBG_UNPACKSTRM_UNPACKING, a_pkginst, a_idsName, instdir);
+
+	if (chdir(instdir)) {
+		progerr(ERR_CHDIR, instdir);
+		return (B_FALSE);
+	}
+
+	while (dparts--) {
+		if (ds_next(a_idsName, instdir)) {
+			progerr(ERR_UNPACK_DSREAD, dparts+1, a_idsName, instdir,
+				a_pkginst);
+			return (B_FALSE);
+		}
+	}
+
+	if (chdir(get_PKGADM())) {
+		progerr(gettext(ERR_CHDIR), get_PKGADM());
+		return (B_FALSE);
+	}
+
+	return (B_TRUE);
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/usr/src/cmd/svr4pkg/pkgadd/Makefile	Fri Jun 05 10:28:40 2009 -0400
@@ -0,0 +1,51 @@
+#
+# 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
+# or http://www.opensolaris.org/os/licensing.
+# 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 2009 Sun Microsystems, Inc.  All rights reserved.
+# Use is subject to license terms.
+#
+
+PROG=		pkgadd
+
+OBJS=		check.o		\
+		main.o		\
+		presvr4.o	\
+		quit.o
+
+ROOTLINKS=   $(ROOTUSRSBIN)/pkgask
+
+include $(SRC)/cmd/svr4pkg/Makefile.svr4pkg
+
+LDLIBS	+=	-lpkg -linstzones -ladm
+LDLIBS	+=	-lcrypto -lwanboot
+
+.KEEP_STATE:
+
+all: $(PROG)
+
+install: all $(ROOTUSRSBINPROG) $(ROOTLINKS)
+
+$(ROOTLINKS): $(ROOTUSRSBINPROG)
+	$(RM) $@
+	$(LN) $(ROOTUSRSBINPROG) $@
+
+include $(SRC)/cmd/svr4pkg/Makefile.svr4pkg.targ
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/usr/src/cmd/svr4pkg/pkgadd/check.c	Fri Jun 05 10:28:40 2009 -0400
@@ -0,0 +1,1029 @@
+/*
+ * 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
+ * or http://www.opensolaris.org/os/licensing.
+ * 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 2009 Sun Microsystems, Inc.  All rights reserved.
+ * Use is subject to license terms.
+ */
+
+#include <stdio.h>
+#include <limits.h>
+#include <string.h>
+#include <stdlib.h>
+#include <sys/stat.h>
+#include <unistd.h>
+#include <errno.h>
+#include <dirent.h>
+#include <sys/types.h>
+#include <locale.h>
+#include <libintl.h>
+#include <pkgstrct.h>
+#include <pkglocs.h>
+#include <assert.h>
+
+#include <instzones_api.h>
+#include <pkglib.h>
+#include <messages.h>
+
+#include <install.h>
+#include <libinst.h>
+#include <libadm.h>
+
+extern int	npkgs;	/* the number of packages yet to be installed */
+
+/*
+ * ckquit is a global that controls 'ckyorn' (defined in libadm)
+ * If ckquit is non-zero, then "quit" is allowed as an answer when
+ * ckyorn is called. If is it zero, then "quit" is not an allowed answer.
+ */
+extern int	ckquit;
+
+extern struct admin adm;
+
+/*
+ * each one of these represents a single kind of dependency check
+ */
+
+static depckError_t er_ckconflict = {0, (depckErrorRecord_t *)NULL};
+static depckError_t er_ckdepend = {0, (depckErrorRecord_t *)NULL};
+static depckError_t er_ckcfcontent = {0, (depckErrorRecord_t *)NULL};
+static depckError_t er_ckinstance = {0, (depckErrorRecord_t *)NULL};
+static depckError_t er_ckdirs = {0, (depckErrorRecord_t *)NULL};
+static depckError_t er_ckpartinst = {0, (depckErrorRecord_t *)NULL};
+static depckError_t er_ckpartrem = {0, (depckErrorRecord_t *)NULL};
+static depckError_t er_ckpkgdirs = {0, (depckErrorRecord_t *)NULL};
+static depckError_t er_ckpkgfilebad = {0, (depckErrorRecord_t *)NULL};
+static depckError_t er_ckpkgfiles = {0, (depckErrorRecord_t *)NULL};
+static depckError_t er_ckpriv = {0, (depckErrorRecord_t *)NULL};
+static depckError_t er_ckrunlevel = {0, (depckErrorRecord_t *)NULL};
+static depckError_t er_cksetuid = {0, (depckErrorRecord_t *)NULL};
+static depckError_t er_ckspace = {0, (depckErrorRecord_t *)NULL};
+static depckError_t er_newonly = {0, (depckErrorRecord_t *)NULL};
+static depckError_t er_prereqinc = {0, (depckErrorRecord_t *)NULL};
+static depckError_t er_prereqinst = {0, (depckErrorRecord_t *)NULL};
+static depckError_t er_runlevel = {0, (depckErrorRecord_t *)NULL};
+static depckError_t er_same = {0, (depckErrorRecord_t *)NULL};
+static depckError_t er_overwrite = {0, (depckErrorRecord_t *)NULL};
+static depckError_t er_uniq1 = {0, (depckErrorRecord_t *)NULL};
+static depckError_t er_attrib = {0, NULL};
+static depckError_t er_setuidf = {0, NULL};
+static depckError_t er_setgidf = {0, NULL};
+static depckError_t er_overwr = {0, NULL};
+
+/*
+ * each one of these represents a localized message for a single kind
+ * of dependency check
+ */
+
+static char *IMSG_ABADFILE = (char *)NULL;
+static char *IMSG_BADFILE = (char *)NULL;
+static char *IMSG_CKRUNLVL = (char *)NULL;
+static char *IMSG_CNFFAILED = (char *)NULL;
+static char *IMSG_DEPEND = (char *)NULL;
+static char *IMSG_CFCONTENT = (char *)NULL;
+static char *IMSG_INSTANCE = "INSTANCE %s <%s> on %s <%s>";
+static char *IMSG_DIRS  = (char *)NULL;
+static char *IMSG_NEWONLY = (char *)NULL;
+static char *IMSG_PARTINST = (char *)NULL;
+static char *IMSG_PARTREM = (char *)NULL;
+static char *IMSG_PKGDIRS = (char *)NULL;
+static char *IMSG_PRENCI  = (char *)NULL;
+static char *IMSG_PREREQ  = (char *)NULL;
+static char *IMSG_PRIV = (char *)NULL;
+static char *IMSG_RUNLEVEL = (char *)NULL;
+static char *IMSG_SAME = (char *)NULL;
+static char *IMSG_OVERWRITE = (char *)NULL;
+static char *IMSG_UNIQ1 = (char *)NULL;
+static char *IMSG_SETUID = (char *)NULL;
+static char *IMSG_SPCFAILED = (char *)NULL;
+static char *IMSG_ATTRIB;
+static char *IMSG_SETUIDF;
+static char *IMSG_SETGIDF;
+static char *IMSG_OVERWR;
+
+/*
+ * each one of these represents a function to handle a single kind of
+ * dependency check
+ */
+
+static int ckconflict(char *a_msg, char *a_pkg);
+static int ckdepend(char *a_msg, char *a_pkg);
+static int ckcfcontent(char *a_msg, char *a_pkg);
+static int ckinstance(char *a_msg, char *a_pkg);
+static int ckdirs(char *a_msg, char *a_pkg);
+static int ckpartinst(char *a_msg, char *a_pkg);
+static int ckpartrem(char *a_msg, char *a_pkg);
+static int ckpkgfilebad(char *a_msg, char *a_pkg);
+static int ckpkgdirs(char *a_msg, char *a_pkg);
+static int ckpkgfiles(char *a_msg, char *a_pkg);
+static int ckprereqinc(char *a_msg, char *a_pkg);
+static int ckprereqinst(char *a_msg, char *a_pkg);
+static int ckpriv(char *a_msg, char *a_pkg);
+static int ckrunlevel(char *a_msg, char *a_pkg);
+static int cksetuid(char *a_msg, char *a_pkg);
+static int ckspace(char *a_msg, char *a_pkg);
+static int attrib(char *a_msg, char *a_pkg);
+static int setuidf(char *a_msg, char *a_pkg);
+static int setgidf(char *a_msg, char *a_pkg);
+static int overwr(char *a_msg, char *a_pkg);
+
+static depckl_t DEPCKL[] = {
+	/*
+	 * name,	ignore_values,	err_msg,	depcklFunc,	recrd
+	 * ---
+	 * ignore_values == NULL:
+	 * package and zone information is collected in the "record" object for
+	 * each occurance - then a message is constructed for each zone that
+	 * reported the condition - the message includes that portion of the
+	 * check past the "=" - then the specified "depcklFunc" is called to
+	 * process each message.
+	 * Message format:
+	 * 	%s %s <%s> %s <%s>
+	 * Message arguments:
+	 *	value, "package", package-name, "zone/zones", zone-name
+	 * ---
+	 * ignore-values == "???":
+	 * these checks are ignored if they return one of the listed values
+	 * if they do NOT return one of the listed values, then the package
+	 * and zone information is collected in the "record" object for each
+	 * occurance - then a single unified message is constructed for all
+	 * zones that report the same condition; then the specified "depcklFunc"
+	 * is called to process the resulting combined message.
+	 * Message format:
+	 * 	%s <%s> %s <%s>
+	 * Message arguments:
+	 *	"package", package-name, "zone/zones", zone-name(s)
+	 * ---
+	 * ignore-values="":
+	 * same as above BUT no check to ignore is done; message always reported
+	 */
+
+	{ "install-same-instance=true",	"",		&IMSG_SAME,
+					NULL,		&er_same
+	},
+	{ "ckpkgfilebad=",		NULL,		&IMSG_ABADFILE,
+					&ckpkgfilebad,	&er_ckpkgfilebad
+	},
+	{ "ckdirs=",			NULL,		&IMSG_DIRS,
+					&ckdirs,	&er_ckdirs
+	},
+	{ "prerequisite-incomplete=",	NULL,		&IMSG_PRENCI,
+					&ckprereqinc,	&er_prereqinc
+	},
+	{ "prerequisite-installed=",	NULL,		&IMSG_PREREQ,
+					&ckprereqinst,	&er_prereqinst
+	},
+	{ "runlevel=",			NULL,		&IMSG_RUNLEVEL,
+					NULL,		&er_runlevel
+	},
+	{ "conflict-contents=",		NULL,		&IMSG_CFCONTENT,
+					&ckcfcontent,	&er_ckcfcontent
+	},
+	{ "ckconflict=",		"0",		&IMSG_CNFFAILED,
+					&ckconflict,	&er_ckconflict
+	},
+	{ "ckdepend=",			"0",		&IMSG_DEPEND,
+					&ckdepend,	&er_ckdepend
+	},
+	{ "ckpartialinstall=",		"0",		&IMSG_PARTINST,
+					&ckpartinst,	&er_ckpartinst
+	},
+	{ "ckpartialremove=",		"0",		&IMSG_PARTREM,
+					&ckpartrem,	&er_ckpartrem
+	},
+	{ "ckpkgdirs=",			"0",		&IMSG_PKGDIRS,
+					&ckpkgdirs,	&er_ckpkgdirs
+	},
+	{ "ckpkgfiles=",		"0",		&IMSG_BADFILE,
+					&ckpkgfiles,	&er_ckpkgfiles
+	},
+	{ "ckpriv=",			"0",		&IMSG_PRIV,
+					&ckpriv,	&er_ckpriv
+	},
+	{ "ckrunlevel=",		"0",		&IMSG_CKRUNLVL,
+					&ckrunlevel,	&er_ckrunlevel
+	},
+	{ "cksetuid=",			"0",		&IMSG_SETUID,
+					&cksetuid,	&er_cksetuid
+	},
+	{ "ckspace=",			"0",		&IMSG_SPCFAILED,
+					&ckspace,	&er_ckspace
+	},
+	{ "install-new-only=true",	"",		&IMSG_NEWONLY,
+					NULL,		&er_newonly
+	},
+	{ "install-ovewrite=true",	"",		&IMSG_OVERWRITE,
+					NULL,		&er_overwrite
+	},
+	{ "install-too-many-instances=true",	"",	&IMSG_UNIQ1,
+					NULL,		&er_uniq1
+	},
+	{ "ckinstance=",		"0",		&IMSG_INSTANCE,
+					&ckinstance,	&er_ckinstance
+	},
+	{ "conflict-attributes=",	NULL,		&IMSG_ATTRIB,
+					&attrib,	&er_attrib
+	},
+	{ "setuid=",			NULL,		&IMSG_SETUIDF,
+					&setuidf,	&er_setuidf
+	},
+	{ "setgid=",			NULL,		&IMSG_SETGIDF,
+					&setgidf,	&er_setgidf
+	},
+	{ "setuid-overwrite=true",	"",		&IMSG_OVERWR,
+					&overwr,	&er_overwr
+	},
+
+	{ NULL,				NULL,	NULL,
+				NULL,		NULL }
+};
+
+/*
+ * Name:	preinstall_verify
+ * Description:	verify results of preinstallation dependency checking
+ * Arguments:	a_pkglist - pointer to array of strings representing the names
+ *			of all the packages that have been checked
+ *		a_zlst - list of zones that dependencies were checked on
+ *		a_zoneTempDir - pointer to string representing the path where
+ *			the files containing the preinstallation dependency
+ *			check data are located
+ * Returns:	int
+ *		== 0 - continue processing
+ *		!= 0 - do not continue processing
+ */
+
+int
+preinstall_verify(char **a_pkglist, zoneList_t a_zlst, char *a_zoneTempDir)
+{
+	char		*pkginst;
+	int		i;
+	int		savenpkgs = npkgs;
+
+	/*
+	 * entry assertions
+	 */
+
+	assert(a_pkglist != (char **)NULL);
+	assert(a_zlst != (zoneList_t)NULL);
+	assert(a_zoneTempDir != (char *)NULL);
+
+	/*
+	 * entry debugging info
+	 */
+
+	echoDebug(DBG_PREIVFY_ENTRY);
+
+	/*
+	 * localize messages
+	 */
+
+	IMSG_ABADFILE = MSG_PKGADDCHK_ABADFILE;
+	IMSG_BADFILE = MSG_PKGADDCHK_BADFILE;
+	IMSG_CFCONTENT = MSG_PKGADDCHK_CFCONTENT;
+	IMSG_CKRUNLVL = MSG_PKGADDCHK_CKRUNLVL;
+	IMSG_CNFFAILED = MSG_PKGADDCHK_CNFFAILED;
+	IMSG_DEPEND = MSG_PKGADDCHK_DEPEND;
+	IMSG_DIRS  = MSG_PKGADDCHK_DIRS;
+	IMSG_NEWONLY = MSG_PKGADDCHK_NEWONLY;
+	IMSG_OVERWRITE = MSG_PKGADDCHK_OVERWRITE;
+	IMSG_PARTINST = MSG_PKGADDCHK_PARTINST;
+	IMSG_PARTREM = MSG_PKGADDCHK_PARTREM;
+	IMSG_PKGDIRS = MSG_PKGADDCHK_PKGDIRS;
+	IMSG_PRENCI  = MSG_PKGADDCHK_PRENCI;
+	IMSG_PREREQ  = MSG_PKGADDCHK_PREREQ;
+	IMSG_PRIV = MSG_PKGADDCHK_PRIV;
+	IMSG_RUNLEVEL = MSG_PKGADDCHK_RUNLEVEL;
+	IMSG_SAME = MSG_PKGADDCHK_SAME;
+	IMSG_SETUID = MSG_PKGADDCHK_SETUID;
+	IMSG_SPCFAILED = MSG_PKGADDCHK_SPCFAILED;
+	IMSG_UNIQ1 = MSG_PKGADDCHK_UNIQ1;
+	IMSG_ATTRIB = gettext("\\nattribute change for %s <%s> on %s <%s>\n");
+	IMSG_SETUIDF = gettext("\\nsetuid %s in %s <%s> on %s <%s>\n");
+	IMSG_SETGIDF = gettext("\\nsetgid %s in %s <%s> on %s <%s>\n");
+	IMSG_OVERWR = gettext("\\nFiles that are setuid will be overwritten "
+	    "by installation of %s\n<%s> on %s <%s>.\n");
+
+	/*
+	 * outer loop - process each package first
+	 */
+
+	for (i = 0; (pkginst = a_pkglist[i]) != NULL; i++) {
+
+		char	*zoneName;
+		int	zoneIndex;
+
+		/*
+		 * if this package is marked "install in this zone only", then
+		 * do not check dependencies in any zone
+		 */
+
+		if (pkgPackageIsThisZone(pkginst) == B_TRUE) {
+			echoDebug(DBG_PREIVFY_SKIP_THISZONE, pkginst);
+			continue;
+		}
+
+		/*
+		 * inner loop - for each package process each zone second
+		 */
+
+		for (zoneIndex = 0;
+			(zoneName = z_zlist_get_zonename(a_zlst, zoneIndex)) !=
+				(char *)NULL; zoneIndex++) {
+
+			FILE	*fp;
+			char	line[PATH_MAX+1];
+			char	preinstallcheckPath[PATH_MAX+1];
+			int	len;
+
+			/* skip the zone if it is NOT bootable */
+
+			if (z_zlist_is_zone_runnable(a_zlst,
+							zoneIndex) == B_FALSE) {
+				continue;
+			}
+
+			/* create path to this packages preinstall check data */
+
+			len = snprintf(preinstallcheckPath,
+				sizeof (preinstallcheckPath),
+				"%s/%s.%s.preinstallcheck.txt", a_zoneTempDir,
+				pkginst, zoneName);
+
+			if (len > sizeof (preinstallcheckPath)) {
+				progerr(ERR_CREATE_PATH_3, a_zoneTempDir,
+					pkginst, zoneName);
+				continue;
+			}
+
+			/* error if preinstall check data path is not a file */
+
+			if (isfile((char *)NULL, preinstallcheckPath) != 0) {
+				echoDebug(DBG_PREIVFY_NOFILE,
+					pkginst, zoneName, preinstallcheckPath,
+					strerror(errno));
+				progerr(ERR_PREIVFY_NOFILE,
+					pkginst, zoneName);
+				continue;
+			}
+
+			/* open the preinstall check data file */
+
+			fp = fopen(preinstallcheckPath, "r");
+			if (fp == (FILE *)NULL) {
+				progerr(ERR_PREIVFY_OPEN_FILE,
+					preinstallcheckPath, pkginst, zoneName,
+					strerror(errno));
+				continue;
+			}
+
+			/* read and process each preinstall check data line */
+
+			while (fgets(line, sizeof (line), fp) != (char *)NULL) {
+				int	j;
+				int	len;
+
+				/* remove all new-lines from end of line */
+
+				len = strlen(line);
+				while ((len > 0) && (line[len-1] == '\n')) {
+					line[--len] = '\0';
+				}
+
+				/* ignore comment lines */
+
+				if (line[0] == '#') {
+					continue;
+				}
+
+				/* ignore empty lines */
+
+				if (line[0] == '\0') {
+					continue;
+				}
+
+				/* scan dependency list for this item */
+
+				for (j = 0;
+					DEPCKL[j].name != (char *)NULL; j++) {
+					len = strlen(DEPCKL[j].name);
+
+					if (strncmp(line, DEPCKL[j].name,
+							len) == 0) {
+						break;
+					}
+				}
+
+				echoDebug(DBG_PREIVFY_SCAN, line, pkginst,
+						zoneName);
+
+				/* ignore line if not found */
+
+				if (DEPCKL[j].name == (char *)NULL) {
+					progerr(ERR_PREIVFY_UNKNOWN_LINE, line,
+							pkginst, zoneName);
+					continue;
+				}
+
+				if ((DEPCKL[j].ignore_values != (char *)NULL) &&
+					(*(DEPCKL[j].ignore_values) != '\0') &&
+					(strchr(DEPCKL[j].ignore_values,
+						line[len]) != (char *)NULL)) {
+						continue;
+				}
+
+				/* found match - record this dependency issue */
+
+				depchkRecordError(DEPCKL[j].record, pkginst,
+					zoneName, &line[len]);
+			}
+
+			/* close preinstall check data file */
+
+			(void) fclose(fp);
+		}
+	}
+
+	/*
+	 * all dependency issues have been recorded; report results
+	 */
+
+	i = depchkReportErrors(DEPCKL);
+
+	/* restore "npkgs" */
+
+	npkgs = savenpkgs;
+
+	/* return continue/dont dontinue results */
+
+	return (i);
+}
+
+/*
+ * Name:	getyorn
+ * Description:	Deliver dependency check reason; ask question; return response
+ * Arguments:	a_msg - pointer to string representing the message to output
+ *			such as 'The package <..> contains <...>'
+ *		a_pkg - pointer to string representing the package for which
+ *			the question is being asked
+ *		a_nocheck - should the message be output?
+ *			== 0 - do not output the message
+ *			!= 0 - output the message
+ *		a_quit - should the question NOT be asked?
+ *			== 0 - ask the question
+ *			!= 0 - do not ask the question - return "no"
+ *		a_helpMsg - pointer to string representing help message to be
+ *			made available if the question is asked
+ *			== NULL - no help message is available
+ *		a_adminMsg - pointer to string representing the dependency check
+ *			failure 'reason' - such as "Privilege checking failed."
+ *			== NULL - no failure reason is available
+ * Returns:	int - results of question/response actions
+ *			0 - success
+ *			1 - end of file
+ *			2 - undefined error
+ *			3 - answer was not "y"/was "q"
+ *			4 - quit action taken
+ *			5 - interactive mode required
+ */
+
+static int
+getyorn(char *a_msg, char *a_pkg, int a_nocheck, int a_quit,
+	char *a_helpMsg, char *a_adminMsg)
+{
+	char	ans[MAX_INPUT];
+	char	ask_cont[MSG_MAX];
+	int	n;
+	int	saveCkquit;
+
+	/*
+	 * entry assertions
+	 */
+
+	assert(a_pkg != (char *)NULL);
+	assert(*a_pkg != '\0');
+
+	/*
+	 * entry debugging info
+	 */
+
+	echoDebug(DBG_PREIVFY_GETYORN_ARGS, a_pkg, a_nocheck, a_quit, a_msg,
+			a_adminMsg ? a_adminMsg : "");
+
+	/* return success (0) if "nocheck" is non-zero */
+
+	if (a_nocheck != 0) {
+		echoDebug(DBG_PREIVFY_GETYORN_NOCHECK, a_pkg);
+		return (0);
+	}
+
+	/* output reason for this particular failure */
+
+	if ((a_msg != (char *)NULL) && (*a_msg != '\0')) {
+		ptext(stderr, "%s", a_msg);
+	}
+
+	/* return "4 (administration)" if "quit" is non-zero */
+
+	if (a_quit != 0) {
+		/* output failure "admin reason" if available */
+		if ((a_adminMsg != (char *)NULL) && (*a_adminMsg != '\0')) {
+			ptext(stderr, a_adminMsg);
+		}
+		echoDebug(DBG_PREIVFY_GETYORN_QUIT, a_pkg);
+		return (4);
+	}
+
+	/* return "5 (administration interaction required)" if -n */
+
+	if (echoGetFlag() == B_FALSE) {
+		ptext(stderr, MSG_PREIVFY_GETYORN_SUSP, a_pkg);
+		echoDebug(DBG_PREIVFY_GETYORN_QUIT_USER, a_pkg);
+		return (5);
+	}
+
+	/* prepare question to ask "continue with pkg <xxx>?" */
+
+	(void) snprintf(ask_cont, sizeof (ask_cont), gettext(ASK_CONT), a_pkg);
+
+	/* ask question */
+
+	saveCkquit = ckquit;
+	ckquit = 0;
+
+	n = ckyorn(ans, NULL, NULL, a_helpMsg, ask_cont);
+
+	ckquit = saveCkquit;
+
+	if (n != 0) {
+		ptext(stderr, MSG_PREIVFY_GETYORN_TERM, a_pkg);
+		echoDebug(DBG_PREIVFY_GETYORN_CKYORN, a_pkg, n);
+		return (n);
+	}
+
+	/* return "3 (interruption) if not "y" or "Y" */
+
+	if (strchr("yY", *ans) == NULL) {
+		ptext(stderr, MSG_PREIVFY_GETYORN_TERM_USER, a_pkg);
+		echoDebug(DBG_PREIVFY_GETYORN_NOT_Y, a_pkg, ans);
+		return (3);
+	}
+
+	/* return "0 - success" */
+
+	echoDebug(DBG_PREIVFY_GETYORN_SUCCESS, a_pkg);
+
+	return (0);
+}
+
+/*
+ * Trigger:	prerequisite-incomplete=<<package>>
+ * Sequence:	- one or more: prerequisite-incomplete=<<package>>
+ *		- one: ckdepend=<<n>>
+ * Actions:	Output message if "idepend!=nocheck"
+ *		Return 0
+ *		Terminate when 'ckdepend' processed
+ */
+
+static int
+ckprereqinc(char *a_msg, char *a_pkg)
+{
+	echoDebug(DBG_PREIVFY_CKPRENCI, a_pkg, a_msg);
+
+	if (!(ADM(idepend, "nocheck"))) {
+		ptext(stderr, "%s", a_msg);
+	}
+
+	return (0);
+}
+
+/*
+ * Trigger:	prerequisite-installed=<<package>>
+ * Sequence:	- one or more: prerequisite-installed=<<package>>
+ *		- one: ckdepend=<<n>>
+ * Actions:	Output message if "idepend!=nocheck"
+ *		Return 0
+ *		Terminate when 'ckdepend' processed
+ */
+
+static int
+ckprereqinst(char *a_msg, char *a_pkg)
+{
+	echoDebug(DBG_PREIVFY_CKPREREQ, a_pkg, a_msg);
+
+	if (!(ADM(idepend, "nocheck"))) {
+		ptext(stderr, "%s", a_msg);
+	}
+
+	return (0);
+}
+
+/*
+ * Trigger:	ckpartialinstall=<<n>>
+ * Sequence:	- one: ckpartialinstall=<<n>>
+ * Actions:	process according to settings
+ * Return value:	int
+ *			0 - success
+ *			1 - end of file
+ *			2 - undefined error
+ *			3 - answer was not "y"/was "q"
+ *			4 - quit action taken
+ *			5 - interactive mode required
+ */
+
+static int
+ckpartinst(char *a_msg, char *a_pkg)
+{
+	echoDebug(DBG_PREIVFY_CKPARTIALINSTALL, a_pkg, a_msg);
+
+	return (getyorn(a_msg, a_pkg, ADM(partial, "nocheck"),
+			ADM(partial, "quit"), HLP_PKGADDCHK_PARTIAL, NULL));
+}
+
+/*
+ * Trigger:	ckpartialremove=<<n>>
+ * Sequence:	- one: ckpartialremove=<<n>>
+ * Actions:	process according to settings
+ * Return value:	int
+ *			0 - success
+ *			1 - end of file
+ *			2 - undefined error
+ *			3 - answer was not "y"/was "q"
+ *			4 - quit action taken
+ *			5 - interactive mode required
+ */
+
+static int
+ckpartrem(char *a_msg, char *a_pkg)
+{
+	echoDebug(DBG_PREIVFY_CKPARTIALREMOVE, a_pkg, a_msg);
+
+	return (getyorn(a_msg, a_pkg, ADM(partial, "nocheck"),
+		ADM(partial, "quit"), HLP_PKGADDCHK_PARTIAL, NULL));
+}
+
+/*
+ * Return value:	int
+ *			0 - success
+ *			1 - end of file
+ *			2 - undefined error
+ *			3 - answer was not "y"/was "q"
+ *			4 - quit action taken
+ *			5 - interactive mode required
+ *			99 - fatal error
+ */
+
+static int
+ckrunlevel(char *a_msg, char *a_pkg)
+{
+	echoDebug(DBG_PREIVFY_CKRUNLEVEL, a_pkg, a_msg);
+	return (0);
+}
+
+/*
+ * Trigger:	conflict-contents=<<n>>
+ * Sequence:	- one or more of:
+ *		-- conflict-contents=<<path>>
+ *		-- conflict-attributes=<<path>>
+ *		- one: ckconflict=<<n>>
+ * Actions:	output message
+ * Return value:	int
+ *			0 - success
+ */
+
+static int
+ckcfcontent(char *a_msg, char *a_pkg)
+{
+	echoDebug(DBG_PREIVFY_CKCFCONTENT, a_pkg, a_msg);
+
+	ptext(stderr, "%s", a_msg);
+
+	return (0);
+}
+
+/*
+ * Trigger:	ckinstance=<<n>>
+ * Sequence:	- one or more of:
+ *		-- install-instance=true
+ *		-- install-new-only=true\n
+ *		-- install-same-instance=true\n
+ *		-- install-ovewrite=true\n
+ *		-- install-too-many-instances=true\n
+ *		-- install-new-instance=true\n
+ *		- one: ckpdepend=<<n>>
+ * Actions:	process according to settings
+ * Return value:	int
+ *			0 - success
+ *			1 - end of file
+ *			2 - undefined error
+ *			3 - answer was not "y"/was "q"
+ *			4 - quit action taken
+ *			5 - interactive mode required
+ */
+
+static int
+ckinstance(char *a_msg, char *a_pkg)
+{
+	echoDebug(DBG_PREIVFY_CKINSTANCE, a_pkg, a_msg);
+
+	return (getyorn(a_msg, a_pkg, ADM(instance, "nocheck"),
+		ADM(instance, "quit"), HLP_PKGADDCHK_DEPEND,
+		ERR_PKGADDCHK_DEPFAILED));
+}
+
+/*
+ * Trigger:	ckdepend=<<n>>
+ * Sequence:	- one or more of:
+ *		-- incompat=<<package>>
+ *		-- prerequisite-incomplete=<<package>>
+ *		-- prerequisite-installed=<<package>>
+ *		-- dependson=<<package>>
+ *		-- dependsonme=<<package>>
+ *		- one: ckpdepend=<<n>>
+ * Actions:	process according to settings
+ * Return value:	int
+ *			0 - success
+ *			1 - end of file
+ *			2 - undefined error
+ *			3 - answer was not "y"/was "q"
+ *			4 - quit action taken
+ *			5 - interactive mode required
+ */
+
+static int
+ckdepend(char *a_msg, char *a_pkg)
+{
+	echoDebug(DBG_PREIVFY_CKDEPEND, a_pkg, a_msg);
+
+	return (getyorn(a_msg, a_pkg, ADM(idepend, "nocheck"),
+		ADM(idepend, "quit"), HLP_PKGADDCHK_DEPEND,
+		ERR_PKGADDCHK_DEPFAILED));
+}
+
+/*
+ * Trigger:	ckspace=<<n>>
+ * Sequence:	- one: ckspace=<<n>>
+ * Actions:	process according to settings
+ * Return value:	int
+ *			0 - success
+ *			1 - end of file
+ *			2 - undefined error
+ *			3 - answer was not "y"/was "q"
+ *			4 - quit action taken
+ *			5 - interactive mode required
+ */
+
+static int
+ckspace(char *a_msg, char *a_pkg)
+{
+	echoDebug(DBG_PREIVFY_CKSPACE, a_pkg, a_msg);
+
+	return (getyorn(a_msg, a_pkg, ADM(space, "nocheck"),
+		ADM(space, "quit"), HLP_PKGADDCHK_SPACE,
+		ERR_PKGADDCHK_SPCFAILED));
+}
+
+/*
+ * Trigger:	ckpkgdirs=<<n>>
+ * Sequence:	- one: ckpkgdirs=<<n>>
+ * Actions:	output message
+ *		Return 4
+ */
+
+static int
+ckpkgdirs(char *a_msg, char *a_pkg)
+{
+	echoDebug(DBG_PREIVFY_CKPKGDIRS, a_pkg, a_msg);
+
+	ptext(stderr, "%s", a_msg);
+
+	return (4);
+}
+
+/*
+ * Trigger:	ckdirs=<<path>>
+ * Sequence:	- one: ckdirs=<<path>>
+ * Actions:	output message
+ *		Return 4
+ */
+
+static int
+ckdirs(char *a_msg, char *a_pkg)
+{
+	echoDebug(DBG_PREIVFY_CKDIRS, a_pkg, a_msg);
+
+	ptext(stderr, "%s", a_msg);
+
+	ptext(stderr, ERR_PKGADDCHK_MKPKGDIR);
+
+	return (4);
+}
+
+/*
+ * Trigger:	ckpkgfilebad=<<path>>
+ * Sequence:	- one or more:
+ *		-- ckpkgfilebad=<<path>>
+ *		- one ckpkgfiles=<n>
+ * Actions:	output message
+ *		Return 0
+ */
+
+static int
+ckpkgfilebad(char *a_msg, char *a_pkg)
+{
+	echoDebug(DBG_PREIVFY_CKPKGFILEBAD, a_pkg, a_msg);
+
+	ptext(stderr, "%s", a_msg);
+
+	return (0);
+}
+
+/*
+ * Trigger:	ckconflict=<<n>>
+ * Sequence:	- one or more:
+ *		-- conflict-contents=<<path>>
+ *		-- conflict-attributes=<<path>>
+ *		- one: ckconflict=<<n>>
+ * Actions:	process according to settings
+ * Return value:	int
+ *			0 - success
+ *			1 - end of file
+ *			2 - undefined error
+ *			3 - answer was not "y"/was "q"
+ *			4 - quit action taken
+ *			5 - interactive mode required
+ */
+
+static int
+ckconflict(char *a_msg, char *a_pkg)
+{
+	echoDebug(DBG_PREIVFY_CKCONFLICT, a_pkg, a_msg);
+
+	return (getyorn(a_msg, a_pkg, ADM(conflict, "nocheck"),
+		ADM(conflict, "quit"), HLP_PKGADDCHK_CONFLICT,
+		ERR_PKGADDCHK_CNFFAILED));
+}
+
+/*
+ * Trigger:	cksetuid=<<n>>
+ * Sequence:	- one or more:
+ *		-- setuid=<path>:<owner>
+ *		-- setgid=<path>:<group>
+ *		-- setuid-overwrite=true
+ *		- one: cksetuid=<<n>>
+ * Actions:	process according to settings
+ * Return value:	int
+ *			0 - success
+ *			1 - end of file
+ *			2 - undefined error
+ *			3 - answer was not "y"/was "q"
+ *			4 - quit action taken
+ *			5 - interactive mode required
+ */
+
+static int
+cksetuid(char *a_msg, char *a_pkg)
+{
+	char	ans[MAX_INPUT];
+	char	ask_cont[MSG_MAX];
+	int	n;
+	int	saveCkquit;
+
+	echoDebug(DBG_PREIVFY_CKSETUID, a_pkg, a_msg);
+
+	n = getyorn(a_msg, a_pkg, ADM(setuid, "nocheck"),
+		ADM(setuid, "quit"), HLP_PKGADDCHK_SETUID, NULL);
+
+	/* if user did not answer "n" return answer given */
+
+	if (n != 3) {
+		return (n);
+	}
+
+	(void) snprintf(ask_cont, sizeof (ask_cont), gettext(ASK_CONT), a_pkg);
+
+	saveCkquit = ckquit;
+	ckquit = 0;
+
+	n = ckyorn(ans, NULL, NULL, gettext(HLP_PKGADDCHK_CONT), ask_cont);
+
+	ckquit = saveCkquit;
+
+	if (n != 0) {
+		ptext(stderr, MSG_PREIVFY_GETYORN_TERM, a_pkg);
+		echoDebug(DBG_PREIVFY_GETYORN_CKYORN, a_pkg, n);
+		return (n);
+	}
+
+	/* return "3 (interruption) if not "y" or "Y" */
+
+	if (strchr("yY", *ans) == NULL) {
+		ptext(stderr, MSG_PREIVFY_GETYORN_TERM_USER, a_pkg);
+		echoDebug(DBG_PREIVFY_GETYORN_NOT_Y, a_pkg, ans);
+		return (3);
+	}
+
+	/* return "0 - success" */
+
+	echoDebug(DBG_PREIVFY_GETYORN_SUCCESS, a_pkg);
+
+	return (0);
+}
+
+/*
+ * Trigger:	ckpriv=<<n>>
+ * Sequence:	- one: ckpriv=<<n>>
+ * Actions:	process according to settings
+ * Return value:	int
+ *			0 - success
+ *			1 - end of file
+ *			2 - undefined error
+ *			3 - answer was not "y"/was "q"
+ *			4 - quit action taken
+ *			5 - interactive mode required
+ */
+
+static int
+ckpriv(char *a_msg, char *a_pkg)
+{
+	echoDebug(DBG_PREIVFY_CKPRIV, a_pkg, a_msg);
+
+	return (getyorn(a_msg, a_pkg, ADM(action, "nocheck"),
+		ADM(action, "quit"), HLP_PKGADDCHK_PRIV,
+		ERR_PKGADDCHK_PRIVFAILED));
+}
+
+/*
+ * Trigger:	ckpkgfiles=<<n>>
+ * Sequence:	- one or more:
+ *		-- ckpkgfilebad=<path>
+ *		- one: ckpkgfiles=<<n>>
+ * Return value:	int
+ *			0 - success
+ *			4 - failure
+ */
+
+static int
+ckpkgfiles(char *a_msg, char *a_pkg)
+{
+	echoDebug(DBG_PREIVFY_CKPKGFILES, a_pkg, a_msg);
+
+	ptext(stderr, "%s", a_msg);
+
+	return (4);
+}
+
+static int
+attrib(char *a_msg, char *a_pkg)
+{
+	return (getyorn(a_msg, a_pkg, ADM(instance, "nocheck"),
+		ADM(instance, "quit"), HLP_PKGADDCHK_CONT,
+		ERR_PKGADDCHK_DEPFAILED));
+}
+
+/* ARGSUSED1 */
+static int
+setuidf(char *a_msg, char *a_pkg)
+{
+	char *cp;
+
+	if ((cp = strchr(a_msg, ':')) != NULL)
+		*cp = ' ';
+	return (0);
+}
+
+/* ARGSUSED1 */
+static int
+setgidf(char *a_msg, char *a_pkg)
+{
+	char *cp;
+
+	if ((cp = strchr(a_msg, ':')) != NULL)
+		*cp = ' ';
+	return (0);
+}
+
+static int
+overwr(char *a_msg, char *a_pkg)
+{
+	return (getyorn(a_msg, a_pkg, ADM(instance, "nocheck"),
+		ADM(instance, "quit"), HLP_PKGADDCHK_SETUID,
+		ERR_PKGADDCHK_DEPFAILED));
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/usr/src/cmd/svr4pkg/pkgadd/main.c	Fri Jun 05 10:28:40 2009 -0400
@@ -0,0 +1,4712 @@
+/*
+ * 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
+ * or http://www.opensolaris.org/os/licensing.
+ * 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 2009 Sun Microsystems, Inc.  All rights reserved.
+ * Use is subject to license terms.
+ */
+
+/* Copyright (c) 1984, 1986, 1987, 1988, 1989 AT&T */
+/* All Rights Reserved */
+
+
+/*
+ * Program:	pkgadd / pkgask
+ *
+ * Function:	public command and private utility functions that
+ *		implement the package add and package ask operations.
+ *
+ */
+
+/*
+ * System includes
+ */
+
+#include <stdio.h>
+#include <limits.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <string.h>
+#include <fcntl.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <signal.h>
+#include <errno.h>
+#include <pkgdev.h>
+#include <pkginfo.h>
+#include <pkglocs.h>
+#include <locale.h>
+#include <libintl.h>
+#include <pkgtrans.h>
+#include <boot_http.h>
+#include <assert.h>
+
+/*
+ * consolidation pkg command library includes
+ */
+#include <pkglib.h>
+#include <pkgerr.h>
+#include <pkgweb.h>
+
+#include <instzones_api.h>
+
+/*
+ * local pkg command library includes
+ */
+#include <install.h>
+#include <libinst.h>
+#include <libadm.h>
+#include <messages.h>
+
+
+/*
+ * pkgadd local includes
+ */
+
+#include "quit.h"
+
+/*
+ * imported global variables/functions
+ */
+
+/* presvr4.c */
+extern int	presvr4(char **ppkg, int a_nointeract);
+
+/* check.c */
+extern int	preinstall_verify(char **a_pkgList, zoneList_t a_zlst,
+			char *a_zoneTempDir);
+
+/*
+ * ckquit is a global that controls 'ckyorn' (defined in libadm)
+ * If ckquit is non-zero, then "quit" is allowed as an answer when
+ * ckyorn is called. If is it zero, then "quit" is not an allowed answer.
+ */
+extern int	ckquit;
+
+/*
+ * exported global variables
+ */
+
+/* these globals are set by ckreturn and used by quit.c */
+
+int	admnflag = 0;	/* != 0 if any pkg op admin setting failure (4) */
+int	doreboot = 0;	/* != 0 if reboot required after installation */
+int	failflag = 0;	/* != 0 if fatal error has occurred (1) */
+int	intrflag = 0;	/* != 0 if user selected quit (3) */
+int	ireboot = 0;	/* != 0 if immediate reboot required */
+int	nullflag = 0;	/* != 0 if admin interaction required (5) */
+int	warnflag = 0;	/* != 0 if non-fatal error has occurred (2) */
+
+/* imported by quit.c */
+int	npkgs = 0;	/* the number of packages yet to be installed */
+
+/* imported by various (many) */
+char	*respfile = NULL;	/* response pathname (or NULL) */
+char	*tmpdir = NULL;		/* location to place temporary files */
+
+struct admin	adm;		/* holds info about installation admin */
+struct pkgdev	pkgdev;		/* holds info about the installation device */
+
+/*
+ * internal global variables
+ */
+
+static char	*admnfile = NULL;	/* file to use for installation admin */
+static char	*ids_name = NULL;	/* name of data stream device */
+static char	*pkgcontsrc = NULL;	/* continuation file (-c option) */
+static char	*pkgdrtarg = NULL;	/* dry run file (-D option) */
+static char	*pkginst = NULL;	/* current pkg/src instance 2 process */
+static char	*respdir = NULL;	/* respfile is a directory spec */
+static char	*rw_block_size = NULL;
+static char	*vfstab_file = NULL;
+static int	askflag = 0;		/* non-zero if invoked as "pkgask" */
+static int	disableAttributes = 0;	/* Disabling attribute checking */
+static int	disableChecksum = 0;	/* Disable checksumming */
+static int	disableSaveSpool = 0;	/* Disable partial spool dir create */
+static int	init_install = 0;	/* inform scripts initial install */
+static int	no_map_client = 0;	/* do not map from vfstab file */
+static int	nointeract = 0;		/* non-zero - no user interaction */
+static int	pkgverbose = 0;		/* non-zero if verbose mode selected */
+static int	saveSpoolInstall = 0;	/* installing from save spool dir */
+static int	suppressCopyright = 0;	/* suppress copyright notices */
+
+/* set by ckreturn() */
+
+static int	interrupted = 0;	/* last pkg op was quit (1,2,3,4,5) */
+static int	needconsult = 0;	/* essential ask admin now (1,2,3,5) */
+
+/* Set by -O nozones: do not process any zones */
+
+static boolean_t	noZones = B_FALSE;
+
+/* Set by -O zonelist=<names...>: process only named zones */
+
+static boolean_t	usedZoneList = B_FALSE;
+
+/* Set by -O debug: debug output is enabled? */
+
+static boolean_t	debugFlag = B_FALSE;
+
+/* Set by the -G option: install packages in global zone only */
+
+static boolean_t	globalZoneOnly = B_FALSE;
+
+/* Set by -O patchPkgRemoval */
+
+static boolean_t	patchPkgRemoval = B_FALSE;
+
+/*
+ * Assume the package is ABI and POSIX compliant as regards user
+ * interactiion during procedure scripts.
+ */
+
+static int	old_pkg = 0;
+
+/* Assume pkg should be installed according to the ABI */
+
+static int	old_symlinks = 0;
+
+/*
+ * Default name length will be 32 chars - if this is set,
+ * disable the 32 char name limit extension
+ */
+
+static int	ABI_namelength = 0;
+
+#if	!defined(TEXT_DOMAIN)	/* Should be defined by cc -D */
+#define	TEXT_DOMAIN	"SYS_TEST"
+#endif
+
+/* printable string - if string is null results in ??? */
+
+#define	PSTR(STR) (((STR) == (char *)NULL) ? "???" : (STR))
+
+#define	MAX_FDS	20
+
+#define	INHERITFS	"inherited-filesystem="
+#define	INHERITFS_LEN	((sizeof (INHERITFS))-1)
+
+/*
+ * forward declarations
+ */
+
+static int		boot_and_pkginstall_check_in_zones(zoneList_t a_zlst,
+				char *a_idsName, char *a_altBinDir,
+				char *a_zoneAdminFile, char *a_zoneTempDir);
+static int		boot_and_install_in_zones(zoneList_t a_zlst,
+				char *a_idsName, char *a_altBinDir,
+				char *a_zoneAdminFile, char *a_zoneTempDir);
+static void		pkginstall_check_in_one_zone(char **a_inheritedPkgDirs,
+				char *a_zoneName, char *a_idsName,
+				char *a_zoneAdminFile, char *a_zoneTempDir,
+				char *a_altBinDir, char *a_scratchName,
+				zone_state_t a_zoneState);
+static void		ckreturn(int retcode);
+static void		create_zone_adminfile(char **r_zoneAdminFile,
+				char *a_zoneTempDir, char *a_admnfile);
+static void		create_zone_tempdir(char **r_zoneTempDir,
+				char *a_tmpdir);
+static void		install_in_one_zone(char **a_inheritedPkgDirs,
+				char *a_zoneName, char *a_idsName,
+				char *a_zoneAdminFile, char *a_zoneTempDir,
+				char *a_altBinDir, zone_state_t a_zoneState);
+static int		pkginstall_check_in_zones(zoneList_t a_zlst,
+				char *a_idsName, char *a_altBinDir,
+				char *a_zoneAdminFile, char *a_zoneTempDir);
+static int		install_in_zones(zoneList_t a_zlst, char *a_idsName,
+				char *a_altBinDir, char *a_zoneAdminFile,
+				char *a_zoneTempDir);
+static int		pkgInstall(char *ir, char *a_idsName, char *a_pkgDir,
+				char *a_altBinDir, char **a_inheritedPkgDirs);
+static int		pkgZoneCheckInstall(char *a_zoneName,
+				char **a_inheritedPkgDirs,
+				zone_state_t a_zoneState,
+				char *a_idsName, char *a_altBinDir,
+				char *a_adminFile, char *a_stdoutPath);
+static int		pkgZoneInstall(char *a_zoneName,
+				char **a_inheritedPkgDirs,
+				zone_state_t a_zoneState,
+				char *a_idsName, char *a_altBinDir,
+				char *a_adminFile);
+static void		resetreturn();
+static void		usage(void);
+static boolean_t	add_packages(char **a_pkgList, char *a_uri,
+				char *a_idsName, int a_repeat,
+				char *a_altBinDir, char *a_device,
+				boolean_t a_noZones);
+static boolean_t	add_packages_in_global_no_zones(char **a_pkgList,
+				char *a_uri, char *a_idsName, int a_repeat,
+				char *a_altBinDir, char *a_device);
+static boolean_t	add_packages_in_global_with_zones(char **a_pkgList,
+				char *a_uri, char *a_idsName, int a_repeat,
+				char *a_altBinDir, char *a_device,
+				zoneList_t a_zlst);
+static boolean_t	add_packages_in_nonglobal_zone(char **a_pkgList,
+				char *a_uri, char *a_idsName, int a_repeat,
+				char *a_altBinDir, char *a_device);
+static boolean_t	check_applicability(char *a_packageDir,
+				char *a_pkgInst, char *a_rootPath,
+				CAF_T a_flags);
+static boolean_t	get_package_list(char ***r_pkgList, char **a_argv,
+				char *a_categories, char **a_categoryList,
+				int a_ignoreSignatures, PKG_ERR *a_err,
+				ushort_t a_httpProxyPort, char *a_httpProxyName,
+				keystore_handle_t a_keystore,
+				char *a_keystoreFile, char *a_idsName,
+				int *r_repeat);
+static boolean_t	continue_installation(void);
+static boolean_t	unpack_and_check_packages(char **a_pkgList,
+				char *a_idsName, char *a_packageDir);
+/*
+ * *****************************************************************************
+ * global external (public) functions
+ * *****************************************************************************
+ */
+
+/*
+ * Name:	main
+ * Description:	main entry point for pkgadd/pkgask
+ * Returns:	int
+ *   0        Successful completion
+ *   1        Fatal error.
+ *   2        Warning.
+ *   3        Interruption.
+ *   4        Administration.
+ *   5        Administration. Interaction is required. Do not use pkgadd -n.
+ * In addition, one of the following values may be added to the previous value
+ * as appropriate:
+ *  10       Reboot after installation of all packages.
+ *  20       Reboot after installation of this package.
+ * For example, "14" would indicate both "administration" and "reboot after
+ * installation of all packages".
+ */
+
+int
+main(int argc, char **argv)
+{
+	PKG_ERR			*err = NULL;
+	WebScheme		scheme = none;
+	char			**category = NULL;
+	char			*abiPtr;
+	char			*altBinDir = (char *)NULL;
+	char			*catg_arg = NULL;
+	char			*device = NULL;		/* dev pkg stored on */
+	char			*dwnld_dir = NULL;
+	char			*keystore_file = NULL;
+	char			*p;
+	char			*q;
+	char			*prog;
+	char			*prog_full_name = NULL;
+	char			*proxy = NULL;
+	char			*spoolDir = NULL;	/* specified with -s */
+	char			*uri = NULL;
+	char			Rpath[PATH_MAX+1] = {'\0'};
+	int			c;
+	int			ignore_sig = 0;
+	int			n;
+	int			repeat;
+	int			retries = NET_RETRIES_DEFAULT;
+	int			timeout = NET_TIMEOUT_DEFAULT;
+	keystore_handle_t	keystore = NULL;
+	struct sigaction	nact;
+	struct sigaction	oact;
+	ushort_t		proxy_port = 0;
+
+	/* initialize locale environment */
+
+	(void) setlocale(LC_ALL, "");
+	(void) textdomain(TEXT_DOMAIN);
+
+	/* initialize program name */
+
+	prog_full_name = argv[0];
+	prog = set_prog_name(argv[0]);
+
+	/* tell spmi zones interface how to access package output functions */
+
+	z_set_output_functions(echo, echoDebug, progerr);
+
+	askflag = (strcmp(prog, "pkgask") == 0);
+
+	/* set sane umask */
+
+	(void) umask(0022);
+
+	/* tell quit which ckreturn function to call */
+
+	quitSetCkreturnFunc(&ckreturn);
+
+	/* initially no source "device" */
+
+	device = NULL;
+
+	/* reset npkgs (used as pkg remaining count in quit.c) */
+
+	npkgs = 0;
+
+	/* set default password prompt for encrypted packages */
+
+	set_passphrase_prompt(MSG_PASSPROMPT);
+
+	/* initialize security operations structures and libraries */
+
+	sec_init();
+
+	if (z_running_in_global_zone() && !enable_local_fs()) {
+		progerr(ERR_CANNOT_ENABLE_LOCAL_FS);
+	}
+
+	/*
+	 * ********************************************************************
+	 * parse command line options
+	 * ********************************************************************
+	 */
+
+	while ((c = getopt(argc, argv,
+		"?Aa:b:B:Cc:D:d:GhIik:MnO:P:R:r:Ss:tV:vx:Y:zZ")) != EOF) {
+		switch (c) {
+
+		/*
+		 * Not a public interface: This disables attribute checking.
+		 * It speeds up installation a little bit.
+		 */
+		case 'A':
+			disableAttributes++;
+			break;
+
+		/*
+		 * Public interface: Define an installation administration
+		 * file, admin, to be used in place of the default
+		 * administration file.	 The token none overrides the use
+		 * of any admin file, and thus forces interaction with the
+		 * user. Unless a full path name is given, pkgadd first
+		 * looks in the current working directory for the
+		 * administration file.	 If the specified administration
+		 * file is not in the current working directory, pkgadd
+		 * looks in the /var/sadm/install/admin directory for the
+		 * administration file.
+		 */
+		case 'a':
+			admnfile = flex_device(optarg, 0);
+			break;
+
+		/*
+		 * Not a public interface: control block size given to
+		 * pkginstall - block size used in read()/write() loop;
+		 * default is st_blksize from stat() of source file.
+		 */
+		case 'B':
+			if (optarg[0] == '-') {
+				usage();
+				quit(1);
+			}
+			rw_block_size = optarg;
+			break;
+
+		/*
+		 * Not a public interface:  location where package executables
+		 * can be found - default is /usr/sadm/install/bin.
+		 */
+		case 'b':
+			if (optarg[0] == '-') {
+				usage();
+				quit(1);
+			}
+			if (!path_valid(optarg)) {
+				progerr(ERR_PATH, optarg);
+				quit(1);
+			}
+			if (isdir(optarg) != 0) {
+				p = strerror(errno);
+				progerr(ERR_CANNOT_USE_DIR, optarg, p);
+				quit(1);
+			}
+			altBinDir = optarg;
+			break;
+
+		/*
+		 * Not a public interface: This disables checksum tests on
+		 * the source files. It speeds up installation a little bit.
+		 */
+		case 'C':
+			disableChecksum++;
+			break;
+
+		/*
+		 * Not a public interface: This allows designation of a
+		 * continuation file. It is the same format as a dryrun file
+		 * but it is used to take up where the dryrun left off.
+		 */
+		case 'c':
+			pkgcontsrc = flex_device(optarg, 0);
+			break;
+
+		/*
+		 * Not a public interface: This allows designation of a
+		 * dryrun file. This pkgadd will create dryrun files
+		 * in the directory provided.
+		 */
+		case 'D':
+			if (optarg[0] == '-') {
+				usage();
+				quit(1);
+			}
+			pkgdrtarg = flex_device(optarg, 0);
+			break;
+
+		/*
+		 * Public interface: Install or copy a package from
+		 * device. device can be a full path name to a directory
+		 * or the identifiers for tape, floppy disk, or removable
+		 * disk - for example, /var/tmp or /floppy/floppy_name.
+		 * It can also be a device alias - for example,
+		 * /floppy/floppy0, or a datastream created by pkgtrans.
+		 */
+		case 'd':
+			if (optarg[0] == '-') {
+				usage();
+				quit(1);
+			}
+			if (!path_valid(optarg)) {
+				progerr(ERR_PATH, optarg);
+				quit(1);
+				/* NOTREACHED */
+			}
+
+			if (strncmp(optarg, HTTP, 7) == 0) {
+				scheme = web_http;
+			} else if (strncmp(optarg, HTTPS, 8) == 0) {
+				scheme = web_https;
+			}
+
+			if (scheme == web_https || scheme == web_http) {
+				uri = optarg;
+				if ((device = malloc(PATH_MAX)) == NULL) {
+					progerr(ERR_MEM);
+					exit(1);
+				}
+				(void) memset(device, '\0', PATH_MAX);
+			} else {
+				device = flex_device(optarg, 1);
+			}
+			break;
+
+		/*
+		 * Public interface: install package in global zone only.
+		 */
+		case 'G':
+			globalZoneOnly = B_TRUE;
+			break;
+
+		/*
+		 * Not a public interface: Enable hollow package support. When
+		 * specified, for any package that has SUNW_PKG_HOLLOW=true:
+		 *  Do not calculate and verify package size against target.
+		 *  Do not run any package procedure or class action scripts.
+		 *  Do not create any target directories.
+		 *  Do not perform any script locking.
+		 *  Do not install any components of any package.
+		 *  Do not output any status or database update messages.
+		 */
+		case 'h':
+			set_depend_pkginfo_DB(B_TRUE);
+			break;
+
+		/*
+		 * Not a public interface: Informs scripts that this is
+		 * an initial install by setting the environment parameter
+		 * PKG_INIT_INSTALL=TRUE for all scripts. They may use it as
+		 * they see fit, safe in the knowledge that the target
+		 * filesystem is tabula rasa.
+		 */
+		case 'I':
+			init_install++;
+			break;
+
+		/*
+		 * Not a public interface: ignore signatures.
+		 */
+		case 'i':
+			ignore_sig++;
+			break;
+
+		/*
+		 * Public interface: Use keystore as the location from which to
+		 * get trusted certificate authority certificates when verifying
+		 * digital signatures found in packages. If no keystore is
+		 * specified, then the default keystore locations are searched
+		 * for valid trusted certificates.
+		 */
+		case 'k':
+			if (!path_valid(optarg)) {
+				progerr(ERR_PATH, optarg);
+				quit(1);
+				/* NOTREACHED */
+			}
+			keystore_file = optarg;
+			break;
+
+		/*
+		 * Public interface: Instruct pkgadd not to use the
+		 * $root_path/etc/vfstab file for determining the client's
+		 * mount points. This option assumes the mount points are
+		 * correct on the server and it behaves consistently with
+		 * Solaris 2.5 and earlier releases.
+		 */
+		case 'M':
+			no_map_client = 1;
+			break;
+
+		/*
+		 * Not a public interface: the -O option allows the behavior
+		 * of the package tools to be modified. Recognized options:
+		 * -> debug
+		 * ---> enable debugging output
+		 * -> addzonename
+		 * ---> add zone name to appropriate messages
+		 * -> nozones
+		 * ---> act as though in global zone with no non-global zones
+		 * -> inherited-filesystems
+		 * ---> add specified file system to list of file systems
+		 * ---> that are inherited from the global zone
+		 * -> enable-hollow-package-support
+		 * ---> Enable hollow package support. When specified, for any
+		 * ---> package that has SUNW_PKG_HOLLOW=true:
+		 * ---> Do not calculate and verify package size against target
+		 * ---> Do not run any package procedure or class action scripts
+		 * ---> Do not create any target directories
+		 * ---> Do not perform any script locking
+		 * ---> Do not install any components of any package
+		 * ---> Do not output any status or database update messages
+		 * -> zonelist="<names...>"
+		 * ---> add package to space/colon separated list of zones only
+		 */
+
+		case 'O':
+			for (p = strtok(optarg, ","); p != (char *)NULL;
+				p = strtok(NULL, ",")) {
+
+				if (strcmp(p, "debug") == 0) {
+					/* set debug flag/enable debug output */
+					debugFlag = B_TRUE;
+					(void) echoDebugSetFlag(debugFlag);
+
+					/* debug info on arguments to pkgadd */
+					for (n = 0; n < argc && argv[n]; n++) {
+						echoDebug(DBG_ARG, n, argv[n]);
+					}
+
+					continue;
+				}
+
+				if (strcmp(p,
+					"enable-hollow-package-support") == 0) {
+					set_depend_pkginfo_DB(B_TRUE);
+					continue;
+				}
+
+				if (strncmp(p, INHERITFS, INHERITFS_LEN) == 0) {
+					if (z_add_inherited_file_system(
+						p+INHERITFS_LEN) == B_FALSE) {
+						progerr(ERR_NOSUCH_INHERITED,
+							p+INHERITFS_LEN);
+						quit(1);
+						/* NOTREACHED */
+					}
+					continue;
+				}
+
+				if (strcmp(p, "addzonename") == 0) {
+					quitSetZoneName(z_get_zonename());
+					continue;
+				}
+
+				if (strcmp(p, "nozones") == 0) {
+					noZones = B_TRUE;
+					continue;
+				}
+
+				/*
+				 * Private interface: package is being
+				 * installed as a patch package.
+				 */
+
+				if (strcmp(p, "patchPkgInstall") == 0) {
+					setPatchUpdate();
+					continue;
+				}
+
+				/*
+				 * If this is a patch removal
+				 * then call setPatchUpdate() and set
+				 * patchPkgRemoval flag.
+				 */
+				if (strcmp(p, "patchPkgRemoval") == 0) {
+					setPatchUpdate();
+					patchPkgRemoval = B_TRUE;
+					continue;
+				}
+
+				if (strncmp(p, "zonelist=", 9) == 0) {
+					/*
+					 * If colons used as separators,
+					 * convert to spaces.
+					 */
+					q = p + 9;
+					while (*q != '\0') {
+						if (*q == ':') {
+							*q = ' ';
+						}
+						q++;
+					}
+
+					if (z_set_zone_spec(p + 9) == -1)
+						quit(1);
+					usedZoneList = B_TRUE;
+					continue;
+				}
+
+				progerr(ERR_INVALID_O_OPTION, p);
+				continue;
+			}
+			break;
+
+		/*
+		 * Public interface: installation occurs in
+		 * non-interactive mode.  Suppress output of the list of
+		 * installed files. The default mode is interactive.
+		 */
+		case 'n':
+			nointeract++;
+			(void) echoSetFlag(B_FALSE);
+			break;
+
+		/*
+		 * Public interface: Password to use to decrypt keystore
+		 * specified with -k, if required. See PASS PHRASE
+		 * ARGUMENTS for more information about the format of this
+		 * option's argument.
+		 */
+		case 'P':
+			if (optarg[0] == '-') {
+				usage();
+				quit(1);
+			}
+			set_passphrase_passarg(optarg);
+			if (ci_strneq(optarg, "pass:", 5)) {
+				/*
+				 * passwords on the command line are highly
+				 * insecure.  complain.
+				 */
+				logerr(PASSWD_CMDLINE, "pass:<pass>");
+			}
+			break;
+
+		/*
+		 * Public interface: Define the full path name of a
+		 * directory to use as the root_path.  All files,
+		 * including package system information files, are
+		 * relocated to a directory tree starting in the specified
+		 * root_path. The root_path may be specified when
+		 * installing to a client from a server (for example,
+		 * /export/root/client1).
+		 */
+		case 'R':
+			if (optarg[0] == '-') {
+				usage();
+				quit(1);
+			}
+			/* determine the real path specified */
+
+			n = resolvepath(optarg, Rpath, sizeof (Rpath)-1);
+
+			/* use supplied path if not resolvable */
+
+			if (n == -1) {
+				(void) strlcpy(Rpath, optarg, sizeof (Rpath));
+			} else {
+				/* null terminate string */
+				Rpath[n] = '\0';
+			}
+
+			/* set the alternative root path */
+
+			if (!set_inst_root(Rpath)) {
+				progerr(ERR_ROOT_CMD);
+				exit(1);
+			}
+			break;
+
+		/*
+		 * Public interface: Identify a file or directory which
+		 * contains output from a previous pkgask(1M)
+		 * session. This file supplies the interaction responses
+		 * that would be requested by the package in interactive
+		 * mode. response must be a full pathname.
+		 */
+		case 'r':
+			if (optarg[0] == '-') {
+				usage();
+				quit(1);
+			}
+			respfile = flex_device(optarg, 2);
+			if (isdir(respfile) == 0)
+				respdir = respfile;
+			break;
+
+		/*
+		 * Not a public interface: suppress copyright notice being
+		 * output during installation.
+		 */
+		case 'S':
+			suppressCopyright++;
+			break;
+
+		/*
+		 * Public interface: Write the package into the directory
+		 * spool instead of installing it. The default directory
+		 * for spooled packages is /var/sadm/pkg.
+		 */
+		case 's':
+			spoolDir = flex_device(optarg, 1);
+			break;
+
+		/*
+		 * Not a public interface: disable save spool area creation;
+		 * suppress the creation and population of the package save
+		 * spool area (var/sadm/pkg/PKG/save/pspool/PKG).
+		 */
+		case 't':
+			disableSaveSpool++;
+			break;
+
+		/*
+		 * Public interface: Specify an alternative fs_file to map
+		 * the client's file systems.  For example, used in
+		 * situations where the $root_path/etc/vfstab file is
+		 * non-existent or unreliable. Informs the pkginstall
+		 * portion to mount up a client filesystem based upon the
+		 * supplied vfstab-like file of stable format.
+		 */
+		case 'V':
+			vfstab_file = flex_device(optarg, 2);
+			no_map_client = 0;
+			break;
+
+		/*
+		 * Public interface: Trace all of the scripts that get
+		 * executed by pkgadd, located in the pkginst/install
+		 * directory. This option is used for debugging the
+		 * procedural and non-procedural scripts
+		 */
+		case 'v':
+			pkgverbose++;
+			break;
+
+		/*
+		 * Public interface: Specify a HTTP[S] proxy to use when
+		 * downloading packages The format of proxy is host:port,
+		 * where host is the hostname of the HTTP[S] proxy, and
+		 * port is the port number associated with the proxy. This
+		 * switch overrides all other methods of specifying a
+		 * proxy. See ENVIRONMENT VARIABLES for more information
+		 * on alternate methods of specifying a default proxy.
+		 */
+		case 'x':
+			if (!path_valid(optarg)) {
+				progerr(ERR_PATH, optarg);
+				quit(1);
+				/* NOTREACHED */
+			}
+			proxy = optarg;
+			break;
+
+		/*
+		 * Public interface: Install packages based on the value
+		 * of the CATEGORY parameter stored in the package's
+		 * pkginfo(4) file. All packages on the source medium
+		 * whose CATEGORY matches one of the specified categories
+		 * will be selected for installation or spooling. Install
+		 * packages that contain the same CATEGORY as the one
+		 * provided on the command line.
+		 */
+		case 'Y':
+			if (optarg[0] == '-') {
+				usage();
+				quit(1);
+			}
+			catg_arg = strdup(optarg);
+
+			if ((category = get_categories(catg_arg)) == NULL) {
+				progerr(ERR_CAT_INV, catg_arg);
+				exit(1);
+			} else if (is_not_valid_length(category)) {
+				progerr(ERR_CAT_LNGTH);
+				exit(1);
+			}
+			break;
+
+		/*
+		 * Not a public interface: perform fresh install from
+		 * package save spool area. When set, the package contents
+		 * are installed from the package spool save area instead
+		 * of from the package root area, so that the original
+		 * source packages are not required to install the
+		 * package. If the -h option is also specified and the
+		 * package is hollow, then this option is ignored. When -z
+		 * is specified:
+		 *  - Editable files are installed from the package instance
+		 *    save area.
+		 *  - Volatile files are installed from the package instance
+		 *    save area.
+		 *  - Executable and data files are installed from the final
+		 *    installed location as specified in the pkgmap file.
+		 *  - Installation scripts are run from the package spool
+		 *    save area.
+		 */
+		case 'z':
+			saveSpoolInstall++;
+			break;
+
+		/*
+		 * unrecognized option
+		 */
+
+		default:
+			usage();
+			return (1);
+		}
+	}
+
+	/*
+	 * ********************************************************************
+	 * validate command line options
+	 * ********************************************************************
+	 */
+
+	/* set "debug echo" flag according to setting of "-O debug" option */
+
+	(void) echoDebugSetFlag(debugFlag);
+
+	/* output entry debugging information */
+
+	if (z_running_in_global_zone()) {
+		echoDebug(DBG_ENTRY_IN_GZ, prog_full_name);
+	} else {
+		echoDebug(DBG_ENTRY_IN_LZ, prog_full_name, getzoneid(),
+			z_get_zonename());
+	}
+
+	/*
+	 * Later, it may be decided to pursue this ability to continue to an
+	 * actual installation based only on the dryrun data. At this time,
+	 * it is too risky.
+	 */
+
+	if (pkgcontsrc && !pkgdrtarg) {
+		progerr(ERR_NO_LIVE_MODE);
+		usage();
+		return (1);
+	}
+
+	/* ignore -G option if not used in the global zone */
+
+	if (!z_running_in_global_zone()) {
+		globalZoneOnly = B_FALSE;
+	}
+
+	/* if zonelist used, must be in global zone */
+
+	if (usedZoneList && !z_running_in_global_zone()) {
+		progerr(ERR_Z_USED_IN_NONGLOBAL_ZONE);
+		return (1);
+	}
+
+	/* -G and zonelist cannot be used together */
+
+	if (globalZoneOnly && usedZoneList) {
+		progerr(ERR_GZ_USED_TOGETHER);
+		usage();
+		return (1);
+	}
+
+	/* -s cannot be used with either -G or zonelist */
+
+	if (spoolDir != NULL) {
+		if (globalZoneOnly) {
+			progerr(ERR_SPOOLDIR_USED_WITH_G);
+			usage();
+			return (1);
+		}
+		if (usedZoneList) {
+			progerr(ERR_SPOOLDIR_USED_WITH_Z);
+			usage();
+			return (1);
+		}
+		if (strcmp(spoolDir, "/var/sadm/pkg") == 0) {
+			progerr(ERR_SPOOLDIR_CANNOT_BE_SYS, "/var/sadm/pkg");
+			usage();
+			return (1);
+		}
+	}
+
+	/* pkgask does not support the same options as pkgadd */
+
+	if (askflag && proxy) {
+		progerr(ERR_PKGASK_AND_PROXY);
+		usage();
+		return (1);
+	}
+
+	if (askflag && uri) {
+		progerr(ERR_PKGASK_AND_URI);
+		usage();
+		return (1);
+	}
+
+	if (askflag && keystore_file) {
+		progerr(ERR_PKGASK_AND_KEYSTORE_FILE);
+		usage();
+		return (1);
+	}
+
+	if (askflag && ignore_sig) {
+		progerr(ERR_PKGASK_AND_IGNORE_SIG);
+		usage();
+		return (1);
+	}
+
+	if (askflag && spoolDir) {
+		progerr(ERR_PKGASK_AND_SPOOLDIR);
+		usage();
+		return (1);
+	}
+
+	if (askflag && nointeract) {
+		progerr(ERR_PKGASK_AND_NOINTERACT);
+		usage();
+		return (1);
+	}
+
+	/* cannot use response file and web address together */
+
+	if (respfile && uri) {
+		progerr(ERR_RESPFILE_AND_URI);
+		usage();
+		return (1);
+	}
+
+	/* cannot use response file/not-interactive and spool-to directory */
+
+	if (spoolDir && nointeract) {
+		progerr(ERR_SPOOLDIR_AND_NOINTERACT);
+		usage();
+		return (1);
+	}
+
+	if (spoolDir && respfile) {
+		progerr(ERR_SPOOLDIR_AND_RESPFILE);
+		usage();
+		return (1);
+	}
+
+	if (usedZoneList) {
+		/* Verify supplied zone list valid for the target */
+		if (z_verify_zone_spec() == -1)
+			return (1);
+
+		/* -z zonelist=global is logically the same as -G */
+		if (z_global_only() && z_running_in_global_zone())
+			globalZoneOnly = B_TRUE;
+	}
+
+	/*
+	 * hook SIGINT and SIGHUP interrupts into quit.c's trap handler
+	 */
+
+	/* hold SIGINT/SIGHUP interrupts */
+
+	(void) sighold(SIGHUP);
+	(void) sighold(SIGINT);
+
+	/* connect quit.c:trap() to SIGINT */
+
+	nact.sa_handler = quitGetTrapHandler();
+	nact.sa_flags = SA_RESTART;
+	(void) sigemptyset(&nact.sa_mask);
+
+	(void) sigaction(SIGINT, &nact, &oact);
+
+	/* connect quit.c:trap() to SIGHUP */
+
+	nact.sa_handler = quitGetTrapHandler();
+	nact.sa_flags = SA_RESTART;
+	(void) sigemptyset(&nact.sa_mask);
+
+	(void) sigaction(SIGHUP, &nact, &oact);
+
+	/* release hold on signals */
+
+	(void) sigrelse(SIGHUP);
+	(void) sigrelse(SIGINT);
+
+	/*
+	 * This function is in the libadm library; it sets:
+	 * -> get_PKGLOC() = <install_root>/var/sadm/pkg
+	 * -> get_PKGOLD() = <install_root>/usr/options
+	 * -> get_PKGADM() = <install_root>/var/sadm/install
+	 * -> pkgdir = <install_root>/var/sadm/pkg
+	 * -> pkg_install_root = <install_root>
+	 * This controls operations of libadm functions such as:
+	 * -> pkginfofind, pkginfopen, fpkgparam, pkgparam, get_PKGLOC,
+	 * -> get_PKGOLD, get_PKGADM, get_install_root
+	 */
+
+	set_PKGpaths(get_inst_root());
+	echoDebug(DBG_PKGADD_PKGPATHS,
+		get_PKGLOC() ? get_PKGLOC() : "",
+		get_PKGADM() ? get_PKGADM() : "");
+
+	/*
+	 * This function is in the libinst library; it reads the specified
+	 * admin(4) file and, using fpkgparam(), sets the global "adm" structure
+	 * values to match what is in the specified admin file.
+	 */
+
+	echoDebug(DBG_PKGADD_ADMINFILE, admnfile ? admnfile : "");
+	setadminFile(admnfile);
+
+	/*
+	 * if running in the global zone, and non-global zones exist, then
+	 * enable hollow package support so that any packages that are marked
+	 * SUNW_PKG_HOLLOW=true will be correctly installed in non-global zones
+	 * when added directly in the global zone by the global zone admin.
+	 */
+
+	if (is_depend_pkginfo_DB()) {
+		echoDebug(DBG_PKGADD_HOLLOW_ENABLED);
+	} else if ((z_running_in_global_zone() == B_TRUE) &&
+		(z_non_global_zones_exist() == B_TRUE)) {
+		echoDebug(DBG_PKGADD_ENABLING_HOLLOW);
+		set_depend_pkginfo_DB(B_TRUE);
+	}
+
+	/* if no device and no url, get and validate default device */
+
+	if ((device == NULL) && (uri == NULL)) {
+		device = devattr("spool", "pathname");
+		if (device == NULL) {
+			progerr(ERR_NODEVICE);
+			quit(1);
+			/* NOTREACHED */
+		}
+	}
+
+	/* must be root if not directing results to spool directory */
+
+	if ((getuid() != 0) && (spoolDir == NULL)) {
+		progerr(ERR_NOT_ROOT, prog);
+		exit(1);
+	}
+
+	/*
+	 * process response file argument
+	 */
+
+	if (respfile) {
+		echoDebug(DBG_PKGADD_RESPFILE,
+			respfile, respdir ? respdir : "");
+
+		if (respfile[0] != '/') {
+			progerr(ERR_RSP_FILE_NOTFULLPATH, respfile);
+			quit(1);
+			/* NOTREACHED */
+		}
+		if (respdir == NULL) {
+			if (askflag) {
+				if (access(respfile, F_OK) == 0) {
+					progerr(ERR_NORESP, respfile);
+					quit(1);
+					/* NOTREACHED */
+				}
+			} else if (access(respfile, F_OK) != 0) {
+				progerr(ERR_ACCRESP, respfile);
+				quit(1);
+				/* NOTREACHED */
+			}
+		}
+	} else if (askflag) {
+		progerr(ERR_RSP_FILE_NOT_GIVEN);
+		usage();
+		quit(1);
+		/* NOTREACHED */
+	}
+
+	/* establish temporary directory to use */
+
+	if ((tmpdir = getenv("TMPDIR")) == NULL) {
+		/* use default - no override specified */
+		tmpdir = P_tmpdir;
+	}
+
+	echoDebug(DBG_PKGADD_TMPDIR, tmpdir);
+
+	/*
+	 * setup and prepare secure package operations
+	 */
+
+	/* initialize error object used by security functions */
+
+	err = pkgerr_new();
+
+	/* validate keystore file */
+
+	if (!check_keystore_admin(&keystore_file)) {
+		progerr(ERR_ADM_KEYSTORE);
+		quit(1);
+		/* NOTREACHED */
+	}
+
+	/* if uri provided, establish session */
+
+	if (uri != NULL) {
+		boolean_t	b;
+		int		len;
+		char		*bname = (char *)NULL;
+
+		set_web_install();
+
+		if (!get_proxy_port(err, &proxy, &proxy_port)) {
+			pkgerr(err);
+			quit(1);
+			/* NOTREACHED */
+		}
+
+		if (proxy == NULL) {
+			if (!get_proxy_port_admin(&proxy, &proxy_port)) {
+				progerr(ERR_ADM_PROXY);
+				quit(1);
+				/* NOTREACHED */
+			}
+		}
+
+		if ((retries = web_ck_retries()) == 0) {
+			pkgerr(err);
+			quit(1);
+			/* NOTREACHED */
+		}
+
+		if ((timeout = web_ck_timeout()) == 0) {
+			pkgerr(err);
+			quit(1);
+			/* NOTREACHED */
+		}
+
+		/* create temporary directory */
+
+		b = setup_temporary_directory(&dwnld_dir, tmpdir, "dwnld");
+		if (b != B_TRUE) {
+			progerr(ERR_DWNLDTEMPDIR, tmpdir, strerror(errno));
+			quit(1);
+			/* NOTREACHED */
+		}
+		canonize_slashes(dwnld_dir);
+
+		/* register with quit() so directory is removed on exit */
+
+		quitSetDwnldTmpdir(dwnld_dir);	/* DO NOT FREE() */
+
+		/* open keystore if this is a secure download */
+		if (scheme == web_https) {
+			if (open_keystore(err, keystore_file,
+			    get_prog_name(),  pkg_passphrase_cb,
+			    KEYSTORE_DFLT_FLAGS, &keystore) != 0) {
+				pkgerr(err);
+				web_cleanup();
+				quit(1);
+				/* NOTREACHED */
+			}
+		}
+
+		if (!web_session_control(err, uri, dwnld_dir, keystore, proxy,
+			proxy_port, retries, timeout, nointeract, &bname)) {
+			pkgerr(err);
+			web_cleanup();
+			quit(1);
+			/* NOTREACHED */
+		}
+
+		/*
+		 * reset device to point to newly-downloaded file; note
+		 * when (scheme == web_https || scheme == web_http) that
+		 * device gets preloaded with a pointer to PATH_MAX bytes
+		 * allocated via malloc().
+		 */
+
+		len = snprintf(device, PATH_MAX, "%s/%s", dwnld_dir, bname);
+		if ((len < 0) || (len >= PATH_MAX)) {
+			progerr(ERR_DIR_CONST, tmpdir);
+			quit(1);
+			/* NOTREACHED */
+		}
+	}
+
+	/*
+	 * See if user wants this to be handled as an old style pkg.
+	 * NOTE : the ``exception_pkg()'' stuff is to be used only
+	 * through on495. This function comes out for on1095. See
+	 * PSARC 1993-546. -- JST
+	 */
+
+	if (getenv("NONABI_SCRIPTS") != NULL) {
+		old_pkg = 1;
+	}
+
+	/*
+	 * See if the user wants to process symlinks consistent with
+	 * the old behavior.
+	 */
+
+	if (getenv("PKG_NONABI_SYMLINKS") != NULL) {
+		old_symlinks = 1;
+	}
+
+	/*
+	 * See if the user wants the package name length restricted.
+	 */
+
+	abiPtr = getenv("PKG_ABI_NAMELENGTH");
+	if (abiPtr && strncasecmp(abiPtr, "TRUE", 4) == 0) {
+		ABI_namelength = 1;
+	}
+
+	/*
+	 * validate the package source device - return pkgdev info that
+	 * describes the package source device.
+	 */
+
+	if (devtype(device, &pkgdev)) {
+		progerr(ERR_BAD_DEVICE, device);
+		quit(1);
+		/* NOTREACHED */
+	}
+
+	/*
+	 * If writing the packages into a spool directory instead of
+	 * installing the packages, open the package datastream and
+	 * invoke pkgtrans to perform the conversion and exit.
+	 */
+
+	if (spoolDir != (char *)NULL) {
+		boolean_t	b;
+		int		n;
+
+		echoDebug(DBG_INSTALLING_TO_SPOOL, spoolDir);
+
+		b = open_package_datastream(argc, argv, spoolDir, device,
+						&repeat, &ids_name, tmpdir,
+						&pkgdev, optind);
+
+		quitSetIdsName(ids_name);
+
+		if (b != B_TRUE) {
+			progerr(ERR_CANNOT_OPEN_PKG_STREAM, PSTR(device));
+			quit(1);
+		}
+
+		n = pkgtrans(device, spoolDir, &argv[optind],
+				0, NULL, NULL);
+		quit(n);
+		/* NOTREACHED */
+	}
+
+	/*
+	 * error if there are packages on the command line and a category
+	 * was specified
+	 */
+
+	if ((optind < argc) && (catg_arg != NULL)) {
+		progerr(ERR_PKGS_AND_CAT_PKGADD);
+		usage();
+		quit(1);
+		/* NOTREACHED */
+	}
+
+	/*
+	 * ********************************************************************
+	 * main package processing "loop"
+	 * ********************************************************************
+	 */
+
+	ids_name = NULL;
+	quitSetIdsName(ids_name);
+
+	for (;;) {
+		boolean_t	b;
+		char		**pkglist;	/* points to array of pkgs */
+
+		/*
+		 * open next package data stream
+		 */
+
+		b = open_package_datastream(argc, argv, spoolDir, device,
+						&repeat, &ids_name, tmpdir,
+						&pkgdev, optind);
+
+		quitSetIdsName(ids_name);
+
+		if (b == B_FALSE) {
+			echoDebug(ERR_CANNOT_OPEN_PKG_STREAM, PSTR(device));
+			continue;
+		}
+
+		/*
+		 * package source data stream open - get the package list
+		 */
+
+		b = get_package_list(&pkglist, argv, catg_arg, category,
+			ignore_sig, err, proxy_port, proxy, keystore,
+			keystore_file, ids_name, &repeat);
+
+		if (b == B_FALSE) {
+			char	path[PATH_MAX];
+
+			echoDebug(DBG_CANNOT_GET_PKGLIST);
+
+			/* check for existence of pre-SVR4 package */
+			(void) snprintf(path, sizeof (path),
+				"%s/install/INSTALL", pkgdev.dirname);
+			if (access(path, F_OK) == 0) {
+				pkginst = ((optind < argc) ?
+					argv[optind++] : NULL);
+				ckreturn(presvr4(&pkginst, nointeract));
+				if (repeat || (optind < argc)) {
+					continue;
+				}
+				quit(0);
+			}
+			progerr(ERR_NOPKGS, pkgdev.dirname);
+			quit(1);
+			/* NOTREACHED */
+		}
+
+		/*
+		 * count the number of packages to install
+		 * NOTE: npkgs is a global variable that is referenced by quit.c
+		 * when error messages are generated - it is referenced directly
+		 * by the other functions called below...
+		 */
+
+		for (npkgs = 0; pkglist[npkgs] != (char *)NULL; /* void */) {
+			echoDebug(DBG_PKG_SELECTED, npkgs, pkglist[npkgs]);
+			npkgs++;
+		}
+
+		/* output number of packages to be added */
+
+		echoDebug(DBG_NUM_PKGS_TO_ADD, npkgs);
+
+		/*
+		 * if pkgask and response container is a file (not a directory),
+		 * and there is more than one package to install, then it is an
+		 * error - too many packages to install when response container
+		 * is a file.
+		 */
+
+		if ((askflag != 0) && (respdir == (char *)NULL) &&
+			(npkgs > 1)) {
+			progerr(ERR_TOO_MANY_PKGS);
+			quit(1);
+			/* NOTREACHED */
+		}
+
+		/*
+		 * package list generated - add packages
+		 */
+
+		b = add_packages(pkglist, uri, ids_name, repeat,
+					altBinDir, device, noZones);
+
+		/*
+		 * close open input data stream (source package) if left open.
+		 */
+
+		if (ids_name) {
+			echoDebug(DBG_CLOSING_STREAM, ids_name,
+					PSTR(pkgdev.dirname));
+			(void) ds_close(1);
+			rrmdir(pkgdev.dirname);
+			ids_name = NULL;
+			quitSetIdsName(ids_name);
+		}
+
+		/*
+		 * continue with next sequence of packages if continue set
+		 */
+
+		if (b == B_TRUE) {
+			continue;
+		}
+
+		/*
+		 * not continuing - quit with 0 exit code
+		 */
+
+		quit(0);
+		/* NOTREACHED */
+	}
+
+	/* NOTREACHED */
+}
+
+/*
+ * *****************************************************************************
+ * static internal (private) functions
+ * *****************************************************************************
+ */
+
+/*
+ * Name:	pkgZoneCheckInstall
+ * Description:	Invoke pkginstall in a specified zone to perform a preinstall
+ *		check of the a single package in the specified zone
+ * Arguments:	a_zoneName - pointer to string representing the name of the
+ *			zone to check install the package in.
+ *		a_inheritedPkgDirs - pointer to array of strings, each one
+ *			representing the non-global zones full path of a
+ *			directory that is inherited from the global zone.
+ *		a_zoneState - current state of the zone; must be mounted or
+ *			running.
+ *		a_idsName - pointer to string representing the data stream
+ *			device (input data stream) containing the package to
+ *			be check installed.
+ *		a_altBinDir - pointer to string representing an alternative
+ *			binary location directory to pass to pkginstall.
+ *			If this is == NULL no alternative binary location is
+ *			passed to pkginstall.
+ *		a_adminFile - pointer to string representing the admin
+ *			file to pass to pkginstall when installing the package.
+ *			If this is == NULL no admin file is given to pkginstall.
+ *		a_stdoutPath - pointer to string representing the local path
+ *			into which all output written by pkginstall to stdout
+ *			is stored.
+ *			If this is == NULL stdout is redirected to /dev/null
+ * Returns:	int	(see ckreturn() function for details)
+ *		0 - success
+ *		1 - package operation failed (fatal error)
+ *		2 - non-fatal error (warning)
+ *		3 - user selected quit (operation interrupted)
+ *		4 - admin settings prevented operation
+ *		5 - interaction required and -n (non-interactive) specified
+ *		"10" will be added to indicate "immediate reboot required"
+ *		"20" will be added to indicate "reboot after install required"
+ */
+
+static int
+pkgZoneCheckInstall(char *a_zoneName, char **a_inheritedPkgDirs,
+	zone_state_t a_zoneState, char *a_idsName, char *a_altBinDir,
+	char *a_adminFile, char *a_stdoutPath)
+{
+	char	*arg[MAXARGS];
+	char	*p;
+	char	adminfd_path[PATH_MAX];
+	char	path[PATH_MAX];
+	char	pkgstreamfd_path[PATH_MAX];
+	int	fds[MAX_FDS];
+	int	maxfds;
+	int	n;
+	int	nargs;
+
+	/* entry assertions */
+
+	assert(a_zoneName != (char *)NULL);
+	assert(*a_zoneName != '\0');
+
+	/* entry debugging info */
+
+	echoDebug(DBG_PKGZONECHECKINSTALL_ENTRY);
+	echoDebug(DBG_PKGZONECHECKINSTALL_ARGS, a_zoneName, PSTR(pkginst),
+		PSTR(pkgdev.dirname), PSTR(pkgdev.mount), PSTR(pkgdev.bdevice),
+		a_zoneState == ZONE_STATE_MOUNTED ? "/a" : "/",
+		PSTR(a_idsName), PSTR(a_adminFile), PSTR(a_stdoutPath));
+
+	/* generate full path to 'phatinstall' to run in zone */
+
+	(void) snprintf(path, sizeof (path), "%s/pkginstall",
+			"/usr/sadm/install/bin");
+
+	/* start at first file descriptor */
+
+	maxfds = 0;
+
+	/*
+	 * generate argument list for call to pkginstall
+	 */
+
+	/* start at argument 0 */
+
+	nargs = 0;
+
+	/* first argument is always: full path to executable */
+
+	arg[nargs++] = path;
+
+	/*
+	 * second argument is always: pass -O debug to pkginstall: debug mode
+	 */
+	if (debugFlag == B_TRUE) {
+		arg[nargs++] = "-O";
+		arg[nargs++] = "debug";
+	}
+
+	/* pkgadd -G: pass -G to pkginstall */
+
+	if (globalZoneOnly == B_TRUE) {
+		arg[nargs++] = "-G";
+	}
+
+	/* pkgadd -b dir: pass -b to pkginstall */
+
+	if (a_altBinDir != (char *)NULL) {
+		arg[nargs++] = "-b";
+		arg[nargs++] = a_altBinDir;
+	}
+
+	/* pkgadd -C: pass -C to pkginstall: disable checksum */
+
+	if (disableChecksum) {
+		arg[nargs++] = "-C";
+	}
+
+	/* pkgadd -A: pass -A to pkginstall: disable attribute checking */
+
+	if (disableAttributes) {
+		arg[nargs++] = "-A";
+	}
+
+	/*
+	 * NONABI_SCRIPTS defined: pass -o to pkginstall; refers to a
+	 * pkg requiring operator interaction during a procedure script
+	 * (common before on1093)
+	 */
+
+	if (old_pkg) {
+		arg[nargs++] = "-o";
+	}
+
+	/*
+	 * PKG_NONABI_SYMLINKS defined: pass -y to pkginstall; process
+	 * symlinks consistent with old behavior
+	 */
+
+	if (old_symlinks) {
+		arg[nargs++] = "-y";
+	}
+
+	/*
+	 * PKG_ABI_NAMELENGTH defined: pass -e to pkginstall; causes
+	 * package name length to be restricted
+	 */
+
+	if (ABI_namelength) {
+		arg[nargs++] = "-e";
+	}
+
+	/* pkgadd -S: pass -S to pkginstall: suppress copyright notices */
+
+	arg[nargs++] = "-S";
+
+	/* pkgadd -M: pass -M to pkginstall: dont mount client file systems */
+
+	arg[nargs++] = "-M";
+
+	/* pkgadd -v: pass -v to pkginstall: never trace scripts */
+
+	/* if running pkgask, pass -i to pkginstall: running pkgask */
+
+	if (askflag) {
+		return (0);
+	}
+
+	/* pass "-O enable-hollow-package-support" */
+
+	if (is_depend_pkginfo_DB()) {
+		arg[nargs++] = "-O";
+		arg[nargs++] = "enable-hollow-package-support";
+	}
+
+	/* check is always in non-interactive mode */
+
+	arg[nargs++] = "-n";
+
+	/* pkgadd -a admin: pass -a admin to pkginstall in zone: admin file */
+
+	if (a_adminFile) {
+		int fd;
+		fd = openLocal(a_adminFile, O_RDONLY, tmpdir);
+		if (fd < 0) {
+			progerr(ERR_CANNOT_COPY_LOCAL, a_adminFile,
+				errno, strerror(errno));
+			return (1);
+		}
+		(void) snprintf(adminfd_path, sizeof (adminfd_path),
+			"/proc/self/fd/%d", fd);
+		fds[maxfds++] = fd;
+		arg[nargs++] = "-a";
+		arg[nargs++] = adminfd_path;
+	}
+
+	/* pkgadd -R root: pass -R /a to pkginstall when zone is mounted */
+
+	if (a_zoneState == ZONE_STATE_MOUNTED) {
+		arg[nargs++] = "-R";
+		arg[nargs++] = "/a";
+	}
+
+	/* pass -N to pkginstall: program name to report */
+
+	arg[nargs++] = "-N";
+	arg[nargs++] = get_prog_name();
+
+	/* pass "-O preinstallcheck" */
+
+	arg[nargs++] = "-O";
+	arg[nargs++] = "preinstallcheck";
+
+	/* add "-O addzonename" */
+
+	arg[nargs++] = "-O";
+	arg[nargs++] = "addzonename";
+
+	if (isPatchUpdate()) {
+		if (patchPkgRemoval == B_TRUE) {
+			arg[nargs++] = "-O";
+			arg[nargs++] = "patchPkgRemoval";
+		} else {
+			arg[nargs++] = "-O";
+			arg[nargs++] = "patchPkgInstall";
+		}
+	}
+
+	/* add all inherited file systems */
+
+	if (a_inheritedPkgDirs != (char **)NULL) {
+		for (n = 0; a_inheritedPkgDirs[n] != (char *)NULL; n++) {
+			char	ifs[MAXPATHLEN+22];
+			(void) snprintf(ifs, sizeof (ifs),
+				"inherited-filesystem=%s",
+				a_inheritedPkgDirs[n]);
+			arg[nargs++] = "-O";
+			arg[nargs++] = strdup(ifs);
+		}
+	}
+
+	/*
+	 * add parent zone info/type
+	 */
+
+	p = z_get_zonename();
+	if ((p != NULL) && (*p != '\0')) {
+			char	zn[MAXPATHLEN];
+			(void) snprintf(zn, sizeof (zn),
+				"parent-zone-name=%s", p);
+			arg[nargs++] = "-O";
+			arg[nargs++] = strdup(zn);
+	}
+
+	/* current zone type */
+
+	arg[nargs++] = "-O";
+	if (z_running_in_global_zone() == B_TRUE) {
+			char	zn[MAXPATHLEN];
+			(void) snprintf(zn, sizeof (zn),
+				"parent-zone-type=%s",
+				TAG_VALUE_GLOBAL_ZONE);
+			arg[nargs++] = strdup(zn);
+	} else {
+			char	zn[MAXPATHLEN];
+			(void) snprintf(zn, sizeof (zn),
+				"parent-zone-type=%s",
+				TAG_VALUE_NONGLOBAL_ZONE);
+			arg[nargs++] = strdup(zn);
+	}
+
+	/* add in the package stream file */
+
+	if (a_idsName != NULL) {
+		int fd;
+		fd = openLocal(a_idsName, O_RDONLY, tmpdir);
+		if (fd < 0) {
+			progerr(ERR_STREAM_UNAVAILABLE, a_idsName,
+				pkginst, strerror(errno));
+			quit(1);
+		}
+		(void) snprintf(pkgstreamfd_path, sizeof (pkgstreamfd_path),
+			"/proc/self/fd/%d", fd);
+		fds[maxfds++] = fd;
+		arg[nargs++] = pkgstreamfd_path;
+	} else {
+		progerr(ERR_PKGZONEINSTALL_NO_STREAM);
+		quit(1);
+	}
+
+	/* add package instance name */
+
+	arg[nargs++] = pkginst;
+
+	/* terminate the argument list */
+
+	arg[nargs++] = NULL;
+
+	/*
+	 * run the appropriate pkginstall command in the specified zone
+	 */
+
+	if (debugFlag == B_TRUE) {
+		echoDebug(DBG_ZONE_EXEC_ENTER, a_zoneName, arg[0]);
+		for (n = 0; arg[n]; n++) {
+			echoDebug(DBG_ARG, n, arg[n]);
+		}
+	}
+
+	/* terminate file descriptor list */
+
+	fds[maxfds] = -1;
+
+	/* exec command in zone */
+
+	n = z_zone_exec(a_zoneName, path, arg, a_stdoutPath, (char *)NULL, fds);
+
+	echoDebug(DBG_ZONE_EXEC_EXIT, a_zoneName, arg[0], n,
+			PSTR(a_stdoutPath));
+
+	/*
+	 * close any files that were opened for use by the
+	 * /proc/self/fd interface so they could be passed to programs
+	 * via the z_zone_exec() interface
+	 */
+
+	for (; maxfds > 0; maxfds--) {
+		(void) close(fds[maxfds-1]);
+	}
+
+	/* return results of pkginstall in zone execution */
+
+	return (n);
+}
+
+/*
+ * Name:	pkgZoneInstall
+ * Description:	Invoke pkginstall in a specified zone to perform an install
+ *		of a single package in the specified zone
+ * Arguments:	a_zoneName - pointer to string representing the name of the
+ *			zone to install the package in.
+ *		a_inheritedPkgDirs - pointer to array of strings, each one
+ *			representing the non-global zones full path of a
+ *			directory that is inherited from the global zone.
+ *		a_zoneState - current state of the zone; must be mounted or
+ *			running.
+ *		a_idsName - pointer to string representing the data stream
+ *			device (input data stream) containing the package to
+ *			be installed.
+ *		a_altBinDir - pointer to string representing an alternative
+ *			binary location directory to pass to pkginstall.
+ *			If this is == NULL no alternative binary location is
+ *			passed to pkginstall.
+ *		a_adminFile - pointer to string representing the admin
+ *			file to pass to pkginstall when installing the package.
+ *			If this is == NULL no admin file is given to pkginstall.
+ *		a_stdoutPath - pointer to string representing the local path
+ *			into which all output written by pkginstall to stdout
+ *			is stored.
+ *			If this is == NULL stdout is redirected to /dev/null
+ * Returns:	int	(see ckreturn() function for details)
+ *		0 - success
+ *		1 - package operation failed (fatal error)
+ *		2 - non-fatal error (warning)
+ *		3 - user selected quit (operation interrupted)
+ *		4 - admin settings prevented operation
+ *		5 - interaction required and -n (non-interactive) specified
+ *		"10" will be added to indicate "immediate reboot required"
+ *		"20" will be added to indicate "reboot after install required"
+ */
+
+static int
+pkgZoneInstall(char *a_zoneName, char **a_inheritedPkgDirs,
+    zone_state_t a_zoneState, char *a_idsName, char *a_altBinDir,
+    char *a_adminFile)
+{
+	char	*arg[MAXARGS];
+	char	*p;
+	char	adminfd_path[PATH_MAX];
+	char	path[PATH_MAX];
+	char	pkgstreamfd_path[PATH_MAX];
+	char	respfilefd_path[PATH_MAX];
+	int	fds[MAX_FDS];
+	int	maxfds;
+	int	n;
+	int	nargs;
+
+	/* entry assertions */
+
+	assert(a_zoneName != (char *)NULL);
+	assert(*a_zoneName != '\0');
+
+	/* entry debugging info */
+
+	echoDebug(DBG_PKGZONEINSTALL_ENTRY);
+	echoDebug(DBG_PKGZONEINSTALL_ARGS, a_zoneName, PSTR(pkginst),
+		PSTR(pkgdev.dirname), PSTR(pkgdev.mount), PSTR(pkgdev.bdevice),
+		a_zoneState == ZONE_STATE_MOUNTED ? "/a" : "", PSTR(a_idsName),
+		a_adminFile);
+
+	/* generate path to pkginstall */
+
+	(void) snprintf(path, sizeof (path), "%s/pkginstall", PKGBIN);
+
+	/* start at first file descriptor */
+
+	maxfds = 0;
+
+	/*
+	 * generate argument list for call to pkginstall
+	 */
+
+	/* start at argument 0 */
+
+	nargs = 0;
+
+	/* first argument is path to executable */
+
+	arg[nargs++] = path;
+
+	/*
+	 * second argument is always: pass -O debug to pkginstall: debug mode
+	 */
+	if (debugFlag == B_TRUE) {
+		arg[nargs++] = "-O";
+		arg[nargs++] = "debug";
+	}
+
+	/* pkgadd -G: pass -G to pkginstall */
+
+	if (globalZoneOnly == B_TRUE) {
+		arg[nargs++] = "-G";
+	}
+
+	/* pkgadd -b dir: pass -b to pkginstall in zone */
+
+	if (a_altBinDir != (char *)NULL) {
+		arg[nargs++] = "-b";
+		arg[nargs++] = a_altBinDir;
+	}
+
+	/* pkgadd -B blocksize: pass -B to pkginstall in zone */
+
+	if (rw_block_size != NULL) {
+		arg[nargs++] = "-B";
+		arg[nargs++] = rw_block_size;
+	}
+
+	/* pkgadd -C: pass -C to pkgadd in zone: disable checksum */
+
+	if (disableChecksum) {
+		arg[nargs++] = "-C";
+	}
+
+	/* pkgadd -A: pass -A to pkgadd in zone: disable attribute checking */
+
+	if (disableAttributes) {
+		arg[nargs++] = "-A";
+	}
+
+	/* pkgadd -S: pass -S to pkgadd in zone: suppress copyright notices */
+
+	arg[nargs++] = "-S";
+
+	/* pkgadd -I: pass -I to pkgadd in zone: initial install */
+
+	if (init_install) {
+		arg[nargs++] = "-I";
+	}
+
+	/* pkgadd -M: pass -M to pkgadd in zone: dont mount client file sys */
+
+	arg[nargs++] = "-M";
+
+	/* pkgadd -v: pass -v to pkgadd in zone: trace scripts */
+
+	if (pkgverbose) {
+		arg[nargs++] = "-v";
+	}
+
+	/* pkgadd -z: pass -z to pkgadd in zone fresh inst from pkg save area */
+
+	if (saveSpoolInstall) {
+		arg[nargs++] = "-z";
+	}
+
+	/* pass "-O enable-hollow-package-support" */
+
+	if (is_depend_pkginfo_DB()) {
+		arg[nargs++] = "-O";
+		arg[nargs++] = "enable-hollow-package-support";
+	}
+
+	/* pkgadd -t pass -t to pkgadd in zone disable save spool area create */
+
+	if (disableSaveSpool) {
+		arg[nargs++] = "-t";
+	}
+
+	/* if running pkgask, pass -i to pkgadd in zone: running pkgask */
+
+	if (askflag) {
+		echo(MSG_BYPASSING_ZONE, a_zoneName);
+		return (0);
+	}
+
+	/*
+	 * pkgadd -n (not pkgask): pass -n to pkginstall: noninteractive mode
+	 */
+	if (nointeract && !askflag) {
+		arg[nargs++] = "-n";
+	}
+
+	/* pkgadd -a admin: pass -a admin to pkginstall in zone: admin file */
+
+	if (a_adminFile) {
+		int fd;
+		fd = openLocal(a_adminFile, O_RDONLY, tmpdir);
+		if (fd < 0) {
+			progerr(ERR_CANNOT_COPY_LOCAL, a_adminFile,
+				errno, strerror(errno));
+			return (1);
+		}
+		(void) snprintf(adminfd_path, sizeof (adminfd_path),
+			"/proc/self/fd/%d", fd);
+		fds[maxfds++] = fd;
+		arg[nargs++] = "-a";
+		arg[nargs++] = adminfd_path;
+	}
+
+	/* pkgadd -R root: pass -R /a to pkginstall when zone is mounted */
+	if (a_zoneState == ZONE_STATE_MOUNTED) {
+		arg[nargs++] = "-R";
+		arg[nargs++] = "/a";
+	}
+
+	/*
+	 * pkgadd -D arg: pass -D dryrun to pkginstall in zone: dryrun
+	 * mode/file
+	 */
+	if (pkgdrtarg) {
+		arg[nargs++] = "-D";
+		arg[nargs++] = pkgdrtarg;
+	}
+
+	/*
+	 * pkgadd -c cont: pass -c cont to pkginstall in zone: continuation
+	 * file
+	 */
+	if (pkgcontsrc) {
+		arg[nargs++] = "-c";
+		arg[nargs++] = pkgcontsrc;
+	}
+
+	/* pkgadd -r resp: pass -r resp to pkginstall in zone: response file */
+
+	if (respfile) {
+		int fd;
+		fd = openLocal(respfile, O_RDONLY, tmpdir);
+		if (fd < 0) {
+			progerr(ERR_CANNOT_COPY_LOCAL, a_adminFile,
+				errno, strerror(errno));
+			return (1);
+		}
+		(void) snprintf(respfilefd_path,
+			sizeof (respfilefd_path),
+			"/proc/self/fd/%d", fd);
+		fds[maxfds++] = fd;
+		arg[nargs++] = "-r";
+		arg[nargs++] = respfilefd_path;
+	}
+
+	/* add "-O addzonename" */
+
+	arg[nargs++] = "-O";
+	arg[nargs++] = "addzonename";
+
+	if (isPatchUpdate()) {
+		if (patchPkgRemoval == B_TRUE) {
+			arg[nargs++] = "-O";
+			arg[nargs++] = "patchPkgRemoval";
+		} else {
+			arg[nargs++] = "-O";
+			arg[nargs++] = "patchPkgInstall";
+		}
+	}
+
+	/* add all inherited file systems */
+
+	if (a_inheritedPkgDirs != (char **)NULL) {
+		for (n = 0; a_inheritedPkgDirs[n] != (char *)NULL; n++) {
+			char	ifs[MAXPATHLEN+22];
+			(void) snprintf(ifs, sizeof (ifs),
+				"inherited-filesystem=%s",
+				a_inheritedPkgDirs[n]);
+			arg[nargs++] = "-O";
+			arg[nargs++] = strdup(ifs);
+		}
+	}
+
+	/*
+	 * add parent zone info/type
+	 */
+
+	p = z_get_zonename();
+	if ((p != NULL) && (*p != '\0')) {
+			char	zn[MAXPATHLEN];
+			(void) snprintf(zn, sizeof (zn),
+				"parent-zone-name=%s", p);
+			arg[nargs++] = "-O";
+			arg[nargs++] = strdup(zn);
+	}
+
+	/* current zone type */
+
+	arg[nargs++] = "-O";
+	if (z_running_in_global_zone() == B_TRUE) {
+			char	zn[MAXPATHLEN];
+			(void) snprintf(zn, sizeof (zn),
+				"parent-zone-type=%s",
+				TAG_VALUE_GLOBAL_ZONE);
+			arg[nargs++] = strdup(zn);
+	} else {
+			char	zn[MAXPATHLEN];
+			(void) snprintf(zn, sizeof (zn),
+				"parent-zone-type=%s",
+				TAG_VALUE_NONGLOBAL_ZONE);
+			arg[nargs++] = strdup(zn);
+	}
+
+	/* add in the package stream file */
+
+	if (a_idsName != NULL) {
+		int fd;
+		fd = openLocal(a_idsName, O_RDONLY, tmpdir);
+		if (fd < 0) {
+			progerr(ERR_STREAM_UNAVAILABLE, a_idsName,
+				pkginst, strerror(errno));
+			quit(1);
+		}
+		(void) snprintf(pkgstreamfd_path, sizeof (pkgstreamfd_path),
+			"/proc/self/fd/%d", fd);
+		fds[maxfds++] = fd;
+		arg[nargs++] = pkgstreamfd_path;
+	} else {
+		progerr(ERR_PKGZONEINSTALL_NO_STREAM);
+		quit(1);
+	}
+
+	/* add package instance name */
+
+	arg[nargs++] = pkginst;
+
+	/* terminate the argument list */
+
+	arg[nargs++] = NULL;
+
+	/*
+	 * run the appropriate pkginstall command in the specified zone
+	 */
+
+	if (debugFlag == B_TRUE) {
+		echoDebug(DBG_ZONE_EXEC_ENTER, a_zoneName, arg[0]);
+		for (n = 0; arg[n]; n++) {
+			echoDebug(DBG_ARG, n, arg[n]);
+		}
+	}
+
+	/* terminate file descriptor list */
+
+	fds[maxfds] = -1;
+
+	/* exec command in zone */
+
+	n = z_zone_exec(a_zoneName, path, arg, (char *)NULL, (char *)NULL, fds);
+
+	echoDebug(DBG_ZONE_EXEC_EXIT, a_zoneName, arg[0], n, "");
+
+	/*
+	 * close any files that were opened for use by the
+	 * /proc/self/fd interface so they could be passed to programs
+	 * via the z_zone_exec() interface
+	 */
+
+	for (; maxfds > 0; maxfds--) {
+		(void) close(fds[maxfds-1]);
+	}
+
+	/* return results of pkginstall in zone execution */
+
+	return (n);
+}
+
+/*
+ * Name:	pkgInstall
+ * Description:	Invoke pkginstall in the current zone to perform an install
+ *		of a single package to the current zone or standalone system
+ * Arguments:	a_altRoot - pointer to string representing the alternative
+ *			root to use for the install
+ *		a_idsName - pointer to string representing the data stream
+ *			device (input data stream) containing the package to
+ *			be installed.
+ *		a_pkgDir - pointer to string representing the path to the
+ *			directory containing the package
+ *		a_altBinDir - pointer to string representing location of the
+ *			pkginstall executable to run. If not NULL, then pass
+ *			the path specified to the -b option to pkginstall.
+ *		a_inheritedPkgDirs - pointer to array of strings, each one
+ *			representing the non-global zones full path of a
+ *			directory that is inherited from the global zone.
+ * Returns:	int	(see ckreturn() function for details)
+ *		0 - success
+ *		1 - package operation failed (fatal error)
+ *		2 - non-fatal error (warning)
+ *		3 - user selected quit (operation interrupted)
+ *		4 - admin settings prevented operation
+ *		5 - interaction required and -n (non-interactive) specified
+ *		"10" will be added to indicate "immediate reboot required"
+ *		"20" will be added to indicate "reboot after install required"
+ * NOTE:	Both a_idsName and a_pkgDir are used to determine where the
+ *		package to be installed is located. If a_idsName is != NULL
+ *		then it must be the path to a device containing a package
+ *		stream that contains the package to be installed. If a_idsName
+ *		is == NULL then a_pkgDir must contain a full path to a directory
+ *		that contains the package to be installed.
+ */
+
+static int
+pkgInstall(char *a_altRoot, char *a_idsName, char *a_pkgDir, char *a_altBinDir,
+	char **a_inheritedPkgDirs)
+{
+	char	*arg[MAXARGS];
+	char	*p;
+	char	path[PATH_MAX];
+	char	buffer[256];
+	int	n, nargs;
+
+	/* entry debugging info */
+
+	echoDebug(DBG_PKGINSTALL_ENTRY);
+	echoDebug(DBG_PKGINSTALL_ARGS, PSTR(pkginst), PSTR(pkgdev.dirname),
+		PSTR(pkgdev.mount), PSTR(pkgdev.bdevice), PSTR(a_altRoot),
+		PSTR(a_idsName), PSTR(a_pkgDir));
+
+	/* generate full path to 'pkginstall' to run in zone */
+
+	(void) snprintf(path, sizeof (path), "%s/pkginstall",
+		a_altBinDir == (char *)NULL ? PKGBIN : a_altBinDir);
+	/*
+	 * generate argument list for call to pkginstall
+	 */
+
+	/* start at argument 0 */
+
+	nargs = 0;
+
+	/* first argument is path to executable */
+
+	arg[nargs++] = path;
+
+	/*
+	 * second argument is always: pass -O debug to pkginstall: debug mode
+	 */
+	if (debugFlag == B_TRUE) {
+		arg[nargs++] = "-O";
+		arg[nargs++] = "debug";
+	}
+
+	/* Installation is from a patch package. */
+
+	if (isPatchUpdate()) {
+		if (patchPkgRemoval == B_TRUE) {
+			arg[nargs++] = "-O";
+			arg[nargs++] = "patchPkgRemoval";
+		} else {
+			arg[nargs++] = "-O";
+			arg[nargs++] = "patchPkgInstall";
+		}
+	}
+
+	/*
+	 * pkgadd -G: pass -G to pkginstall if:
+	 *  - the -G option is specified on the pkgadd command line
+	 *  - this package is marked 'this zone only':
+	 *  -- package has SUNW_PKG_THISZONE=true, or
+	 *  -- package has a request script
+	 * Setting -G for pkginstall causes pkginstall to install the package
+	 * in the target zone. If running in the global zone, will install the
+	 * package and mark the package as installed "in the global zone only".
+	 * If running in a non-global zone, will just install the package.
+	 */
+
+	if (globalZoneOnly == B_TRUE) {
+		arg[nargs++] = "-G";
+	} else if (pkgPackageIsThisZone(pkginst) == B_TRUE) {
+		arg[nargs++] = "-G";
+	}
+
+	/* pkgadd -b dir: pass -b to pkginstall */
+
+	if (a_altBinDir != (char *)NULL) {
+		arg[nargs++] = "-b";
+		arg[nargs++] = a_altBinDir;
+	}
+
+	/* pkgadd -B blocksize: pass -B to pkginstall */
+
+	if (rw_block_size != NULL) {
+		arg[nargs++] = "-B";
+		arg[nargs++] = rw_block_size;
+	}
+
+	/* pkgadd -C: pass -C to pkginstall: disable checksum */
+
+	if (disableChecksum) {
+		arg[nargs++] = "-C";
+	}
+
+	/* pkgadd -A: pass -A to pkginstall: disable attribute checking */
+
+	if (disableAttributes) {
+		arg[nargs++] = "-A";
+	}
+
+	/*
+	 * NONABI_SCRIPTS defined: pass -o to pkginstall; refers to a
+	 * pkg requiring operator interaction during a procedure script
+	 * (common before on1093)
+	 */
+
+	if (old_pkg) {
+		arg[nargs++] = "-o";
+	}
+
+	/*
+	 * PKG_NONABI_SYMLINKS defined: pass -y to pkginstall; process
+	 * symlinks consistent with old behavior
+	 */
+
+	if (old_symlinks) {
+		arg[nargs++] = "-y";
+	}
+
+	/*
+	 * PKG_ABI_NAMELENGTH defined: pass -e to pkginstall; causes
+	 * package name length to be restricted
+	 */
+
+	if (ABI_namelength) {
+		arg[nargs++] = "-e";
+	}
+
+	/* pkgadd -S: pass -S to pkginstall: suppress copyright notices */
+
+	if (suppressCopyright) {
+		arg[nargs++] = "-S";
+	}
+
+	/* pkgadd -I: pass -I to pkginstall: initial install being performed */
+
+	if (init_install) {
+		arg[nargs++] = "-I";
+	}
+
+	/* pkgadd -M: pass -M to pkginstall: dont mount client file systems */
+
+	if (no_map_client) {
+		arg[nargs++] = "-M";
+	}
+
+	/* pkgadd -v: pass -v to pkginstall: trace scripts */
+
+	if (pkgverbose) {
+		arg[nargs++] = "-v";
+	}
+
+	/* pkgadd -z: pass -z to pkginstall: fresh install from pkg save area */
+
+	if (saveSpoolInstall) {
+		arg[nargs++] = "-z";
+	}
+
+	/*
+	 * if running in a non-global zone and the 'hollow' attribute is
+	 * passed in, then pass -h to pkginstall so that it knows how to
+	 * handle hollow packages for this local zone.
+	 */
+
+	if (!z_running_in_global_zone() && is_depend_pkginfo_DB()) {
+		arg[nargs++] = "-h";
+	}
+
+	/* pkgadd -t: pass -t to pkginstall: disable save spool area creation */
+
+	if (disableSaveSpool) {
+		arg[nargs++] = "-t";
+	}
+
+	/* if running pkgask, pass -i to pkginstall: running pkgask */
+
+	if (askflag) {
+		arg[nargs++] = "-i";
+	}
+
+	/* pkgadd -n (not pkgask): pass -n to pkginstall: noninteractive mode */
+
+	if (nointeract && !askflag) {
+		arg[nargs++] = "-n";
+	}
+
+	/* pkgadd -a admin: pass -a admin to pkginstall: admin file */
+
+	if (admnfile) {
+		arg[nargs++] = "-a";
+		arg[nargs++] = admnfile;
+	}
+
+	/* pkgadd -D dryrun: pass -D dryrun to pkginstall: dryrun mode/file */
+
+	if (pkgdrtarg) {
+		arg[nargs++] = "-D";
+		arg[nargs++] = pkgdrtarg;
+	}
+
+	/* pkgadd -c cont: pass -c cont to pkginstall: continuation file */
+
+	if (pkgcontsrc) {
+		arg[nargs++] = "-c";
+		arg[nargs++] = pkgcontsrc;
+	}
+
+	/* pkgadd -V vfstab: pass -V vfstab to pkginstall: alternate vfstab */
+
+	if (vfstab_file) {
+		arg[nargs++] = "-V";
+		arg[nargs++] = vfstab_file;
+	}
+
+	/* pkgadd -r resp: pass -r resp to pkginstall: response file */
+
+	if (respfile) {
+		arg[nargs++] = "-r";
+		arg[nargs++] = respfile;
+	}
+
+	/* pkgadd -R root: pass -R root to pkginstall: alternative root */
+
+	if (a_altRoot && *a_altRoot) {
+		arg[nargs++] = "-R";
+		arg[nargs++] = a_altRoot;
+	}
+
+	/*
+	 * If input data stream is available,
+	 * - add: -d ids_name -p number_of_parts
+	 * else,
+	 * - add: -d device -m mount [-f type]
+	 */
+
+	if (a_idsName != NULL) {
+		arg[nargs++] = "-d";
+		arg[nargs++] = a_idsName;
+		arg[nargs++] = "-p";
+		ds_close(1);
+		ds_putinfo(buffer);
+		arg[nargs++] = buffer;
+	} else if (pkgdev.mount != NULL) {
+		arg[nargs++] = "-d";
+		arg[nargs++] = pkgdev.bdevice;
+		arg[nargs++] = "-m";
+		arg[nargs++] = pkgdev.mount;
+		if (pkgdev.fstyp != NULL) {
+			arg[nargs++] = "-f";
+			arg[nargs++] = pkgdev.fstyp;
+		}
+	}
+
+	/* add all inherited file systems */
+
+	if (a_inheritedPkgDirs != (char **)NULL) {
+		for (n = 0; a_inheritedPkgDirs[n] != (char *)NULL; n++) {
+			char	ifs[MAXPATHLEN+22];
+			(void) snprintf(ifs, sizeof (ifs),
+				"inherited-filesystem=%s",
+				a_inheritedPkgDirs[n]);
+			arg[nargs++] = "-O";
+			arg[nargs++] = strdup(ifs);
+		}
+	}
+
+	/*
+	 * add parent zone info/type
+	 */
+
+	p = z_get_zonename();
+	if ((p != NULL) && (*p != '\0')) {
+			char	zn[MAXPATHLEN];
+			(void) snprintf(zn, sizeof (zn),
+				"parent-zone-name=%s", p);
+			arg[nargs++] = "-O";
+			arg[nargs++] = strdup(zn);
+	}
+
+	/* current zone type */
+
+	arg[nargs++] = "-O";
+	if (z_running_in_global_zone() == B_TRUE) {
+			char	zn[MAXPATHLEN];
+			(void) snprintf(zn, sizeof (zn),
+				"parent-zone-type=%s",
+				TAG_VALUE_GLOBAL_ZONE);
+			arg[nargs++] = strdup(zn);
+	} else {
+			char	zn[MAXPATHLEN];
+			(void) snprintf(zn, sizeof (zn),
+				"parent-zone-type=%s",
+				TAG_VALUE_NONGLOBAL_ZONE);
+			arg[nargs++] = strdup(zn);
+	}
+
+	/* pass -N to pkginstall: program name to report */
+
+	arg[nargs++] = "-N";
+	arg[nargs++] = get_prog_name();
+
+	/* add package directory name */
+
+	arg[nargs++] = a_pkgDir;
+
+	/* add package instance name */
+
+	arg[nargs++] = pkginst;
+
+	/* terminate the argument list */
+
+	arg[nargs++] = NULL;
+
+	/*
+	 * run the appropriate pkginstall command in the specified zone
+	 */
+
+	if (debugFlag == B_TRUE) {
+		echoDebug(DBG_ZONE_EXEC_ENTER, "global", arg[0]);
+		for (n = 0; arg[n]; n++) {
+			echoDebug(DBG_ARG, n, arg[n]);
+		}
+	}
+
+	/* execute pkginstall command */
+
+	n = pkgexecv(NULL, NULL, NULL, NULL, arg);
+
+	/* return results of pkginstall execution */
+
+	return (n);
+}
+
+/*
+ *  function to clear out any exisiting error return conditions that may have
+ *  been set by previous calls to ckreturn()
+ */
+static void
+resetreturn()
+{
+	admnflag = 0;	/* != 0 if any pkg op admin setting failure (4) */
+	doreboot = 0;	/* != 0 if reboot required after installation (>= 10) */
+	failflag = 0;	/* != 0 if fatal error has occurred (1) */
+	intrflag = 0;	/* != 0 if user selected quit (3) */
+	ireboot = 0;	/* != 0 if immediate reboot required (>= 20) */
+	nullflag = 0;	/* != 0 if admin interaction required (5) */
+	warnflag = 0;	/* != 0 if non-fatal error has occurred (2) */
+	interrupted = 0;	/* last pkg op was quit (1,2,3,4,5) */
+	needconsult = 0;	/* essential ask admin now (1,2,3,5) */
+}
+
+/*
+ *  function which checks the indicated return value
+ *  and indicates disposition of installation
+ */
+static void
+ckreturn(int retcode)
+{
+	/*
+	 * entry debugging info
+	 */
+
+	echoDebug(DBG_PKGADD_CKRETURN, retcode, PSTR(pkginst));
+
+	/* reset needconsult so it only reflects this call to ckreturn */
+	needconsult = 0;
+
+	switch (retcode) {
+	    case  0:		/* successful */
+	    case 10:
+	    case 20:
+		break; /* empty case */
+
+	    case  1:		/* package operation failed (fatal error) */
+	    case 11:
+	    case 21:
+		failflag++;
+		interrupted++;
+		needconsult++;
+		break;
+
+	    case  2:		/* non-fatal error (warning) */
+	    case 12:
+	    case 22:
+		warnflag++;
+		interrupted++;
+		needconsult++;
+		break;
+
+	    case  3:		/* user selected quit; operation interrupted */
+	    case 13:
+	    case 23:
+		intrflag++;
+		interrupted++;
+		needconsult++;
+		break;
+
+	    case  4:		/* admin settings prevented operation */
+	    case 14:
+	    case 24:
+		admnflag++;
+		interrupted++;
+		break;
+
+	    case  5:		/* administration: interaction req (no -n) */
+	    case 15:
+	    case 25:
+		nullflag++;
+		interrupted++;
+		needconsult++;
+		break;
+
+	    default:
+		failflag++;
+		interrupted++;
+		needconsult++;
+		return;
+	}
+
+	if (retcode >= 20) {
+		ireboot++;
+	} else if (retcode >= 10) {
+		doreboot++;
+	}
+}
+
+static void
+usage(void)
+{
+	char *prog = get_prog_name();
+
+	if (askflag) {
+		(void) fprintf(stderr, ERR_USAGE_PKGASK, prog);
+	} else if (z_running_in_global_zone() == B_FALSE) {
+		(void) fprintf(stderr, ERR_USAGE_PKGADD_NONGLOBALZONE,
+			prog, prog);
+	} else {
+		(void) fprintf(stderr, ERR_USAGE_PKGADD_GLOBALZONE,
+			prog, prog);
+	}
+}
+
+/*
+ * Name:	check_applicability
+ * Description:	determine if a package is installable in this zone; that is,
+ *		does the scope of install conflict with existing installation
+ *		or can the package be installed
+ * Arguments:	a_packageDir - [RO, *RO] - (char *)
+ *			Pointer to string representing the directory where the
+ *			package is located
+ *		a_pkgInst - [RO, *RO] - (char *)
+ *			Pointer to string representing the name of the package
+ *			to check
+ *		a_rootPath - [RO, *RO] - (char *)
+ *			Pointer to string representing path to the root of the
+ *			file system where the package is to be installed - this
+ *			is usually the same as the "-R" argument to pkgadd
+ *		a_flags - [RO, *RO] - (CAF_T)
+ *			Flags set by the caller to indicate the conditions
+ *			under which the package is to be installed:
+ *				CAF_IN_GLOBAL_ZONE - in global zone
+ *				CAF_SCOPE_GLOBAL - -G specified
+ *				CAF_SCOPE_NONGLOBAL - -Z specified
+ * Returns:	boolean_t
+ *			B_TRUE - the package can be installed
+ *			B_FALSE - the package can not be installed
+ */
+
+static boolean_t
+check_applicability(char *a_packageDir, char *a_pkgInst, char *a_rootPath,
+	CAF_T a_flags)
+{
+	FILE		*pkginfoFP;
+	FILE		*pkgmapFP;
+	boolean_t	all_zones;	/* pkg is "all zones" only */
+	boolean_t	in_gz_only;	/* pkg installed in global zone only */
+	boolean_t	is_hollow;	/* pkg is "hollow" */
+	boolean_t	pkg_installed;	/* pkg is installed */
+	boolean_t	this_zone;	/* pkg is "this zone" only */
+	boolean_t	reqfile_found = B_FALSE;
+	char		instPkg[PKGSIZ+1];	/* installed pkg instance nam */
+	char		instPkgPath[PATH_MAX];	/* installed pkg toplevel dir */
+	char		pkginfoPath[PATH_MAX];	/* pkg 2 install pkginfo file */
+	char		pkgmapPath[PATH_MAX];	/* pkg 2 install pkgmap file */
+	char		pkgpath[PATH_MAX];	/* pkg 2 install toplevel dir */
+	int		len;
+	char		line[LINE_MAX];
+
+	/* entry assertions */
+
+	assert(a_packageDir != (char *)NULL);
+	assert(*a_packageDir != '\0');
+	assert(a_pkgInst != (char *)NULL);
+	assert(*a_pkgInst != '\0');
+
+	/* normalize root path */
+
+	if (a_rootPath == (char *)NULL) {
+		a_rootPath = "";
+	}
+
+	/* entry debugging info */
+
+	echoDebug(DBG_CHECKAPP_ENTRY);
+	echoDebug(DBG_CHECKAPP_ARGS, a_pkgInst, a_packageDir, a_rootPath);
+
+	/*
+	 * calculate paths to various objects
+	 */
+
+	/* path to package to be installed top level (main) directory */
+
+	len = snprintf(pkgpath, sizeof (pkgpath), "%s/%s", a_packageDir,
+			a_pkgInst);
+	if (len > sizeof (pkgpath)) {
+		progerr(ERR_CREATE_PATH_2, a_packageDir, a_pkgInst);
+		return (B_FALSE);
+	}
+
+	/* error if package top level directory does not exist */
+
+	if (isdir(pkgpath) != 0) {
+		progerr(ERR_NO_PKGDIR, pkgpath, a_pkgInst, strerror(errno));
+		return (B_FALSE);
+	}
+
+	/* path to pkginfo file within the package to be installed */
+
+	len = snprintf(pkginfoPath, sizeof (pkginfoPath), "%s/pkginfo",
+			pkgpath);
+	if (len > sizeof (pkginfoPath)) {
+		progerr(ERR_CREATE_PATH_2, pkgpath, "pkginfo");
+		return (B_FALSE);
+	}
+
+	/* path to highest instance of package currently installed */
+
+	pkgLocateHighestInst(instPkgPath, sizeof (instPkgPath),
+		instPkg, sizeof (instPkg), a_rootPath, a_pkgInst);
+
+	/*
+	 * gather information from this package's pkginfo file
+	 */
+
+	pkginfoFP = fopen(pkginfoPath, "r");
+
+	if (pkginfoFP == (FILE *)NULL) {
+		progerr(ERR_NO_PKG_INFOFILE, a_pkgInst, pkginfoPath,
+							strerror(errno));
+		return (B_FALSE);
+	}
+
+	/* determine "HOLLOW" setting for this package */
+
+	is_hollow = pkginfoParamTruth(pkginfoFP, PKG_HOLLOW_VARIABLE,
+			"true", B_FALSE);
+
+	/* determine "ALLZONES" setting for this package */
+
+	all_zones = pkginfoParamTruth(pkginfoFP, PKG_ALLZONES_VARIABLE,
+			"true", B_FALSE);
+
+	/* determine "THISZONE" setting for this package */
+
+	this_zone = pkginfoParamTruth(pkginfoFP, PKG_THISZONE_VARIABLE,
+			"true", B_FALSE);
+
+	/* close pkginfo file */
+
+	(void) fclose(pkginfoFP);
+
+	/*
+	 * If request file is not found, it may be in the datastream which
+	 * is not yet unpacked. Check in the pkgmap file.
+	 */
+	if (isfile(pkgpath, REQUEST_FILE) != 0) {
+
+		/* path to pkgmap file within the package to be installed */
+		(void) snprintf(pkgmapPath, sizeof (pkgmapPath), "%s/pkgmap",
+		    pkgpath);
+
+		pkgmapFP = fopen(pkgmapPath, "r");
+
+		if (pkgmapFP == NULL) {
+			progerr(ERR_NO_PKG_MAPFILE, a_pkgInst,
+			    pkgmapPath, strerror(errno));
+			return (B_FALSE);
+		}
+
+		while (fgets(line, LINE_MAX, pkgmapFP) != NULL) {
+			if (strstr(line, " i request") != NULL) {
+				reqfile_found = B_TRUE;
+				break;
+			}
+		}
+		(void) fclose(pkgmapFP);
+	} else {
+		reqfile_found = B_TRUE;
+	}
+
+	/*
+	 * If this package is not marked for installation in this zone only,
+	 * check to see if this package has a request script. If this package
+	 * does have a request script, then mark the package for installation
+	 * in this zone only. Any package with a request script cannot be
+	 * installed outside of the zone the pkgadd command is being run in,
+	 * nor can such a package be installed as part of a new zone install.
+	 * A new zone install must be non-interactive, which is required
+	 * by all packages integrated into the Solaris WOS.
+	 */
+
+	if ((!this_zone) && (reqfile_found)) {
+		if (a_flags & CAF_IN_GLOBAL_ZONE) {
+			echoDebug(DBG_CHECKAPP_THISZONE_REQUEST, a_pkgInst);
+		}
+		this_zone = B_TRUE;
+	}
+
+	/*
+	 * If this package is already installed, see if the current installation
+	 * of the package has a request file - if it does, then act as though
+	 * the current package to be added has a request file - install the
+	 * package in the current zone only.
+	 */
+
+	if ((!this_zone) && (instPkgPath[0] != '\0') &&
+		(isfile(instPkgPath, REQUEST_FILE) == 0)) {
+		if (a_flags & CAF_IN_GLOBAL_ZONE) {
+			echoDebug(DBG_CHECKAPP_THISZONE_INSTREQ,
+				a_pkgInst, instPkg);
+		}
+		this_zone = B_TRUE;
+	}
+
+	/* gather information from the global zone only file */
+
+	in_gz_only = B_FALSE;
+	if (a_flags & CAF_IN_GLOBAL_ZONE) {
+		in_gz_only = pkgIsPkgInGzOnly(a_rootPath, a_pkgInst);
+	}
+
+	/* determine if this package is currently installed */
+
+	pkg_installed = pkginfoIsPkgInstalled((struct pkginfo **)NULL,
+								a_pkgInst);
+
+	/*
+	 * verify package applicability based on information gathered,
+	 * and validate the three SUNW_PKG_ options:
+	 *
+	 * -----------|--------------|-------------|-------------|-----------
+	 * - - - - - -| GLOBAL ZONE -| GLOBAL ZONE | LOCAL ZONE	 | LOCAL ZONE
+	 * - - - - - -|	- - pkgadd - | pkgadd -G   | pkgadd	 | pkgadd -G
+	 * ----1------|--------------|-------------|-------------|------------
+	 * ALLZONES f | add to gz    | add to gz   | add to ls	 | add to ls
+	 * HOLLOW   f | current lz   | not to curr | only - - - -| only - - -
+	 * THISZONE f | futr lz - - -| or futr lz  | - - - - - - | - - - - - -
+	 * ----2------|--------------|-------------|-------------|------------
+	 * ALLZONES T | add to gz    | operation   | operation	 | operation
+	 * HOLLOW   f | current lz   | not allowed | not allowed | not allowed
+	 * THISZONE f | future lz    | - - - - - - | - - - - - - | - - - - - -
+	 * ----3------|--------------|-------------|-------------|------------
+	 * ALLZONES T | add to gz    | operation   | operation	 | operation
+	 * HOLLOW   T | pkg db only  | not allowed | not allowed | not allowed
+	 * THISZONE f | curr/futr lz | - - - - - - | - - - - - - | - - - - - -
+	 * ----4------|--------------|-------------|-------------|------------
+	 * ALLZONES T | bad option   | bad option  | bad option	 | bad option
+	 * HOLLOW   * | combo - - - -| combo - - - | combo - - - | combo - -
+	 * THISZONE T |	- - - - - - -|- - - - - - -|- - - - - - -|- - - - - -
+	 * ----5------|--------------|-------------|-------------|------------
+	 * ALLZONES f | bad option   | bad option  | bad option	 | bad option
+	 * HOLLOW   T | combo - - - -| combo - - - | combo - - - | combo - - -
+	 * THISZONE * | - - - - - - -| - - - - - - | - - - - - - | - - - - - -
+	 * ----6------|--------------|-------------|-------------|------------
+	 * ALLZONES f | add to gz    | add to gz   | add to lz	 | add to lz
+	 * HOLLOW   f | not current  | not current | only - - -	 | only - - -
+	 * THISZONE T | or future lz | or futr lz  | - - - - - - | - - - - - -
+	 * -----------|--------------|-------------|-------------|-----------
+	 */
+
+	/* pkg "all zones" && "this zone" (#4) */
+
+	if (all_zones && this_zone) {
+		progerr(ERR_ALLZONES_AND_THISZONE, a_pkgInst,
+			PKG_ALLZONES_VARIABLE, PKG_THISZONE_VARIABLE);
+		return (B_FALSE);
+	}
+
+	/* pkg "!all zones" && "hollow" (#5) */
+
+	if ((!all_zones) && is_hollow) {
+		progerr(ERR_NOW_ALLZONES_AND_HOLLOW, a_pkgInst,
+			PKG_ALLZONES_VARIABLE, PKG_HOLLOW_VARIABLE);
+		return (B_FALSE);
+	}
+
+	/* pkg ALLZONES=true && -Z specified */
+
+	if (all_zones && (a_flags & CAF_SCOPE_NONGLOBAL)) {
+		progerr(ERR_ALLZONES_AND_Z_USED, a_pkgInst);
+		return (B_FALSE);
+	}
+
+	/* pkg ALLZONES=true & not running in global zone (#2/#3) */
+
+	if (all_zones && (!(a_flags & CAF_IN_GLOBAL_ZONE))) {
+		progerr(ERR_ALLZONES_AND_IN_LZ, a_pkgInst);
+		return (B_FALSE);
+	}
+
+	/* pkg "in gz only" & pkg "NOT installed" */
+
+	if (in_gz_only && (!pkg_installed)) {
+		/* MAKE A WARNING */
+		echo(ERR_IN_GZ_AND_NOT_INSTALLED, a_pkgInst,
+			pkgGetGzOnlyPath());
+	}
+
+	/* pkg ALLZONES=true & pkg "in gz only" & pkg "is installed" */
+
+	if (all_zones && in_gz_only && pkg_installed) {
+		progerr(ERR_IN_GZ_AND_ALLZONES_AND_INSTALLED, a_pkgInst);
+		return (B_FALSE);
+	}
+
+	/* pkg ALLZONES=true && -G specified (#2/#3) */
+
+	if (all_zones && (a_flags & CAF_SCOPE_GLOBAL)) {
+		progerr(ERR_ALLZONES_AND_G_USED, a_pkgInst);
+		return (B_FALSE);
+	}
+
+	/* pkg "!this zone" && "in gz only" & -G not specified */
+
+	if ((!this_zone) && in_gz_only && (!(a_flags & CAF_SCOPE_GLOBAL))) {
+		progerr(ERR_IN_GZ_AND_NO_G_USED, a_pkgInst);
+		return (B_FALSE);
+	}
+
+	/* pkg "NOT in gz only" & -Z specified */
+
+	if ((!in_gz_only) && (a_flags & CAF_SCOPE_NONGLOBAL)) {
+		progerr(ERR_NOT_IN_GZ_AND_Z_USED, a_pkgInst);
+		return (B_FALSE);
+	}
+
+	/* pkg "this zone" && -Z specified */
+
+	if (this_zone && (a_flags & CAF_SCOPE_NONGLOBAL)) {
+		progerr(ERR_THISZONE_AND_Z_USED, PKG_THISZONE_VARIABLE,
+			a_pkgInst);
+		return (B_FALSE);
+	}
+
+	/*
+	 * If this package is marked 'this zone only', then mark the package
+	 * as "add to this zone only". This is referenced by the various
+	 * add_package_... functions to determine if the package should be
+	 * added to the current zone, or to all zones, depending on the
+	 * zone in which the command is being run.
+	 */
+
+	if (this_zone) {
+		pkgAddThisZonePackage(a_pkgInst);
+	}
+
+	return (B_TRUE);
+}
+
+/*
+ * Name:	create_zone_adminfile
+ * Description: Given a zone temporary directory and optionally an existing
+ *		administration file, generate an administration file that
+ *		can be used to perform "non-interactive" operations in a
+ *		non-global zone.
+ * Arguments:	r_zoneAdminFile - pointer to handle that will contain a
+ *			string representing the path to the temporary
+ *			administration file created - this must be NULL
+ *			before the first call to this function - on
+ *			subsequent calls if the pointer is NOT null then
+ *			the existing string will NOT be overwritten.
+ *		a_zoneTempDir - pointer to string representing the path
+ *			to the zone temporary directory to create the
+ *			temporary administration file in
+ *		a_admnfile - pointer to string representing the path to
+ *			an existing "user" administration file - the
+ *			administration file created will contain the
+ *			settings contained in this file, modified as
+ *			appropriate to supress any interaction;
+ *			If this is == NULL then the administration file
+ *			created will not contain any extra settings
+ * Returns:	void
+ * NOTE:	Any string returned is placed in new storage for the
+ *		calling method. The caller must use 'free' to dispose
+ *		of the storage once the string is no longer needed.
+ * NOTE:	On any error this function will call 'quit(1)'
+ */
+
+static void
+create_zone_adminfile(char **r_zoneAdminFile, char *a_zoneTempDir,
+	char *a_admnfile)
+{
+	boolean_t	b;
+
+	/* entry assertions */
+
+	assert(r_zoneAdminFile != (char **)NULL);
+	assert(a_zoneTempDir != (char *)NULL);
+	assert(*a_zoneTempDir != '\0');
+
+	/* entry debugging info */
+
+	echoDebug(DBG_CREATE_ZONE_ADMINFILE, a_zoneTempDir, PSTR(a_admnfile));
+
+	/* if temporary name already exists, do not overwrite */
+
+	if (*r_zoneAdminFile != (char *)NULL) {
+		return;
+	}
+
+	/* create temporary name */
+
+	*r_zoneAdminFile = tempnam(a_zoneTempDir, "zadmn");
+	b = z_create_zone_admin_file(*r_zoneAdminFile, a_admnfile);
+	if (b == B_FALSE) {
+		progerr(ERR_CREATE_TMPADMIN, *r_zoneAdminFile,
+			strerror(errno));
+		quit(1);
+		/* NOTREACHED */
+	}
+
+	echoDebug(DBG_CREATED_ZONE_ADMINFILE, *r_zoneAdminFile);
+}
+
+/*
+ * Name:	create_zone_tempdir
+ * Description: Given a system temporary directory, create a "zone" specific
+ *		temporary directory and return the path to the directory
+ *		created.
+ * Arguments:	r_zoneTempDir - pointer to handle that will contain a
+ *			string representing the path to the temporary
+ *			directory created - this must be NULL before the
+ *			first call to this function - on subsequent calls
+ *			if the pointer is NOT null then the existing string
+ *			will NOT be overwritten.
+ *		a_zoneTempDir - pointer to string representing the path
+ *			to the system temporary directory to create the
+ *			temporary zone directory in
+ * Returns:	void
+ * NOTE:	Any string returned is placed in new storage for the
+ *		calling method. The caller must use 'free' to dispose
+ *		of the storage once the string is no longer needed.
+ * NOTE:	On any error this function will call 'quit(1)'
+ * NOTE:	This function calls "quitSetZoneTmpdir" on success to
+ *		register the directory created with quit() so that the
+ *		directory will be automatically deleted on exit.
+ */
+
+static void
+create_zone_tempdir(char **r_zoneTempDir, char *a_tmpdir)
+{
+	boolean_t	b;
+
+	/* entry assertions */
+
+	assert(r_zoneTempDir != (char **)NULL);
+	assert(a_tmpdir != (char *)NULL);
+	assert(*a_tmpdir != '\0');
+
+	/* entry debugging info */
+
+	echoDebug(DBG_CREATE_ZONE_TEMPDIR, a_tmpdir);
+
+	/* if temporary directory already exists, do not overwrite */
+
+	if (*r_zoneTempDir != (char *)NULL) {
+		return;
+	}
+
+	/* create temporary directory */
+
+	b = setup_temporary_directory(r_zoneTempDir, a_tmpdir, "ztemp");
+	if (b == B_FALSE) {
+		progerr(ERR_ZONETEMPDIR, a_tmpdir, strerror(errno));
+		quit(1);
+		/* NOTREACHED */
+	}
+
+	/* register with quit() so directory is removed on exit */
+
+	quitSetZoneTmpdir(*r_zoneTempDir);
+
+	/* exit debugging info */
+
+	echoDebug(DBG_CREATED_ZONE_TEMPDIR, *r_zoneTempDir);
+}
+
+/*
+ * Name:	continue_installation
+ * Description: Called from within a loop that is installing packages,
+ *		this function examines various global variables and decides
+ *		whether or not to ask an appropriate question, and wait for
+ *		and appropriate reply.
+ * Arguments:	<<global variables>>
+ * Returns:	B_TRUE - continue processing with next package
+ *		B_FALSE - do not continue processing with next package
+ */
+
+static boolean_t
+continue_installation(void)
+{
+	char	ans[MAX_INPUT];
+	int	n;
+
+	/* return TRUE if not interrupted */
+
+	if (!interrupted) {
+		return (B_TRUE);
+	}
+
+	/*
+	 * process interrupted - determine whether or not to continue
+	 */
+
+	/* output appropriate interrupted message */
+
+	if (askflag) {
+		echo(npkgs == 1 ? MSG_1MORE_PROC : MSG_MORE_PROC, npkgs);
+	} else {
+		echo(npkgs == 1 ? MSG_1MORE_INST : MSG_MORE_INST, npkgs);
+	}
+
+	/* if running with no interaction (-n) do not ask question */
+
+	if (nointeract) {
+		/* if admin required return 'dont continue' */
+		if (needconsult) {
+			return (B_FALSE);
+		}
+		ckquit = 1;
+		return (B_TRUE);
+	}
+
+	/* interaction possible: ask question */
+
+	ckquit = 0;
+	n = ckyorn(ans, NULL, NULL, NULL, ASK_CONTINUE_ADD);
+	if (n != 0) {
+		quit(n);
+		/* NOTREACHED */
+	}
+	ckquit = 1;
+	if (strchr("yY", *ans) == NULL) {
+		return (B_FALSE);
+	}
+	return (B_TRUE);
+}
+
+/*
+ * package can be in a number of formats:
+ * - file containing package stream (pkgadd -d file [pkgs])
+ * - directory containing packages (pkgadd -d /dir [pkgs])
+ * - device containing packages (pkgadd -d diskette1 [pkgs])
+ * non-global zones can be passed open files and strings as arguments
+ * - for file containing package stream
+ * -- the stream can be passed directly to the non-global zone
+ * - for directory
+ * -- convert packages to datastream to pass to the non-global zone
+ * - for device
+ * -- ?
+ */
+
+static boolean_t
+unpack_and_check_packages(char **a_pkgList, char *a_idsName, char *a_packageDir)
+{
+	int	savenpkgs = npkgs;
+	int	i;
+	CAF_T	flags = 0;
+
+	/* entry assertions */
+
+	assert(a_pkgList != (char **)NULL);
+
+	/* entry debugging info */
+
+	echoDebug(DBG_UNPACKCHECK_ENTRY);
+	echoDebug(DBG_UNPACKCHECK_ARGS, PSTR(a_idsName), PSTR(a_packageDir));
+
+	/*
+	 * set flags for applicability check
+	 */
+
+	/* determine if running in the global zone */
+
+	if (z_running_in_global_zone() == B_TRUE) {
+		flags |= CAF_IN_GLOBAL_ZONE;
+	}
+
+	/* set -G flag */
+
+	if (globalZoneOnly == B_TRUE) {
+		flags |= CAF_SCOPE_GLOBAL;
+	}
+
+	/*
+	 * for each package to install:
+	 * - if packages from datastream, unpack package into package dir
+	 * - check applicability of installing package on this system/zone
+	 */
+
+	for (i = 0; (pkginst = a_pkgList[i]) != NULL; i++) {
+		if (a_idsName != (char *)NULL) {
+			/* create stream out of package if not already one */
+			if (unpack_package_from_stream(a_idsName, pkginst,
+				a_packageDir) == B_FALSE) {
+				progerr(ERR_CANNOT_UNPACK_PKGSTRM,
+					PSTR(pkginst), PSTR(a_idsName),
+					PSTR(a_packageDir));
+
+				npkgs = savenpkgs;
+				return (B_FALSE);
+			}
+		} else {
+			echoDebug(DBG_PKG_IN_DIR, pkginst, a_packageDir);
+		}
+
+		/* check package applicability */
+		if (check_applicability(a_packageDir,
+			pkginst, get_inst_root(), flags) == B_FALSE) {
+			progerr(ERR_PKG_NOT_INSTALLABLE, pkginst);
+			npkgs = savenpkgs;
+			return (B_FALSE);
+		}
+		npkgs--;
+	}
+
+	npkgs = savenpkgs;
+	return (B_TRUE);
+}
+
+/*
+ * returns:
+ *	B_TRUE - package list generated
+ *	B_FALSE - failed to generate package list
+ *	Will call quit(n) on fatal error.
+ */
+
+static boolean_t
+get_package_list(char ***r_pkgList, char **a_argv, char *a_categories,
+	char **a_categoryList, int a_ignoreSignatures, PKG_ERR *a_err,
+	ushort_t a_httpProxyPort, char *a_httpProxyName,
+	keystore_handle_t a_keystore, char *a_keystoreFile,
+	char *a_idsName, int *r_repeat)
+{
+	int		n;
+	url_hport_t	*proxytmp = NULL;
+
+	/* entry assertions */
+
+	assert(r_repeat != (int *)NULL);
+
+	/* entry debugging info */
+
+	echoDebug(DBG_GETPKGLIST_ENTRY);
+	echoDebug(DBG_GETPKGLIST_ARGS, PSTR(a_idsName), PSTR(pkgdev.dirname),
+			*r_repeat);
+
+	/*
+	 * get the list of the packages to add
+	 */
+
+	n = pkgGetPackageList(r_pkgList, a_argv, optind, a_categories,
+				a_categoryList, &pkgdev);
+
+	switch (n) {
+		case -1:	/* no packages found */
+			echoDebug(DBG_PKGLIST_NONFOUND, PSTR(a_idsName),
+					pkgdev.dirname);
+			return (B_FALSE);
+
+		case 0:		/* packages found */
+			break;
+
+		default:	/* "quit" error */
+			echoDebug(DBG_PKGLIST_ERROR, PSTR(a_idsName),
+				pkgdev.dirname, n);
+			quit(n);
+			/* NOTREACHED */
+	}
+
+	/*
+	 * If we are not ignoring signatures, check the package's
+	 * signature if one exists.  pkgask doesn't care about
+	 * signatures though.
+	 */
+	if (!askflag && !a_ignoreSignatures && a_idsName &&
+		(web_ck_authentication() == AUTH_QUIT)) {
+
+		PKCS7		*sig = NULL;
+		STACK_OF(X509)	*cas = NULL;
+
+		/* Retrieve signature */
+		if (!get_signature(a_err, a_idsName, &pkgdev, &sig)) {
+			pkgerr(a_err);
+			web_cleanup();
+			quit(1);
+			/* NOTREACHED */
+		}
+
+		if (sig != NULL) {
+			/* Found signature.  Verify. */
+			if (a_httpProxyName != NULL) {
+				/* Proxy will be needed for OCSP */
+				proxytmp = malloc(sizeof (url_hport_t));
+				if (url_parse_hostport(a_httpProxyName,
+					proxytmp, a_httpProxyPort)
+					!= URL_PARSE_SUCCESS) {
+					progerr(ERR_PROXY,
+						a_httpProxyName);
+					PKCS7_free(sig);
+					quit(99);
+					/* NOTREACHED */
+				}
+			}
+
+			/* Start with fresh error stack */
+			pkgerr_clear(a_err);
+
+			if (a_keystore == NULL) {
+				/* keystore not opened - open it */
+				if (open_keystore(a_err, a_keystoreFile,
+					get_prog_name(), pkg_passphrase_cb,
+					KEYSTORE_DFLT_FLAGS,
+					&a_keystore) != 0) {
+					pkgerr(a_err);
+					web_cleanup();
+					PKCS7_free(sig);
+					quit(1);
+					/* NOTREACHED */
+				}
+			}
+
+			/* get trusted CA certs */
+			if (find_ca_certs(a_err, a_keystore, &cas) != 0) {
+				pkgerr(a_err);
+				PKCS7_free(sig);
+				web_cleanup();
+				quit(1);
+				/* NOTREACHED */
+			}
+
+			/* Verify signature */
+			if (!ds_validate_signature(a_err, &pkgdev,
+				&a_argv[optind], a_idsName, sig,
+				cas, proxytmp, nointeract)) {
+				pkgerr(a_err);
+				quit(99);
+				/* NOTREACHED */
+			}
+
+			/* cleanup */
+			PKCS7_free(sig);
+			web_cleanup();
+			pkgerr_free(a_err);
+		}
+	}
+
+	/* order package list if input data stream specified */
+
+	if (a_idsName) {
+		ds_order(*r_pkgList);
+	}
+
+	return (B_TRUE);
+}
+
+/*
+ * Name:	install_in_one_zone
+ * Description:	Install a single package in a single zone
+ * Arguments:	a_inheritedPkgDirs - pointer to array of strings, each one
+ *			representing the non-global zones full path of a
+ *			directory that is inherited from the global zone.
+ *		a_zoneName - pointer to string representing the name of the
+ *			zone to install the package into.
+ *		a_idsName - pointer to string representing the data stream
+ *			device (input data stream) containing the package to
+ *			be installed.
+ *			If this is == NULL the package is assumed to be
+ *			spooled in the zone temporary directory.
+ *		a_zoneAdminFile - pointer to string representing the admin
+ *			file to pass to pkginstall when installing the package.
+ *			If this is == NULL no admin file is given to pkginstall.
+ *		a_zoneTempDir - pointer to string representing the temporary
+ *			directory in which spooled packages can be found if
+ *			a_idsName is == NULL.
+ *		a_altBinDir - pointer to string representing an alternative
+ *			binary location directory to pass to pkginstall.
+ *			If this is == NULL no alternative binary location is
+ *			passed to pkginstall.
+ *		a_scratchName - pointer to string representing the name of the
+ *			scratch zone to use for installation.
+ *		a_zoneState - state of the zone; must be mounted or running.
+ * Returns:	void
+ * NOTE:	As a side effect, "ckreturn" is called on the result returned
+ *		from running 'pkginstall' in the zone; this sets several global
+ *		variables which allows the caller to determine the result of
+ *		the installation operation.
+ */
+
+static void
+install_in_one_zone(char **a_inheritedPkgDirs, char *a_zoneName,
+	char *a_idsName, char *a_zoneAdminFile, char *a_zoneTempDir,
+	char *a_altBinDir, zone_state_t a_zoneState)
+{
+	char	zoneStreamName[PATH_MAX] = {'\0'};
+	int	n;
+
+	/* entry assertions */
+
+	assert(a_zoneName != (char *)NULL);
+	assert(*a_zoneName != '\0');
+
+	/* entry debugging info */
+
+	echoDebug(DBG_INSTINONEZONE_ENTRY);
+	echoDebug(DBG_INSTINONEZONE_ARGS, a_zoneName, PSTR(a_idsName),
+			PSTR(a_zoneAdminFile), PSTR(a_zoneTempDir),
+			PSTR(a_altBinDir));
+
+	/* echo operation to perform to stdout */
+
+	echo(MSG_INSTALL_PKG_IN_ZONE, pkginst, a_zoneName);
+
+	/* determine path to the package stream */
+
+	if (a_idsName == (char *)NULL) {
+		/* locate temp stream created earlier */
+		(void) snprintf(zoneStreamName, sizeof (zoneStreamName),
+			"%s/%s.dstream", a_zoneTempDir, pkginst);
+	} else {
+		/* use stream passed in on command line */
+		(void) snprintf(zoneStreamName, sizeof (zoneStreamName),
+			"%s", a_idsName);
+	}
+
+	echoDebug(DBG_INSTALL_IN_ZONE, pkginst, a_zoneName, zoneStreamName);
+
+	n = pkgZoneInstall(a_zoneName, a_inheritedPkgDirs, a_zoneState,
+		zoneStreamName, a_altBinDir, a_zoneAdminFile);
+
+	/* set success/fail condition variables */
+
+	ckreturn(n);
+
+	/* exit debugging info */
+
+	echoDebug(DBG_INSTALL_FLAG_VALUES, "after install", admnflag, doreboot,
+		failflag, interrupted, intrflag, ireboot, needconsult,
+		nullflag, warnflag);
+}
+
+/*
+ * Name:	install_in_zones
+ * Description:	Install a single package in the zones that are running from
+ *		a list of zones
+ * Arguments:	a_zlst - list of zones to install the package into
+ *		a_idsName - pointer to string representing the data stream
+ *			device (input data stream) containing the package to
+ *			be installed.
+ *			If this is == NULL the package is assumed to be
+ *			spooled in the zone temporary directory.
+ *		a_altBinDir - pointer to string representing an alternative
+ *			binary location directory to pass to pkginstall.
+ *			If this is == NULL no alternative binary location is
+ *			passed to pkginstall.
+ *		a_zoneAdminFile - pointer to string representing the admin
+ *			file to pass to pkginstall when installing the package.
+ *			If this is == NULL no admin file is given to pkginstall.
+ *		a_zoneTempDir - pointer to string representing the temporary
+ *			directory in which spooled packages can be found if
+ *			a_idsName is == NULL.
+ */
+
+static int
+install_in_zones(zoneList_t a_zlst, char *a_idsName, char *a_altBinDir,
+	char *a_zoneAdminFile, char *a_zoneTempDir)
+{
+	char		**inheritedPkgDirs;
+	char		*zoneName;
+	int		zoneIndex;
+	int		zonesSkipped = 0;
+	zone_state_t	zst;
+
+	/* entry assertions */
+
+	assert(a_zlst != (zoneList_t)NULL);
+
+	/* entry debugging info */
+
+	echoDebug(DBG_INSTALLINZONES_ENTRY);
+	echoDebug(DBG_INSTALLINZONES_ARGS, PSTR(a_idsName),
+			PSTR(a_zoneAdminFile), PSTR(a_zoneTempDir));
+
+	/* process each zone in the list */
+
+	for (zoneIndex = 0;
+		(zoneName = z_zlist_get_zonename(a_zlst, zoneIndex)) != NULL;
+		zoneIndex++) {
+
+		/* skip the zone if it is NOT running */
+
+		zst = z_zlist_get_current_state(a_zlst, zoneIndex);
+		if (zst != ZONE_STATE_RUNNING && zst != ZONE_STATE_MOUNTED) {
+			zonesSkipped++;
+			echoDebug(DBG_SKIPPING_ZONE, zoneName);
+			continue;
+		}
+
+		/* determine list of directories inherited from global zone */
+
+		inheritedPkgDirs = z_zlist_get_inherited_pkg_dirs(a_zlst,
+					zoneIndex);
+
+		/* install the package in this zone */
+
+		install_in_one_zone(inheritedPkgDirs,
+		    z_zlist_get_scratch(a_zlst, zoneIndex), a_idsName,
+		    a_zoneAdminFile, a_zoneTempDir, a_altBinDir, zst);
+	}
+
+	return (zonesSkipped);
+}
+
+/*
+ * Name:	boot_and_install_in_zones
+ * Description:	Install a single package in the zones that are NOT running from
+ *		a list of zones - each zone is booted, the package installed,
+ *		and the zone is halted
+ * Arguments:	a_zlst - list of zones to install the package into
+ *		a_idsName - pointer to string representing the data stream
+ *			device (input data stream) containing the package to
+ *			be installed.
+ *			If this is == NULL the package is assumed to be
+ *			spooled in the zone temporary directory.
+ *		a_altBinDir - pointer to string representing an alternative
+ *			binary location directory to pass to pkginstall.
+ *			If this is == NULL no alternative binary location is
+ *			passed to pkginstall.
+ *		a_zoneAdminFile - pointer to string representing the admin
+ *			file to pass to pkginstall when installing the package.
+ *			If this is == NULL no admin file is given to pkginstall.
+ *		a_zoneTempDir - pointer to string representing the temporary
+ *			directory in which spooled packages can be found if
+ *			a_idsName is == NULL.
+ */
+
+static int
+boot_and_install_in_zones(zoneList_t a_zlst, char *a_idsName, char *a_altBinDir,
+	char *a_zoneAdminFile, char *a_zoneTempDir)
+{
+	boolean_t	b;
+	char		**inheritedPkgDirs;
+	char		*zoneName;
+	int		zoneIndex;
+	int		zonesSkipped = 0;
+	zone_state_t	zst;
+
+	/* entry assertions */
+
+	assert(a_zlst != (zoneList_t)NULL);
+
+	/* entry debugging info */
+
+	echoDebug(DBG_BOOTINSTALLINZONES_ENTRY);
+	echoDebug(DBG_BOOTINSTALLINZONES_ARGS, PSTR(a_idsName),
+			PSTR(a_zoneAdminFile), PSTR(a_zoneTempDir));
+
+	/* process each zone in the list */
+
+	for (zoneIndex = 0;
+		(zoneName = z_zlist_get_zonename(a_zlst, zoneIndex)) != NULL;
+		zoneIndex++) {
+
+		/* skip the zone if it IS running */
+
+		zst = z_zlist_get_current_state(a_zlst, zoneIndex);
+		if (zst == ZONE_STATE_RUNNING || zst == ZONE_STATE_MOUNTED) {
+			echoDebug(DBG_SKIPPING_ZONE_BOOT, zoneName);
+			continue;
+		}
+
+		/* skip the zone if it is NOT bootable */
+
+		if (z_zlist_is_zone_runnable(a_zlst, zoneIndex) == B_FALSE) {
+			echo(MSG_SKIPPING_ZONE_NOT_RUNNABLE, zoneName);
+			echoDebug(DBG_SKIPPING_ZONE_NOT_RUNNABLE, zoneName);
+			continue;
+		}
+
+		/* mount up the zone */
+
+		echo(MSG_BOOTING_ZONE, zoneName);
+		echoDebug(DBG_BOOTING_ZONE, zoneName);
+
+		b = z_zlist_change_zone_state(a_zlst, zoneIndex,
+					ZONE_STATE_MOUNTED);
+		if (b == B_FALSE) {
+			progerr(ERR_CANNOT_BOOT_ZONE, zoneName);
+			/* set fatal error return condition */
+			ckreturn(1);
+			zonesSkipped++;
+			continue;
+		}
+
+		/* determine list of directories inherited from global zone */
+
+		inheritedPkgDirs = z_zlist_get_inherited_pkg_dirs(a_zlst,
+					zoneIndex);
+
+		/* install the package in this zone */
+
+		install_in_one_zone(inheritedPkgDirs,
+		    z_zlist_get_scratch(a_zlst, zoneIndex), a_idsName,
+		    a_zoneAdminFile, a_zoneTempDir, a_altBinDir,
+		    ZONE_STATE_MOUNTED);
+
+		/* restore original state of zone */
+
+		echo(MSG_RESTORE_ZONE_STATE, zoneName);
+		echoDebug(DBG_RESTORE_ZONE_STATE, zoneName);
+
+		b = z_zlist_restore_zone_state(a_zlst, zoneIndex);
+	}
+
+	return (zonesSkipped);
+}
+
+/*
+ * Name:	pkginstall_check_in_one_zone
+ * Description:	Do a pre install check of a single package in a single zone
+ * Arguments:	a_inheritedPkgDirs - pointer to array of strings, each one
+ *			representing the non-global zones full path of a
+ *			directory that is inherited from the global zone.
+ *		a_zoneName - pointer to string representing the name of the
+ *			zone to check install the package in.
+ *		a_idsName - pointer to string representing the data stream
+ *			device (input data stream) containing the package to
+ *			be check installed.
+ *			If this is == NULL the package is assumed to be
+ *			spooled in the zone temporary directory.
+ *		a_zoneAdminFile - pointer to string representing the admin
+ *			file to pass to pkginstall when installing the package.
+ *			If this is == NULL no admin file is given to pkginstall.
+ *		a_zoneTempDir - pointer to string representing the temporary
+ *			directory in which spooled packages can be found if
+ *			a_idsName is == NULL.
+ *		a_altBinDir - pointer to string representing an alternative
+ *			binary location directory to pass to pkginstall.
+ *			If this is == NULL no alternative binary location is
+ *			passed to pkginstall.
+ *		a_scratchName - pointer to string representing the name of the
+ *			scratch zone to use for installation.
+ *		a_zoneState - state of the zone; must be mounted or running.
+ * Returns:	void
+ * NOTE:	As a side effect, "ckreturn" is called on the result returned
+ *		from running 'pkginstall' in the zone; this sets several global
+ *		variables which allows the caller to determine the result of
+ *		the pre installation check operation.
+ */
+
+static void
+pkginstall_check_in_one_zone(char **a_inheritedPkgDirs, char *a_zoneName,
+	char *a_idsName, char *a_zoneAdminFile, char *a_zoneTempDir,
+	char *a_altBinDir, char *a_scratchName, zone_state_t a_zoneState)
+{
+	char	preinstallcheckPath[PATH_MAX+1];
+	char	zoneStreamName[PATH_MAX] = {'\0'};
+	int	n;
+
+	echo(MSG_CHECKINSTALL_PKG_IN_ZONE, pkginst, a_zoneName);
+	echoDebug(MSG_CHECKINSTALL_PKG_IN_ZONE, pkginst, a_zoneName);
+
+	(void) snprintf(preinstallcheckPath, sizeof (preinstallcheckPath),
+		"%s/%s.%s.preinstallcheck.txt", a_zoneTempDir, pkginst,
+		a_zoneName);
+
+	if (a_idsName == (char *)NULL) {
+		/* locate temporary stream created earlier */
+		(void) snprintf(zoneStreamName, sizeof (zoneStreamName),
+			"%s/%s.dstream", a_zoneTempDir, pkginst);
+	} else {
+		(void) snprintf(zoneStreamName, sizeof (zoneStreamName),
+			"%s", a_idsName);
+	}
+
+	echoDebug(DBG_CHECKINSTALL_IN_ZONE, pkginst, a_zoneName,
+						zoneStreamName);
+
+	n = pkgZoneCheckInstall(a_scratchName, a_inheritedPkgDirs,
+	    a_zoneState, zoneStreamName, a_altBinDir, a_zoneAdminFile,
+	    preinstallcheckPath);
+
+	/* set success/fail condition variables */
+
+	ckreturn(n);
+
+	echoDebug(DBG_INSTALL_FLAG_VALUES, "after preinstall check",
+		admnflag, doreboot, failflag, interrupted, intrflag,
+		ireboot, needconsult, nullflag, warnflag);
+}
+
+/*
+ * Name:	pkginstall_check_in_zones
+ * Description:	Check installation of a single package in the zones that
+ *		are running from a list of zones
+ * Arguments:	a_zlst - list of zones to check install the package
+ *		a_idsName - pointer to string representing the data stream
+ *			device (input data stream) containing the package to
+ *			be check installed.
+ *			If this is == NULL the package is assumed to be
+ *			spooled in the zone temporary directory.
+ *		a_altBinDir - pointer to string representing an alternative
+ *			binary location directory to pass to pkginstall.
+ *			If this is == NULL no alternative binary location is
+ *			passed to pkginstall.
+ *		a_zoneAdminFile - pointer to string representing the admin
+ *			file to pass to pkginstall when checking the installing
+ *			of the package.
+ *			If this is == NULL no admin file is given to pkginstall.
+ *		a_zoneTempDir - pointer to string representing the temporary
+ *			directory in which spooled packages can be found if
+ *			a_idsName is == NULL.
+ */
+
+static int
+pkginstall_check_in_zones(zoneList_t a_zlst, char *a_idsName, char *a_altBinDir,
+	char *a_zoneAdminFile, char *a_zoneTempDir)
+{
+	char		**inheritedPkgDirs;
+	char		*zoneName;
+	int		zoneIndex;
+	int		zonesSkipped = 0;
+	zone_state_t	zst;
+
+	for (zoneIndex = 0;
+		(zoneName = z_zlist_get_zonename(a_zlst, zoneIndex)) != NULL;
+		zoneIndex++) {
+
+		zst = z_zlist_get_current_state(a_zlst, zoneIndex);
+		if (zst != ZONE_STATE_RUNNING && zst != ZONE_STATE_MOUNTED) {
+			zonesSkipped++;
+			echoDebug(DBG_SKIPPING_ZONE, zoneName);
+			continue;
+		}
+
+		inheritedPkgDirs = z_zlist_get_inherited_pkg_dirs(a_zlst,
+					zoneIndex);
+
+		pkginstall_check_in_one_zone(inheritedPkgDirs, zoneName,
+		    a_idsName, a_zoneAdminFile, a_zoneTempDir, a_altBinDir,
+		    z_zlist_get_scratch(a_zlst, zoneIndex), zst);
+	}
+
+	return (zonesSkipped);
+}
+
+/*
+ * Name:	boot_and_pkginstall_check_in_zones
+ * Description:	Check installation of a single package in the zones that
+ *		are NOT running from a list of zones - each zone is booted,
+ *		the package installation is checked, and the zone is halted.
+ * Arguments:	a_zlst - list of zones to install the package into
+ *		a_idsName - pointer to string representing the data stream
+ *			device (input data stream) containing the package to
+ *			be check installed.
+ *			If this is == NULL the package is assumed to be
+ *			spooled in the zone temporary directory.
+ *		a_altBinDir - pointer to string representing an alternative
+ *			binary location directory to pass to pkginstall.
+ *			If this is == NULL no alternative binary location is
+ *			passed to pkginstall.
+ *		a_zoneAdminFile - pointer to string representing the admin
+ *			file to pass to pkginstall when check installing the
+ *			package.
+ *			If this is == NULL no admin file is given to pkginstall.
+ *		a_zoneTempDir - pointer to string representing the temporary
+ *			directory in which spooled packages can be found if
+ *			a_idsName is == NULL.
+ */
+
+static int
+boot_and_pkginstall_check_in_zones(zoneList_t a_zlst, char *a_idsName,
+	char *a_altBinDir, char *a_zoneAdminFile, char *a_zoneTempDir)
+{
+	int		zoneIndex;
+	int		zonesSkipped = 0;
+	char		*zoneName;
+	boolean_t	b;
+	char		**inheritedPkgDirs;
+	zone_state_t	zst;
+
+	/* entry assertions */
+
+	assert(a_zlst != (zoneList_t)NULL);
+
+	/* entry debugging info */
+
+	echoDebug(DBG_BOOTCHECKINSTALLINZONES_ENTRY);
+	echoDebug(DBG_BOOTCHECKINSTALLINZONES_ARGS, PSTR(a_idsName),
+			PSTR(a_zoneAdminFile), PSTR(a_zoneTempDir));
+
+	/* process each zone in the list */
+
+	for (zoneIndex = 0;
+		(zoneName = z_zlist_get_zonename(a_zlst, zoneIndex)) != NULL;
+		zoneIndex++) {
+
+		/* skip the zone if it IS running */
+
+		zst = z_zlist_get_current_state(a_zlst, zoneIndex);
+		if (zst == ZONE_STATE_RUNNING || zst == ZONE_STATE_MOUNTED) {
+			echoDebug(DBG_SKIPPING_ZONE_BOOT, zoneName);
+			continue;
+		}
+
+		/* skip the zone if it is NOT bootable */
+
+		if (z_zlist_is_zone_runnable(a_zlst, zoneIndex) == B_FALSE) {
+			echo(MSG_SKIPPING_ZONE_NOT_RUNNABLE, zoneName);
+			echoDebug(DBG_SKIPPING_ZONE_NOT_RUNNABLE, zoneName);
+			continue;
+		}
+
+		/* mount up the zone */
+
+		echo(MSG_BOOTING_ZONE, zoneName);
+		echoDebug(DBG_BOOTING_ZONE, zoneName);
+
+		b = z_zlist_change_zone_state(a_zlst, zoneIndex,
+		    ZONE_STATE_MOUNTED);
+		if (b == B_FALSE) {
+			progerr(ERR_CANNOT_BOOT_ZONE, zoneName);
+			/* set fatal error return condition */
+			ckreturn(1);
+			zonesSkipped++;
+			continue;
+		}
+
+		/* determine list of directories inherited from global zone */
+
+		inheritedPkgDirs = z_zlist_get_inherited_pkg_dirs(a_zlst,
+					zoneIndex);
+
+		/* pre-installation check of the package in this zone */
+
+		pkginstall_check_in_one_zone(inheritedPkgDirs, zoneName,
+		    a_idsName, a_zoneAdminFile, a_zoneTempDir, a_altBinDir,
+		    z_zlist_get_scratch(a_zlst, zoneIndex),
+		    ZONE_STATE_MOUNTED);
+
+		/* restore original state of zone */
+
+		echo(MSG_RESTORE_ZONE_STATE, zoneName);
+		echoDebug(DBG_RESTORE_ZONE_STATE, zoneName);
+
+		b = z_zlist_restore_zone_state(a_zlst, zoneIndex);
+	}
+
+	return (zonesSkipped);
+}
+
+/*
+ * Function:	add_packages_in_global_with_zones
+ * Description: call this function to add a list of packages in the global zone
+ *		when one or more non-global zones exist
+ * returns:
+ *	B_TRUE to process next data stream
+ *	B_FALSE to exit
+ */
+
+static boolean_t
+add_packages_in_global_with_zones(char **a_pkgList, char *a_uri,
+	char *a_idsName, int a_repeat, char *a_altBinDir,
+	char *a_device, zoneList_t a_zlst)
+{
+static	char		*zoneTempDir = (char *)NULL;
+static	char		*zoneAdminFile = (char *)NULL;
+
+	boolean_t	b;
+	char		*packageDir;
+	char		instdir[PATH_MAX];
+	char		respfile_path[PATH_MAX];
+	char		zoneStreamName[PATH_MAX] = {'\0'};
+	int		i;
+	int		n;
+	int		savenpkgs = npkgs;
+	int		zonesSkipped;
+	boolean_t	globalPresent;
+
+	/* entry assertions */
+
+	assert(a_pkgList != (char **)NULL);
+	assert(a_zlst != (zoneList_t)NULL);
+
+	echoDebug(DBG_ADDPACKAGES_GZ_W_LZ_ENTRY);
+	echoDebug(DBG_ADDPACKAGES_GZ_W_LZ_ARGS, npkgs, PSTR(a_uri),
+			PSTR(a_idsName), a_repeat, PSTR(a_device));
+
+	/* create temporary directory for use by zone operations */
+
+	create_zone_tempdir(&zoneTempDir, tmpdir);
+
+	/* create hands off settings admin file for use in a non-global zone */
+
+	create_zone_adminfile(&zoneAdminFile, zoneTempDir, admnfile);
+
+	/* determine directory where packages can be found */
+
+	if (a_idsName == (char *)NULL) {
+		/* no stream - directory containing packages provided */
+		packageDir = pkgdev.dirname;
+	} else {
+		packageDir = zoneTempDir;
+	}
+
+	/* unpack and check all packages */
+
+	b = unpack_and_check_packages(a_pkgList, a_idsName, packageDir);
+	if (b != B_TRUE) {
+		quit(1);
+	}
+
+	/*
+	 * if the packages are contained in a directory, convert the
+	 * packages into individual streams because pkgZoneInstall is only able
+	 * to pass a stream to the non-global zone's pkginstall command.
+	 * After this code is executed:
+	 * if the original input was a datastream:
+	 * -> that datastream has been unpacked into "instdir"
+	 * if the original input was a directory with packages in it:
+	 * -> those packages have been placed into a single datastream
+	 */
+
+	if (a_idsName == (char *)NULL) {
+		for (i = 0; (pkginst = a_pkgList[i]) != NULL; i++) {
+			char	*pkgs[2];
+
+			/* package is not a stream - create one */
+
+			(void) snprintf(zoneStreamName, sizeof (zoneStreamName),
+				"%s/%s.dstream", zoneTempDir, pkginst);
+
+			echoDebug(DBG_CONVERTING_PKG, packageDir, pkginst,
+				zoneStreamName);
+
+			/* set up list of packages to be this package only */
+
+			pkgs[0] = pkginst;
+			pkgs[1] = (char *)NULL;
+
+			n = pkgtrans(packageDir, zoneStreamName, pkgs,
+					PT_SILENT|PT_ODTSTREAM, NULL, NULL);
+			if (n != 0) {
+				progerr(ERR_CANNOT_CONVERT_PKGSTRM,
+					pkginst, packageDir, zoneStreamName);
+				quit(1);
+			}
+			npkgs--;
+		}
+		npkgs = savenpkgs;
+	}
+
+	/*
+	 * Phase I - run collect dependency information for all packages for all
+	 * zones - this involves running pkginstall with the "preinstallcheck"
+	 * option which causes all dependency checks to be performed without
+	 * actually doing the installation of the packages. This information is
+	 * gathered in the zone temporary directory and is used later to present
+	 * the dependency check results to the system administrator depending
+	 * on the administration settings.
+	 */
+
+	for (i = 0; (pkginst = a_pkgList[i]) != NULL; i++) {
+
+		/* reset interrupted flag before calling pkginstall */
+
+		interrupted = 0;	/* last action was NOT quit */
+
+		/*
+		 * if this package is marked "install in this zone only", then
+		 * do not check dependencies in any other zone
+		 */
+
+		if (pkgPackageIsThisZone(pkginst) == B_TRUE) {
+			echoDebug(DBG_VERIFY_SKIP_THISZONE, pkginst);
+			npkgs--;
+			continue;
+		}
+
+		/*
+		 * if operation failed in global zone do not propagate
+		 * to any non-global zones
+		 */
+
+		if (interrupted != 0) {
+			echo(MSG_CHECKINSTALL_INTERRUPT_B4_Z, pkginst);
+			echoDebug(MSG_CHECKINSTALL_INTERRUPT_B4_Z, pkginst);
+			break;
+		}
+
+		echoDebug(DBG_INSTALL_FLAG_VALUES, "after pkginstall",
+			admnflag, doreboot, failflag, interrupted, intrflag,
+			ireboot, needconsult, nullflag, warnflag);
+
+		/*
+		 * call pkginstall to verify this package for all non-global
+		 * zones that are currently booted
+		 */
+
+		zonesSkipped = pkginstall_check_in_zones(a_zlst, a_idsName,
+				a_altBinDir, admnfile, zoneTempDir);
+
+		/*
+		 * if any zones were skipped (becuase they are not currently
+		 * booted), boot each zone one at a time and call pkginstall
+		 * to verify this package for each such non-global zone
+		 */
+
+		if (zonesSkipped > 0) {
+			echoDebug(DBG_ZONES_SKIPPED, zonesSkipped);
+
+			zonesSkipped =
+				boot_and_pkginstall_check_in_zones(a_zlst,
+				a_idsName, a_altBinDir, admnfile,
+				zoneTempDir);
+
+			if (zonesSkipped > 0) {
+				progerr(ERR_INSTALL_ZONES_SKIPPED,
+							zonesSkipped);
+			}
+		}
+
+		npkgs--;
+	}
+
+	/*
+	 * At this point, all of the dependency information has been gathered
+	 * and is ready to be analyzed. This function processes all of that
+	 * dependency information and presents the results to the system
+	 * administrator, depending on the current administration settings.
+	 */
+
+	i = preinstall_verify(a_pkgList, a_zlst, zoneTempDir);
+	if (i != 0) {
+		/* dependency checks failed - exit */
+		quit(i);
+	}
+
+	npkgs = savenpkgs;
+
+	/*
+	 * reset all error return condition variables that may have been
+	 * set during package installation dependency checking so that they
+	 * do not reflect on the success/failure of the actual package
+	 * installation operations
+	 */
+
+	resetreturn();
+
+	/*
+	 * At this point, all of the dependency checking is completed, and
+	 * the installation of the packages can proceed. Install each package
+	 * one at a time, starting with the global zone, and the for each
+	 * non-global zone that is booted, and then for each non-global zone
+	 * that is not currently booted.
+	 */
+
+	globalPresent = z_on_zone_spec(GLOBAL_ZONENAME);
+
+	for (i = 0; (pkginst = a_pkgList[i]) != NULL; i++) {
+		/*
+		 * if immediate reboot required from last package and this is
+		 * not 'pkgask' then suspend installation of remaining packages
+		 */
+
+		if ((ireboot != 0) && (askflag == 0)) {
+			ptext(stderr, MSG_SUSPEND_ADD, pkginst);
+				continue;
+		}
+
+		/*
+		 * handle interrupt if the previous pkginstall was interrupted
+		 */
+
+		if (continue_installation() == B_FALSE) {
+			return (B_FALSE);
+		}
+
+		/*
+		 * if pkgask, handle response file creation:
+		 * - if the response file is a directory, then create a path to
+		 * -- a package instance within the response file directory.
+		 * - If the response file is NOT a directory, if more than one
+		 * -- package is to be installed.
+		 */
+
+		if ((askflag != 0) && (respdir != (char *)NULL)) {
+			(void) snprintf(respfile_path, sizeof (respfile_path),
+					"%s/%s", respdir, pkginst);
+			respfile = respfile_path;
+		}
+
+		echo(MSG_PROC_INST, pkginst,
+			(a_uri && a_idsName) ? a_uri : a_device);
+
+		/*
+		 * If we're installing another package in the same
+		 * session, the second through nth pkginstall, must
+		 * continue from where the prior one left off. For this
+		 * reason, the continuation feature (implied by the
+		 * nature of the command) is used for the remaining
+		 * packages.
+		 */
+
+		if ((i == 1) && (pkgdrtarg != (char *)NULL)) {
+			pkgcontsrc = pkgdrtarg;
+		}
+
+		if (globalPresent) {
+			/*
+			 * call pkginstall for this package for the global zone
+			 */
+
+			echo(MSG_INSTALLING_PKG_IN_GZ, pkginst);
+
+			/* reset interrupted flag before calling pkginstall */
+
+			interrupted = 0;	/* last action was NOT quit */
+
+			n = pkgInstall(get_inst_root(), NULL, packageDir,
+			    a_altBinDir,  NULL);
+
+			/* set success/fail condition variables */
+
+			ckreturn(n);
+
+			/*
+			 * if operation failed in global zone do not propagate
+			 * to any non-global zones
+			 */
+
+			if (interrupted != 0) {
+				echo(MSG_INSTALL_INTERRUPT_B4_ZONES, pkginst);
+				echoDebug(MSG_INSTALL_INTERRUPT_B4_ZONES,
+				    pkginst);
+				break;
+			}
+		}
+
+		/*
+		 * if this package is marked "install in this zone only",
+		 * then only need to install the package in the global zone;
+		 * skip installation in any non-global zones.
+		 */
+
+		if (pkgPackageIsThisZone(pkginst) == B_TRUE) {
+			echoDebug(DBG_INSTALL_SKIP_THISZONE, pkginst);
+			npkgs--;
+			continue;
+		}
+
+		echoDebug(DBG_INSTALL_FLAG_VALUES, "install in running zones",
+			admnflag, doreboot, failflag, interrupted, intrflag,
+			ireboot, needconsult, nullflag, warnflag);
+
+		/* install package in currently booted zones */
+
+		zonesSkipped = install_in_zones(a_zlst, a_idsName, a_altBinDir,
+					zoneAdminFile, zoneTempDir);
+
+		/* install package in zones that are not currently booted */
+
+		if (zonesSkipped > 0) {
+			echoDebug(DBG_ZONES_SKIPPED, zonesSkipped);
+
+			zonesSkipped = boot_and_install_in_zones(a_zlst,
+				a_idsName, a_altBinDir, zoneAdminFile,
+				zoneTempDir);
+
+			if (zonesSkipped > 0) {
+				progerr(ERR_INSTALL_ZONES_SKIPPED,
+							zonesSkipped);
+			}
+		}
+
+		/*
+		 * package completely installed - remove any temporary stream
+		 * of the package that might have been created
+		 */
+
+		if (a_idsName == (char *)NULL) {
+			/* locate temporary stream created earlier */
+			(void) snprintf(zoneStreamName, sizeof (zoneStreamName),
+				"%s/%s.dstream", zoneTempDir, pkginst);
+			/* remove stream - no longer needed */
+			echoDebug(DBG_REMOVING_DSTREAM_PKGDIR, zoneStreamName,
+					pkginst);
+			(void) remove(zoneStreamName);
+		} else {
+			/* remove package - no longer needed */
+			if (snprintf(instdir, sizeof (instdir), "%s/%s",
+					zoneTempDir, pkginst) >= PATH_MAX) {
+				progerr(ERR_CANNOT_CREATE_PKGPATH, tmpdir);
+				quit(1);
+			}
+			echoDebug(DBG_REMOVING_PKG_TMPDIR, instdir, pkginst);
+			(void) remove(instdir);
+		}
+
+		/* decrement number of packages left to install */
+
+		npkgs--;
+
+		/*
+		 * if no packages left to install, unmount package source
+		 * device if appropriate
+		 */
+
+		if ((npkgs <= 0) && (pkgdev.mount || a_idsName)) {
+			(void) chdir("/");
+			if (!a_idsName) {
+				echoDebug(DBG_UNMOUNTING_DEV,
+							PSTR(pkgdev.mount));
+				(void) pkgumount(&pkgdev);
+			}
+		}
+	}
+
+	/*
+	 * all packages in the package list have been installed.
+	 * Continue with installation if:
+	 * -- immediate reboot is NOT required
+	 * -- there are more packages to install
+	 * -- the package source is a path to a file
+	 * else return do NOT continue.
+	 */
+
+	if ((ireboot == 0) && (a_repeat != 0) &&
+		(pkgdev.pathname == (char *)NULL)) {
+		return (B_TRUE);
+	}
+
+	/* return 'dont continue' */
+
+	return (B_FALSE);
+}
+
+/*
+ * Function:	add_packages_in_nonglobal_zone
+ * Description: call this function to add a list of packages in a non-global
+ *		zone
+ * returns:
+ *	B_TRUE to process next data stream
+ *	B_FALSE to exit
+ */
+
+static boolean_t
+add_packages_in_nonglobal_zone(char **a_pkgList, char *a_uri,
+	char *a_idsName, int a_repeat, char *a_altBinDir, char *a_device)
+{
+static	char		*zoneTempDir = (char *)NULL;
+
+	char		*packageDir;
+	char		respfile_path[PATH_MAX];
+	int		i;
+	int		n;
+	boolean_t	b;
+	int		savenpkgs = npkgs;
+
+	/* entry assertions */
+
+	assert(a_pkgList != (char **)NULL);
+
+	/* entry debugging info */
+
+	echoDebug(DBG_ADDPACKAGES_LZ_ENTRY);
+	echoDebug(DBG_ADDPACKAGES_LZ_ARGS, npkgs, PSTR(a_uri), PSTR(a_idsName),
+		a_repeat, PSTR(a_device));
+
+	/* create temporary directory for use by zone operations */
+
+	create_zone_tempdir(&zoneTempDir, tmpdir);
+
+	/*
+	 * package can be in a number of formats:
+	 * - file containing package stream (pkgadd -d file [pkgs])
+	 * - directory containing packages (pkgadd -d /dir [pkgs])
+	 * - device containing packages (pkgadd -d diskette1 [pkgs])
+	 * non-global zones can be passed open file drescriptors and
+	 * strings as arguments
+	 * - for file containing package stream
+	 * -- the stream can be passed directly to the non-global zone
+	 * - for directory
+	 * -- convert packages to datastream to pass to the non-global zone
+	 * - for device
+	 */
+
+	/* determine directory where packages can be found */
+
+	if (a_idsName == (char *)NULL) {
+		/* no stream - directory containing packages provided */
+		packageDir = pkgdev.dirname;
+	} else {
+		packageDir = zoneTempDir;
+	}
+
+	b = unpack_and_check_packages(a_pkgList, a_idsName, packageDir);
+	if (b != B_TRUE) {
+		quit(1);
+	}
+
+	/*
+	 * this is the main loop where all of the packages (as listed in the
+	 * package list) are added one at a time.
+	 */
+
+	for (i = 0; (pkginst = a_pkgList[i]) != NULL; i++) {
+		npkgs--;
+	}
+
+	npkgs = savenpkgs;
+
+	/*
+	 * this is the main loop where all of the packages (as listed in the
+	 * package list) are added one at a time.
+	 */
+
+	for (i = 0; (pkginst = a_pkgList[i]) != NULL; i++) {
+		/*
+		 * if immediate reboot required from last package and this is
+		 * not 'pkgask' then suspend installation of remaining packages
+		 */
+
+		if ((ireboot != 0) && (askflag == 0)) {
+			ptext(stderr, MSG_SUSPEND_ADD, pkginst);
+				continue;
+		}
+
+		/*
+		 * handle interrupt if the previous pkginstall was interrupted
+		 */
+
+		if (continue_installation() == B_FALSE) {
+			return (B_FALSE);
+		}
+
+		/*
+		 * if pkgask, handle response file creation:
+		 * - if the response file is a directory, then create a path to
+		 * -- a package instance within the response file directory.
+		 * - If the response file is NOT a directory, if more than one
+		 * -- package is to be installed.
+		 */
+
+		if ((askflag != 0) && (respdir != (char *)NULL)) {
+			(void) snprintf(respfile_path, sizeof (respfile_path),
+					"%s/%s", respdir, pkginst);
+			respfile = respfile_path;
+		}
+
+		echo(MSG_PROC_INST, pkginst,
+			(a_uri && a_idsName) ? a_uri : a_device);
+
+		/*
+		 * If we're installing another package in the same
+		 * session, the second through nth pkginstall, must
+		 * continue from where the prior one left off. For this
+		 * reason, the continuation feature (implied by the
+		 * nature of the command) is used for the remaining
+		 * packages.
+		 */
+
+		if ((i == 1) && (pkgdrtarg != (char *)NULL)) {
+			pkgcontsrc = pkgdrtarg;
+		}
+
+		/* reset interrupted flag before calling pkginstall */
+
+		interrupted = 0;	/* last action was NOT quit */
+
+		/* call pkginstall for this package */
+
+		n = pkgInstall(get_inst_root(), NULL,
+				packageDir, a_altBinDir,
+				(char **)NULL);
+
+		/* set success/fail condition variables */
+
+		ckreturn(n);
+
+		/* decrement number of packages left to install */
+
+		npkgs--;
+
+		/*
+		 * if no packages left to install, unmount package source
+		 * device if appropriate
+		 */
+
+		if ((npkgs <= 0) && (pkgdev.mount || a_idsName)) {
+			(void) chdir("/");
+			if (!a_idsName) {
+				(void) pkgumount(&pkgdev);
+			}
+		}
+	}
+
+	/*
+	 * all packages in the package list have been installed.
+	 * Continue with installation if:
+	 * -- immediate reboot is NOT required
+	 * -- there are more packages to install
+	 * -- the package source is a path to a file
+	 * else return do NOT continue.
+	 */
+
+	if ((ireboot == 0) && (a_repeat != 0) &&
+		(pkgdev.pathname == (char *)NULL)) {
+		return (B_TRUE);
+	}
+
+	/* return 'dont continue' */
+
+	return (B_FALSE);
+}
+
+/*
+ * Function:	add_packages_in_global_no_zones
+ * Description: call this function to add a list of packages in the global zone
+ *		when no non-global zones exist
+ * returns:
+ *	B_TRUE to process next data stream
+ *	B_FALSE to exit
+ */
+
+static boolean_t
+add_packages_in_global_no_zones(char **a_pkgList, char *a_uri,
+	char *a_idsName, int a_repeat, char *a_altBinDir, char *a_device)
+{
+	int		n;
+	int		i;
+	char		respfile_path[PATH_MAX];
+	CAF_T		flags = 0;
+
+	/* entry assertions */
+
+	assert(a_pkgList != (char **)NULL);
+
+	echoDebug(DBG_ADDPACKAGES_GZ_NO_LZ_ENTRY);
+	echoDebug(DBG_ADDPACKAGES_GZ_NO_LZ_ARGS, npkgs, PSTR(a_uri),
+		PSTR(a_idsName), a_repeat, PSTR(a_device));
+
+	/*
+	 * set flags for applicability check
+	 */
+
+	/* in the global zone */
+
+	flags |= CAF_IN_GLOBAL_ZONE;
+
+	/* set -G flag */
+
+	if (globalZoneOnly == B_TRUE) {
+		flags |= CAF_SCOPE_GLOBAL;
+	}
+
+	/*
+	 * this is the main loop where all of the packages (as listed in the
+	 * package list) are added one at a time.
+	 */
+
+	for (i = 0; (pkginst = a_pkgList[i]) != NULL; i++) {
+		/*
+		 * if immediate reboot required from last package and this is
+		 * not 'pkgask' then suspend installation of remaining packages
+		 */
+
+		if ((ireboot != 0) && (askflag == 0)) {
+			ptext(stderr, MSG_SUSPEND_ADD, pkginst);
+				continue;
+		}
+
+		/*
+		 * handle interrupt if the previous pkginstall was interrupted
+		 */
+
+		if (continue_installation() == B_FALSE) {
+			return (B_FALSE);
+		}
+
+		/*
+		 * check package applicability to install in this context
+		 */
+
+		if (check_applicability(pkgdev.dirname,
+			pkginst, get_inst_root(), flags) == B_FALSE) {
+			progerr(ERR_PKG_NOT_APPLICABLE, pkginst);
+			quit(1);
+		}
+
+		/*
+		 * if pkgask, handle response file creation:
+		 * - if the response file is a directory, then create a path to
+		 * -- a package instance within the response file directory.
+		 * - If the response file is NOT a directory, if more than one
+		 * -- package is to be installed.
+		 */
+
+		if ((askflag != 0) && (respdir != (char *)NULL)) {
+			(void) snprintf(respfile_path, sizeof (respfile_path),
+					"%s/%s", respdir, pkginst);
+			respfile = respfile_path;
+		}
+
+		echo(MSG_PROC_INST, pkginst,
+			(a_uri && a_idsName) ? a_uri : a_device);
+
+		/*
+		 * If we're installing another package in the same
+		 * session, the second through nth pkginstall, must
+		 * continue from where the prior one left off. For this
+		 * reason, the continuation feature (implied by the
+		 * nature of the command) is used for the remaining
+		 * packages.
+		 */
+
+		if ((i == 1) && (pkgdrtarg != (char *)NULL)) {
+			pkgcontsrc = pkgdrtarg;
+		}
+
+		/* reset interrupted flag before calling pkginstall */
+
+		interrupted = 0;	/* last action was NOT quit */
+
+		/* call pkginstall for this package */
+
+		n = pkgInstall(get_inst_root(), a_idsName,
+				pkgdev.dirname, a_altBinDir,
+				z_get_inherited_file_systems());
+
+		/* set success/fail condition variables */
+
+		ckreturn(n);
+
+		/* decrement number of packages left to install */
+
+		npkgs--;
+
+		/*
+		 * if no packages left to install, unmount package source
+		 * device if appropriate
+		 */
+
+		if ((npkgs <= 0) && (pkgdev.mount || a_idsName)) {
+			(void) chdir("/");
+			if (!a_idsName) {
+				(void) pkgumount(&pkgdev);
+			}
+		}
+	}
+
+	/*
+	 * all packages in the package list have been installed.
+	 * Continue with installation if:
+	 * -- immediate reboot is NOT required
+	 * -- there are more packages to install
+	 * -- the package source is a path to a file
+	 * else return do NOT continue.
+	 */
+
+	if ((ireboot == 0) && (a_repeat != 0) &&
+		(pkgdev.pathname == (char *)NULL)) {
+		return (B_TRUE);
+	}
+
+	/* return 'dont continue' */
+
+	return (B_FALSE);
+}
+
+/*
+ * returns:
+ *	B_TRUE to process next data stream
+ *	B_FALSE to exit
+ */
+
+static boolean_t
+add_packages(char **a_pkgList, char *a_uri,
+	char *a_idsName, int a_repeat, char *a_altBinDir, char *a_device,
+	boolean_t a_noZones)
+{
+	zoneList_t	zlst;
+	boolean_t	b;
+
+	/* entry assertions */
+
+	assert(a_pkgList != (char **)NULL);
+
+	echoDebug(DBG_ADDPACKAGES_ENTRY);
+	echoDebug(DBG_ADDPACKAGES_ARGS, npkgs, PSTR(a_uri), PSTR(a_idsName),
+		a_repeat, PSTR(a_altBinDir), PSTR(a_device));
+
+	/*
+	 * if running in the global zone AND one or more non-global
+	 * zones exist, add packages in a 'zones aware' manner, else
+	 * add packages in the standard 'non-zones aware' manner.
+	 */
+
+	if ((a_noZones == B_FALSE) && (z_running_in_global_zone() == B_FALSE)) {
+		/* in non-global zone */
+
+		echoDebug(DBG_IN_LZ);
+
+		b = z_lock_this_zone(ZLOCKS_PKG_ADMIN);
+		if (b != B_TRUE) {
+			progerr(ERR_CANNOT_LOCK_THIS_ZONE);
+			/* set fatal error return condition */
+			ckreturn(1);
+			return (B_FALSE);
+		}
+
+		b = add_packages_in_nonglobal_zone(a_pkgList, a_uri, a_idsName,
+			a_repeat, a_altBinDir, a_device);
+
+		(void) z_unlock_this_zone(ZLOCKS_ALL);
+
+		return (B_FALSE);
+	}
+
+	/* running in the global zone */
+
+	b = z_non_global_zones_exist();
+	if ((a_noZones == B_FALSE) && (b == B_TRUE) &&
+					(globalZoneOnly == B_FALSE)) {
+
+		echoDebug(DBG_IN_GZ_WITH_LZ);
+
+		/* error if -V specified - what to use in non-global zone? */
+
+		if (vfstab_file) {
+			progerr(ERR_V_USED_WITH_GZS);
+			quit(1);
+		}
+
+		/* get a list of all non-global zones */
+		zlst = z_get_nonglobal_zone_list();
+		if (zlst == (zoneList_t)NULL) {
+			progerr(ERR_CANNOT_GET_ZONE_LIST);
+			quit(1);
+		}
+
+		/* need to lock all of the zones */
+
+		quitSetZonelist(zlst);
+		b = z_lock_zones(zlst, ZLOCKS_PKG_ADMIN);
+		if (b == B_FALSE) {
+			z_free_zone_list(zlst);
+			progerr(ERR_CANNOT_LOCK_ZONES);
+			/* set fatal error return condition */
+			ckreturn(1);
+			return (B_FALSE);
+		}
+
+		/* add packages to all zones */
+
+		b = add_packages_in_global_with_zones(a_pkgList, a_uri,
+			a_idsName, a_repeat, a_altBinDir, a_device, zlst);
+
+		/* unlock all zones */
+
+		(void) z_unlock_zones(zlst, ZLOCKS_ALL);
+		quitSetZonelist((zoneList_t)NULL);
+
+		/* free list of all non-global zones */
+
+		z_free_zone_list(zlst);
+
+		return (B_FALSE);
+	}
+
+	/* in global zone no non-global zones */
+
+	echoDebug(DBG_IN_GZ_NO_LZ);
+
+	b = z_lock_this_zone(ZLOCKS_PKG_ADMIN);
+	if (b != B_TRUE) {
+		progerr(ERR_CANNOT_LOCK_THIS_ZONE);
+		/* set fatal error return condition */
+		ckreturn(1);
+		return (B_FALSE);
+	}
+
+	b = add_packages_in_global_no_zones(a_pkgList, a_uri, a_idsName,
+		a_repeat, a_altBinDir, a_device);
+
+	(void) z_unlock_this_zone(ZLOCKS_ALL);
+
+	return (B_FALSE);
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/usr/src/cmd/svr4pkg/pkgadd/presvr4.c	Fri Jun 05 10:28:40 2009 -0400
@@ -0,0 +1,172 @@
+/*
+ * 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
+ * or http://www.opensolaris.org/os/licensing.
+ * 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 2009 Sun Microsystems, Inc.  All rights reserved.
+ * Use is subject to license terms.
+ */
+
+/* Copyright (c) 1984, 1986, 1987, 1988, 1989 AT&T */
+/* All Rights Reserved */
+
+
+/*
+ * system includes
+ */
+#include <stdio.h>
+#include <signal.h>
+#include <string.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <limits.h>
+#include <sys/stat.h>
+#include <sys/utsname.h>
+#include <pkginfo.h>
+#include <pkgstrct.h>
+#include <pkgdev.h>
+#include <pkglocs.h>
+#include <locale.h>
+#include <libintl.h>
+
+/*
+ * consolidation pkg command library includes
+ */
+#include <pkglib.h>
+#include <messages.h>
+
+/*
+ * local pkg command library includes
+ */
+#include <install.h>
+#include <libinst.h>
+#include <libadm.h>
+
+/*
+ * pkgadd local includes
+ */
+#include "quit.h"
+
+
+extern struct admin adm;
+extern struct pkgdev pkgdev;
+extern char	*respfile;
+extern char	*tmpdir;
+extern int	warnflag;
+
+static void	intf_reloc(void);
+
+/*
+ * *****************************************************************************
+ * global external (public) functions
+ * *****************************************************************************
+ */
+
+int
+presvr4(char **ppkg, int a_nointeract)
+{
+	int	retcode;
+	char	*tmpcmd, path[PATH_MAX];
+	void	(*tmpfunc)();
+
+	echo(MSG_INSTALLING_PSVR4);
+	if (a_nointeract) {
+		progerr(ERR_NOINT);
+		quit(1);
+		/* NOTREACHED */
+	}
+
+	if (respfile) {
+		progerr(ERR_RESPFILE);
+		quit(1);
+		/* NOTREACHED */
+	}
+
+	/*
+	 * if we were looking for a particular package, verify
+	 * the first media has a /usr/options file on it
+	 * which matches
+	 */
+	psvr4pkg(ppkg);
+
+	/*
+	 * check to see if we can guess (via Rlist) what
+	 * pathnames this package is likely to install;
+	 * if we can, check these against the 'contents'
+	 * file and warn the administrator that these
+	 * pathnames might be modified in some manner
+	 */
+	psvr4cnflct();
+
+	if (chdir(tmpdir)) {
+		progerr(ERR_CHDIR, tmpdir);
+		quit(99);
+		/* NOTREACHED */
+	}
+
+	(void) snprintf(path, sizeof (path), "%s/install/INSTALL",
+			pkgdev.dirname);
+
+	tmpcmd = tempnam(tmpdir, "INSTALL");
+	if (!tmpcmd || copyf(path, tmpcmd, 0L) || chmod(tmpcmd, 0500)) {
+		progerr(ERR_NOCOPY, tmpdir);
+		quit(99);
+		/* NOTREACHED */
+	}
+
+	echo(MSG_EXE_INSTALL_SCRIPT);
+
+	retcode = pkgexecl(NULL, NULL, NULL, NULL, SHELL, "-c", tmpcmd,
+	    pkgdev.bdevice, pkgdev.dirname, NULL);
+
+	echo(retcode ? MSG_FAIL : gettext(MSG_SUCCEED));
+
+	(void) unlink(tmpcmd);
+	(void) chdir("/");
+	(void) pkgumount(&pkgdev);
+
+	psvr4mail(adm.mail, MSG_MAIL, retcode, *ppkg ? *ppkg : MSG_NODENAME);
+
+	/* tell quit to call intf_reloc on exit */
+
+	quitSetIntfReloc(&intf_reloc);
+
+	return (retcode);
+}
+
+/*
+ * *****************************************************************************
+ * static internal (private) functions
+ * *****************************************************************************
+ */
+
+/*
+ * When quit() gains control this function will be invoked if quitSetIntfReloc()
+ * is called specifying this function - see presvr4() above for details.
+ */
+
+static void
+intf_reloc(void)
+{
+	char	path[PATH_MAX];
+
+	(void) snprintf(path, sizeof (path), "%s/intf_reloc", PKGBIN);
+	(void) pkgexecl(NULL, NULL, NULL, NULL, SHELL, "-c", path, NULL);
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/usr/src/cmd/svr4pkg/pkgadd/quit.c	Fri Jun 05 10:28:40 2009 -0400
@@ -0,0 +1,409 @@
+/*
+ * 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
+ * or http://www.opensolaris.org/os/licensing.
+ * 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 2009 Sun Microsystems, Inc.  All rights reserved.
+ * Use is subject to license terms.
+ */
+
+/* Copyright (c) 1984, 1986, 1987, 1988, 1989 AT&T */
+/* All Rights Reserved */
+
+#include <stdio.h>
+#include <signal.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <pkgdev.h>
+#include <locale.h>
+#include <libintl.h>
+
+#include <pkglib.h>
+#include <pkgweb.h>
+#include <messages.h>
+
+#include <libadm.h>
+#include <libinst.h>
+
+#include "quit.h"
+
+/*
+ * imported global variables
+ */
+
+/* imported from main.c */
+
+extern struct pkgdev pkgdev;	/* holds info about the installation device */
+
+extern int	npkgs;		/* the number of packages yet to be installed */
+extern int	admnflag;	/* != 0 if any pkgop admin setting failed (4) */
+extern int	doreboot;	/* != 0 if reboot required after installation */
+extern int	failflag;	/* != 0 if fatal error has occurred (1) */
+extern int	intrflag;	/* != 0 if user selected quit (3) */
+extern int	ireboot;	/* != 0 if immediate reboot required */
+extern int	nullflag;	/* != 0 if admin interaction required (5) */
+extern int	warnflag;	/* != 0 if non-fatal error has occurred (2) */
+
+/*
+ * forward declarations
+ */
+
+static char		*dwnldTempDir = (char *)NULL;
+static char		*idsName = (char *)NULL;
+static char		*zoneTempDir = (char *)NULL;
+static ckreturnFunc_t	*ckreturnFunc = (ckreturnFunc_t *)NULL;
+static int		trapEntered = 0;
+static intfRelocFunc_t	*intfRelocFunc = (intfRelocFunc_t *)NULL;
+static void		trap(int signo);
+static zoneList_t	zoneList = (zoneList_t)NULL;
+
+/*
+ * exported functions
+ */
+
+void		quit(int retcode);
+void		quitSetCkreturnFunc(ckreturnFunc_t *a_ckreturnFunc);
+void		quitSetDwnldTmpdir(char *a_dwnldTempDir);
+void		quitSetIdsName(char *a_idsName);
+void		quitSetZoneName(char *a_zoneName);
+void		quitSetZoneTmpdir(char *z_zoneTempDir);
+void		quitSetZonelist(zoneList_t a_zlst);
+sighdlrFunc_t	*quitGetTrapHandler(void);
+
+/*
+ * *****************************************************************************
+ * global external (public) functions
+ * *****************************************************************************
+ */
+
+/*
+ * Name:	quitGetTrapHandler
+ * Description:	return address of this modules "signal trap" handler
+ * Arguments:	void
+ * Returns:	sighdlrFunc_t
+ *			The address of the trap handler that can be passed to
+ *			the signal() type system calls
+ */
+
+sighdlrFunc_t *
+quitGetTrapHandler()
+{
+	return (&trap);
+}
+
+/*
+ * Name:	quitSetIdsName
+ * Description:	set the input data stream name to use when quit() is called
+ * Arguments:	a_idsName - pointer to string representing the input data
+ *			stream object currently open
+ *			== NULL - there is no input datastream object to use
+ * Returns:	void
+ * NOTE:	When quit() is called, if an input datastream object is set,
+ *		quit will close the datastream and cleanup certain objects
+ *		associated with the datastream
+ */
+
+void
+quitSetIdsName(char *a_idsName)
+{
+	idsName = a_idsName;
+}
+
+/*
+ * Name:	quitSetIntfReloc
+ * Description:	set the "intf_reloc" interface to run when quit() is called
+ * Arguments:	a_intfReloc - pointer to function to call when quit() is called
+ * Returns:	void
+ * NOTE:	When quit() is called, if an "intf_reloc" function is set, quit
+ *		will call that function to perform whatever operations it needs
+ *		to perform - typically this is needed to run "intf_reloc" when
+ *		pre-SVR4 packages have been installed
+ */
+
+void
+quitSetIntfReloc(intfRelocFunc_t *a_intfReloc)
+{
+	intfRelocFunc = a_intfReloc;
+}
+
+/*
+ * Name:	quitSetCkreturnFunc
+ * Description:	set the ckreturn() interface to call when quit() is called
+ * Arguments:	a_ckreturnFunc - pointer to function to call when quit() is
+ *			called
+ * Returns:	void
+ * NOTE:	When quit() is called if a "ckreturnfunc" is set, then the first
+ *		action quit() takes is to call the "ckreturnfunc" specified with
+ *		the value passed to quit as the first argument. Quit will then
+ *		set the final return code to be used when exit() is called based
+ *		on the contents of these global variables:
+ *		 - admnflag - != 0 if any pkgop admin setting failed (4)
+ *		 - doreboot - != 0 if reboot required after installation
+ *		 - failflag - != 0 if fatal error has occurred (1)
+ *		 - intrflag - != 0 if user selected quit (3)
+ *		 - ireboot - != 0 if immediate reboot required
+ *		 - nullflag - != 0 if admin interaction required (5)
+ *		 - warnflag - != 0 if non-fatal error has occurred (2)
+ */
+
+void
+quitSetCkreturnFunc(ckreturnFunc_t *a_ckreturnFunc)
+{
+	ckreturnFunc = a_ckreturnFunc;
+}
+
+/*
+ * Name:	quitSetZonelist
+ * Description:	set the list of zones that are "locked" so that the zones can
+ *		be unlocked if quit() is called to exit
+ * Arguments:	a_zlst - list of zones that are "locked"
+ * Returns:	void
+ * NOTE:	When quit() is called, if this list is set, then z_unlock_zones
+ *		is called to unlock all of the zones in the list. If this list
+ *		is NOT set, then z_unlock_this_zone is called to unlock this
+ *		zone.
+ */
+
+void
+quitSetZonelist(zoneList_t a_zlst)
+{
+	zoneList = a_zlst;
+}
+
+/*
+ * Name:	quitSetZoneName
+ * Description:	set the zone name the program is running in
+ * Arguments:	a_zoneName - pointer to string representing the name of the zone
+ *			that the program is running in
+ * Returns:	void
+ */
+
+/* ARGSUSED */
+void
+quitSetZoneName(char *a_zoneName)
+{
+}
+
+/*
+ * Name:	quitSetZoneTmpdir
+ * Description:	set the path to the "zone temporary directory" in use
+ * Arguments:	a_zoneTempDir - pointer to string representing the full path to
+ *			the temporary directory used to hold files used during
+ *			zone operations
+ * Returns:	void
+ * NOTE:	If a zone temporary directory is set when quit() is called, the
+ *		directory is recursively removed before quit() calls exit
+ */
+
+void
+quitSetZoneTmpdir(char *a_zoneTempDir)
+{
+	zoneTempDir = a_zoneTempDir;
+}
+
+/*
+ * Name:	quitSetDwnldTmpdir
+ * Description:	set the path to the "download temporary directory" in use
+ * Arguments:	a_dwnldTempDir - pointer to string representing the full path to
+ *			the temporary directory used to hold files used during
+ *			download operations
+ * Returns:	void
+ * NOTE:	If a download temporary directory is set when quit() is called,
+ *		the directory is recursively removed before quit() calls exit
+ */
+
+void
+quitSetDwnldTmpdir(char *a_dwnldTempDir)
+{
+	dwnldTempDir = a_dwnldTempDir;
+}
+
+/*
+ * Name:	quit
+ * Description:	cleanup and exit
+ * Arguments:	a_retcode - the code to use to determine final exit status;
+ *			if this is NOT "99" and if a "ckreturnFunc" is
+ *			set, then that function is called with a_retcode
+ *			to set the final exit status.
+ *		Valid values are:
+ *		0 - success
+ *		1 - package operation failed (fatal error)
+ *		2 - non-fatal error (warning)
+ *		3 - user selected quit (operation interrupted)
+ *		4 - admin settings prevented operation
+ *		5 - interaction required and -n (non-interactive) specified
+ *		"10" is added to indicate "immediate reboot required"
+ *		"20" is be added to indicate "reboot after install required"
+ *		99 - do not interpret the code - just exit "99"
+ * Returns:	<<this function does not return - calls exit()>>
+ */
+
+void
+quit(int a_retcode)
+{
+	/* disable interrupts */
+
+	(void) signal(SIGINT, SIG_IGN);
+	(void) signal(SIGHUP, SIG_IGN);
+
+	if (!restore_local_fs()) {
+		progerr(ERR_CANNOT_RESTORE_LOCAL_FS);
+	}
+
+	/* process return code if not quit(99) */
+
+	if (a_retcode != 99) {
+		if (ckreturnFunc != (ckreturnFunc_t *)NULL) {
+			(ckreturnFunc)(a_retcode);
+		}
+		if (failflag) {
+			a_retcode = 1;
+		} else if (warnflag) {
+			a_retcode = 2;
+		} else if (intrflag) {
+			a_retcode = 3;
+		} else if (admnflag) {
+			a_retcode = 4;
+		} else if (nullflag) {
+			a_retcode = 5;
+		} else {
+			a_retcode = 0;
+		}
+		if (ireboot) {
+			a_retcode = (a_retcode % 10) + 20;
+		}
+		if (doreboot) {
+			a_retcode = (a_retcode % 10) + 10;
+		}
+	}
+
+	if (doreboot || ireboot) {
+		ptext(stderr, MSG_REBOOT);
+	}
+
+	(void) chdir("/");
+
+	/* if set remove download temporary directory */
+
+	if (dwnldTempDir != (char *)NULL) {
+		echoDebug(DBG_REMOVING_DWNLD_TMPDIR, dwnldTempDir);
+		(void) rrmdir(dwnldTempDir);
+		dwnldTempDir = (char *)NULL;
+	}
+
+	/* if set remove zone temporary directory */
+
+	if (zoneTempDir != (char *)NULL) {
+		echoDebug(DBG_REMOVING_ZONE_TMPDIR, zoneTempDir);
+		(void) rrmdir(zoneTempDir);
+		zoneTempDir = (char *)NULL;
+	}
+
+	/* close and cleanup if input datastream is set */
+
+	if (idsName != (char *)NULL) { /* datastream */
+		if (pkgdev.dirname != NULL) {
+			echoDebug(DBG_REMOVING_DSTREAM_TMPDIR, pkgdev.dirname);
+			(void) rrmdir(pkgdev.dirname);  /* from tempnam */
+		}
+		/*
+		 * cleanup after a web-based install.
+		 * web-based install failures
+		 * are indicated by exit codes 10-98
+		 * exit code 99 is fatal error exit.
+		 */
+		if (pkgdev.pathname != NULL && is_web_install() &&
+			(a_retcode == 0 ||
+				(a_retcode >= 10 && a_retcode < 99))) {
+			(void) web_cleanup();
+		}
+		(void) ds_close(1);
+	} else if (pkgdev.mount) {
+		(void) pkgumount(&pkgdev);
+	}
+
+	/*
+	 * issue final exit message depending on number of packages left
+	 * to process
+	 */
+
+	if (npkgs == 1) {
+		echo(MSG_1_PKG_NOT_PROCESSED);
+	} else if (npkgs) {
+		echo(MSG_N_PKGS_NOT_PROCESSED, npkgs);
+	}
+
+	/* call intf_reloc function if registered */
+
+	if (intfRelocFunc != (intfRelocFunc_t *)NULL) {
+		(intfRelocFunc)();
+	}
+
+	/* if a zone list exists, unlock all zones */
+
+	if (zoneList != (zoneList_t)NULL) {
+		(void) z_unlock_zones(zoneList, ZLOCKS_ALL);
+	} else {
+		(void) z_unlock_this_zone(ZLOCKS_ALL);
+	}
+
+	/* final exit debugging message */
+
+	echoDebug(DBG_EXIT_WITH_CODE, a_retcode);
+
+	exit(a_retcode);
+	/* NOTREACHED */
+}
+
+/*
+ * *****************************************************************************
+ * static internal (private) functions
+ * *****************************************************************************
+ */
+
+/*
+ * Name:	trap
+ * Description:	signal handler connected via quitGetTrapHandler()
+ * Arguments:	signo - [RO, *RO] - (int)
+ *			Integer representing the signal that caused the trap
+ *			to this function to occur
+ * Returns:	<< NONE >>
+ * NOTE:	This function exits the program after doing mandatory cleanup.
+ * NOTE:	Even though quit() should NOT return, there is a call to _exit()
+ *		put after each call to quit() just in case quit() ever returned
+ *		by mistake.
+ */
+
+static void
+trap(int signo)
+{
+	/* prevent reentrance */
+
+	if (trapEntered++ != 0) {
+		return;
+	}
+
+	if ((signo == SIGINT) || (signo == SIGHUP)) {
+		quit(3);
+		_exit(3);
+	}
+	quit(1);
+	_exit(1);
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/usr/src/cmd/svr4pkg/pkgadd/quit.h	Fri Jun 05 10:28:40 2009 -0400
@@ -0,0 +1,64 @@
+/*
+ * 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
+ * or http://www.opensolaris.org/os/licensing.
+ * 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 2009 Sun Microsystems, Inc.  All rights reserved.
+ * Use is subject to license terms.
+ */
+
+/*
+ * Header:	pkgadd: quit.c
+ *
+ * Function:	external definitions for references to the quit.c module
+ *
+ */
+
+#ifndef	__PKGADD_QUIT_H__
+#define	__PKGADD_QUIT_H__
+
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#include <instzones_api.h>
+#include <libinst.h>
+
+/*
+ * exported (global) functions
+ */
+
+typedef void (intfRelocFunc_t)(void);
+
+extern sighdlrFunc_t *quitGetTrapHandler(void);
+extern void	quit(int retcode);
+extern void	quitSetCkreturnFunc(ckreturnFunc_t *a_ckreturnFunc);
+extern void	quitSetDwnldTmpdir(char *z_dwnldTempDir);
+extern void	quitSetIdsName(char *a_idsName);
+extern void	quitSetIntfReloc(intfRelocFunc_t *a_intfReloc);
+extern void	quitSetZoneName(char *a_zoneName);
+extern void	quitSetZoneTmpdir(char *z_zoneTempDir);
+extern void	quitSetZonelist(zoneList_t a_zlst);
+#ifdef __cplusplus
+}
+#endif
+
+#endif	/* __PKGADD_QUIT_H__ */
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/usr/src/cmd/svr4pkg/pkgadm/Makefile	Fri Jun 05 10:28:40 2009 -0400
@@ -0,0 +1,45 @@
+#
+# 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
+# or http://www.opensolaris.org/os/licensing.
+# 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 2009 Sun Microsystems, Inc.  All rights reserved.
+# Use is subject to license terms.
+#
+
+PROG=           pkgadm
+
+OBJS=           addcert.o	\
+		certs.o		\
+		listcert.o	\
+		lock.o		\
+		main.o		\
+		removecert.o
+
+include $(SRC)/cmd/svr4pkg/Makefile.svr4pkg
+
+LDLIBS	+=	-lpkg -lcrypto -lgen
+
+.KEEP_STATE:
+all:            $(PROG)
+
+install:	all $(ROOTPROG)
+
+include $(SRC)/cmd/svr4pkg/Makefile.svr4pkg.targ
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/usr/src/cmd/svr4pkg/pkgadm/addcert.c	Fri Jun 05 10:28:40 2009 -0400
@@ -0,0 +1,573 @@
+/*
+ * 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
+ * or http://www.opensolaris.org/os/licensing.
+ * 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 2009 Sun Microsystems, Inc.  All rights reserved.
+ * Use is subject to license terms.
+ */
+
+
+#include <stdio.h>
+#include <stdarg.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/types.h>
+#include <unistd.h>
+#include <signal.h>
+#include <locale.h>
+#include <sys/param.h>
+#include <openssl/bio.h>
+#include <openssl/x509v3.h>
+#include <openssl/ui.h>
+
+#include <pkglib.h>
+#include <libinst.h>
+#include <pkgerr.h>
+#include <keystore.h>
+#include "pkgadm.h"
+#include "pkgadm_msgs.h"
+
+typedef enum {
+	VerifyFailed,
+	Accept,
+	Reject
+} VerifyStatus;
+
+static VerifyStatus	verify_trust(X509 *);
+static boolean_t	is_ca_cert(X509 *);
+
+/*
+ * Name:	addcert
+ * Desc:  	Imports a user certificate into the keystore, along with a
+ *		private key.
+ * Returns:	0 on success, non-zero otherwise.
+ */
+int
+addcert(int argc, char **argv)
+{
+	int i;
+	char	keystore_file[MAXPATHLEN] = "";
+	char	*keystore_base = NULL;
+	char	*homedir;
+	char	*passarg = NULL;
+	char	*import_passarg = NULL;
+	char	*altroot = NULL;
+	char	*prog = NULL;
+	char	*alias = NULL;
+	char	*infile = NULL;
+	char	*inkeyfile = NULL;
+	keystore_encoding_format_t	informat = NULL;
+	char	*informat_str = NULL;
+	int	ret = 1;
+	boolean_t	trusted = B_FALSE;
+	boolean_t	implicit_trust = B_FALSE;
+
+	FILE	*certfile = NULL;
+	FILE	*keyfile = NULL;
+	X509	*cert = NULL;
+	STACK_OF(X509) *trustcerts = NULL;
+	EVP_PKEY *key = NULL;
+	PKG_ERR	*err = NULL;
+	keystore_handle_t	keystore = NULL;
+
+	while ((i = getopt(argc, argv, ":a:k:e:f:n:P:p:R:ty")) != EOF) {
+		switch (i) {
+		case 'a':
+			prog = optarg;
+			break;
+		case 'k':
+			keystore_base = optarg;
+			break;
+		case 'e':
+			inkeyfile = optarg;
+			break;
+		case 'f':
+			informat_str = optarg;
+			break;
+		case 'n':
+			alias = optarg;
+			break;
+		case 'P':
+			passarg = optarg;
+			break;
+		case 'p':
+			import_passarg = optarg;
+			break;
+		case 'R':
+			altroot = optarg;
+			break;
+		case 't':
+			trusted = B_TRUE;
+			break;
+		case 'y':
+			implicit_trust = B_TRUE;
+			break;
+		case ':':
+			log_msg(LOG_MSG_ERR, MSG_MISSING_OPERAND, optopt);
+			/* LINTED fallthrough intentional */
+		case '?':
+		default:
+			log_msg(LOG_MSG_ERR, MSG_USAGE);
+			goto cleanup;
+		}
+	}
+
+	if (!trusted && alias == NULL) {
+		/* for untrusted (user) certs, we require a name */
+		log_msg(LOG_MSG_ERR, MSG_USER_NAME);
+		log_msg(LOG_MSG_ERR, MSG_USAGE);
+		goto cleanup;
+	} else if (trusted && alias != NULL) {
+		/* for trusted certs, we cannot have a name */
+		log_msg(LOG_MSG_ERR, MSG_TRUSTED_NAME);
+		log_msg(LOG_MSG_ERR, MSG_USAGE);
+		goto cleanup;
+	}
+
+	if (trusted && inkeyfile != NULL) {
+		/* for trusted certs, we cannot have a private key */
+		log_msg(LOG_MSG_ERR, MSG_TRUSTED_KEY);
+		log_msg(LOG_MSG_ERR, MSG_USAGE);
+		goto cleanup;
+	}
+
+	/* last argument should be the path to the certificate */
+	if ((argc-optind) > 1) {
+	    log_msg(LOG_MSG_ERR, MSG_USAGE);
+	    goto cleanup;
+	} else if ((argc-optind) < 1) {
+		infile = "stdin";
+		certfile = stdin;
+		log_msg(LOG_MSG_DEBUG, "Loading stdin certificate");
+	} else {
+		infile = argv[optind];
+		log_msg(LOG_MSG_DEBUG, "Loading <%s> certificate",
+		    argv[optind]);
+		if ((certfile = fopen(infile, "r")) == NULL) {
+			log_msg(LOG_MSG_ERR, MSG_OPEN, infile);
+			goto cleanup;
+		}
+	}
+
+	/*
+	 * if specific key file supplied, open it, otherwise open
+	 * default (stdin)
+	 */
+	if (inkeyfile != NULL) {
+		if ((keyfile = fopen(inkeyfile, "r")) == NULL) {
+			log_msg(LOG_MSG_ERR, MSG_OPEN, inkeyfile);
+			goto cleanup;
+		}
+	} else {
+		inkeyfile = "stdin";
+		keyfile = stdin;
+	}
+
+	/* set up proper keystore */
+	if (altroot != NULL) {
+	    if (strlcpy(keystore_file, altroot, MAXPATHLEN) >= MAXPATHLEN) {
+		log_msg(LOG_MSG_ERR, MSG_TOO_LONG, altroot);
+		goto cleanup;
+	    }
+
+	    if (strlcat(keystore_file, "/", MAXPATHLEN) >= MAXPATHLEN) {
+		log_msg(LOG_MSG_ERR, MSG_TOO_LONG, altroot);
+		goto cleanup;
+	    }
+	}
+
+	if (keystore_base == NULL) {
+		if (geteuid() == 0 || altroot != NULL) {
+				/*
+				 * If we have an alternate
+				 * root, then we have no choice but to use
+				 * root's keystore on that alternate root,
+				 * since there is no way to resolve a
+				 * user's home dir given an alternate root
+				 */
+			if (strlcat(keystore_file, PKGSEC,
+			    MAXPATHLEN) >= MAXPATHLEN) {
+				log_msg(LOG_MSG_ERR, MSG_TOO_LONG,
+				    keystore_file);
+				goto cleanup;
+			}
+		} else {
+			if ((homedir = getenv("HOME")) == NULL) {
+				/*
+				 * not superuser, but no home dir, so
+				 * use superuser's keystore
+				 */
+				if (strlcat(keystore_file, PKGSEC,
+				    MAXPATHLEN) >= MAXPATHLEN) {
+					log_msg(LOG_MSG_ERR, MSG_TOO_LONG,
+					    keystore_file);
+					goto cleanup;
+				}
+			} else {
+				if (strlcat(keystore_file, homedir,
+				    MAXPATHLEN) >= MAXPATHLEN) {
+					log_msg(LOG_MSG_ERR, MSG_TOO_LONG,
+					    homedir);
+					goto cleanup;
+				}
+				if (strlcat(keystore_file, "/.pkg/security",
+				    MAXPATHLEN) >= MAXPATHLEN) {
+					log_msg(LOG_MSG_ERR, MSG_TOO_LONG,
+					    keystore_file);
+					goto cleanup;
+				}
+			}
+		}
+	} else {
+		if (strlcat(keystore_file, keystore_base,
+		    MAXPATHLEN) >= MAXPATHLEN) {
+		    log_msg(LOG_MSG_ERR, MSG_TOO_LONG,
+			keystore_base);
+		    goto cleanup;
+		}
+	}
+
+	/* figure out input format */
+	if (informat_str == NULL) {
+		informat = KEYSTORE_FORMAT_PEM;
+	} else {
+		if (ci_streq(informat_str, "pem")) {
+			informat = KEYSTORE_FORMAT_PEM;
+		} else if (ci_streq(informat_str, "der")) {
+			informat = KEYSTORE_FORMAT_DER;
+		} else {
+			log_msg(LOG_MSG_ERR, MSG_BAD_FORMAT, informat_str);
+			goto cleanup;
+		}
+	}
+
+	err = pkgerr_new();
+
+	if (trusted) {
+		/* load all possible certs */
+		if (load_all_certs(err, certfile, informat, import_passarg,
+		    &trustcerts) != 0) {
+			log_pkgerr(LOG_MSG_ERR, err);
+			log_msg(LOG_MSG_ERR, MSG_NO_ADDCERT, infile);
+			goto cleanup;
+		}
+
+		/* we must have gotten at least one cert, if not, fail */
+		if (sk_X509_num(trustcerts) < 1) {
+			log_msg(LOG_MSG_ERR, MSG_NO_CERTS, infile);
+			goto cleanup;
+		}
+	} else {
+		/* first, try to load user certificate and key */
+		if (load_cert_and_key(err, certfile, informat, import_passarg,
+		    &key, &cert) != 0) {
+			log_pkgerr(LOG_MSG_ERR, err);
+			log_msg(LOG_MSG_ERR, MSG_NO_ADDCERT, infile);
+			goto cleanup;
+		}
+
+		/* we must have gotten a cert, if not, fail */
+		if (cert == NULL) {
+			log_msg(LOG_MSG_ERR, MSG_NO_CERTS, infile);
+			goto cleanup;
+		}
+
+		if (key == NULL) {
+			/*
+			 * if we are importing a user cert, and did not get
+			 * a key, try to load it from the key file
+			 */
+			if (keyfile == NULL) {
+				log_msg(LOG_MSG_ERR, MSG_NEED_KEY, infile);
+				goto cleanup;
+			} else {
+				log_msg(LOG_MSG_DEBUG,
+				    "Loading private key <%s>", inkeyfile);
+				if (load_cert_and_key(err, keyfile, informat,
+				    import_passarg,
+				    &key, NULL) != 0) {
+					log_pkgerr(LOG_MSG_ERR, err);
+					log_msg(LOG_MSG_ERR,
+					    MSG_NO_ADDKEY, inkeyfile);
+					goto cleanup;
+				}
+
+				if (key == NULL) {
+					log_msg(LOG_MSG_ERR, MSG_NO_PRIVKEY,
+					    inkeyfile);
+					log_msg(LOG_MSG_ERR,
+					    MSG_NO_ADDKEY, inkeyfile);
+					goto cleanup;
+				}
+			}
+		}
+	}
+
+	if (trusted) {
+		/* check validity date of all certificates */
+		for (i = 0; i < sk_X509_num(trustcerts); i++) {
+			/* LINTED pointer cast may result in improper algnmnt */
+			cert = sk_X509_value(trustcerts, i);
+			if (check_cert(err, cert) != 0) {
+				log_pkgerr(LOG_MSG_ERR, err);
+				log_msg(LOG_MSG_ERR, MSG_NO_ADDCERT,
+				    infile);
+				goto cleanup;
+			}
+		}
+	} else {
+		/* check validity date of user certificate */
+		if (check_cert_and_key(err, cert, key) != 0) {
+			log_pkgerr(LOG_MSG_ERR, err);
+			log_msg(LOG_MSG_ERR, MSG_NO_ADDCERT, infile);
+			goto cleanup;
+		}
+	}
+
+	if (trusted && !implicit_trust) {
+		/*
+		 * if importing more than one cert, must use implicit trust,
+		 * because we can't ask the user to individually trust
+		 * each one, since there may be many
+		 */
+		if (sk_X509_num(trustcerts) != 1) {
+			log_pkgerr(LOG_MSG_ERR, err);
+			log_msg(LOG_MSG_ERR, MSG_MULTIPLE_TRUST, infile, "-y");
+			goto cleanup;
+		} else {
+			/* LINTED pointer cast may result in improper algnmnt */
+			cert = sk_X509_value(trustcerts, 0);
+		}
+
+		/* ask the user */
+		switch (verify_trust(cert)) {
+		case Accept:
+			/* user accepted */
+			break;
+		case Reject:
+			/* user aborted operation */
+			log_msg(LOG_MSG_ERR, MSG_ADDCERT_ABORT);
+			goto cleanup;
+		case VerifyFailed:
+		default:
+			log_msg(LOG_MSG_ERR, MSG_NO_ADDCERT, infile);
+			goto cleanup;
+		}
+	}
+
+	/* now load the key store */
+	log_msg(LOG_MSG_DEBUG, "Loading keystore <%s>", keystore_file);
+
+	set_passphrase_prompt(MSG_KEYSTORE_PASSPROMPT);
+	set_passphrase_passarg(passarg);
+	if (open_keystore(err, keystore_file, prog, pkg_passphrase_cb,
+	    KEYSTORE_ACCESS_READWRITE | KEYSTORE_PATH_HARD, &keystore) != 0) {
+		log_pkgerr(LOG_MSG_ERR, err);
+		log_msg(LOG_MSG_ERR, MSG_NO_ADDCERT, infile);
+		goto cleanup;
+	}
+
+	/* now merge the new cert into the keystore */
+	log_msg(LOG_MSG_DEBUG, "Merging certificate <%s>",
+	    get_subject_display_name(cert));
+	if (trusted) {
+		/* merge all trusted certs found */
+		for (i = 0; i < sk_X509_num(trustcerts); i++) {
+			/* LINTED pointer cast may result in improper algnmnt */
+			cert = sk_X509_value(trustcerts, i);
+			if (merge_ca_cert(err, cert, keystore) != 0) {
+				log_pkgerr(LOG_MSG_ERR, err);
+				log_msg(LOG_MSG_ERR,
+				    MSG_NO_ADDCERT, infile);
+				goto cleanup;
+
+			} else {
+				log_msg(LOG_MSG_INFO, MSG_TRUSTING,
+				    get_subject_display_name(cert));
+			}
+		}
+	} else {
+		/* merge user cert */
+		if (merge_cert_and_key(err, cert, key, alias, keystore) != 0) {
+			log_pkgerr(LOG_MSG_ERR, err);
+			log_msg(LOG_MSG_ERR, MSG_NO_ADDCERT, infile);
+			goto cleanup;
+		}
+	}
+
+	/* now write it back out */
+	log_msg(LOG_MSG_DEBUG, "Closing keystore");
+	set_passphrase_prompt(MSG_KEYSTORE_PASSOUTPROMPT);
+	set_passphrase_passarg(passarg);
+	if (close_keystore(err, keystore, pkg_passphrase_cb) != 0) {
+		log_pkgerr(LOG_MSG_ERR, err);
+		log_msg(LOG_MSG_ERR, MSG_NO_ADDCERT, infile);
+		goto cleanup;
+	}
+
+	if (trusted) {
+		log_msg(LOG_MSG_INFO, MSG_TRUSTED, infile);
+	} else {
+		log_msg(LOG_MSG_INFO, MSG_ADDED, infile, alias);
+	}
+
+	ret = 0;
+
+	/* fallthrough intentional */
+cleanup:
+	if (err != NULL)
+		pkgerr_free(err);
+
+	if (certfile != NULL)
+		(void) fclose(certfile);
+
+	if (keyfile != NULL)
+		(void) fclose(keyfile);
+
+	return (ret);
+	}
+
+/* Asks user to verify certificate data before proceeding */
+static VerifyStatus verify_trust(X509 *cert)
+{
+	char		vfy_trust = 'y';
+	VerifyStatus	ret = Accept;
+	PKG_ERR		*err;
+	UI		*ui = NULL;
+
+	err = pkgerr_new();
+	/* print cert data */
+	if (print_cert(err, cert, KEYSTORE_FORMAT_TEXT,
+	    get_subject_display_name(cert), B_TRUE, stdout) != 0) {
+		log_pkgerr(LOG_MSG_ERR, err);
+		ret = VerifyFailed;
+		goto cleanup;
+	}
+
+	if ((ui = UI_new()) == NULL) {
+		log_msg(LOG_MSG_ERR, MSG_MEM);
+		ret = VerifyFailed;
+		goto cleanup;
+	}
+
+	/*
+	 * The prompt is internationalized, but the valid
+	 * response values are fixed, to avoid any complex
+	 * multibyte processing that results in bugs
+	 */
+	if (UI_add_input_boolean(ui, MSG_VERIFY_TRUST,
+	    "",
+	    "yY", "nN",
+	    UI_INPUT_FLAG_ECHO, &vfy_trust) <= 0) {
+		log_msg(LOG_MSG_ERR, MSG_MEM);
+		ret = VerifyFailed;
+		goto cleanup;
+	}
+
+	if (UI_process(ui) != 0) {
+		log_msg(LOG_MSG_ERR, MSG_MEM);
+		ret = VerifyFailed;
+		goto cleanup;
+	}
+
+	if (vfy_trust != 'y') {
+		ret = Reject;
+		goto cleanup;
+	}
+
+	/*
+	 * if the cert does not appear to be a CA cert
+	 * r is not self-signed, verify that as well
+	 */
+	if (!is_ca_cert(cert)) {
+		UI_free(ui);
+		if ((ui = UI_new()) == NULL) {
+			log_msg(LOG_MSG_ERR, MSG_MEM);
+			ret = VerifyFailed;
+			goto cleanup;
+		}
+
+		if (UI_add_input_boolean(ui,
+		    MSG_VERIFY_NOT_CA,
+		    "",
+		    "yY", "nN",
+		    UI_INPUT_FLAG_ECHO, &vfy_trust) <= 0) {
+			ret = VerifyFailed;
+			goto cleanup;
+		}
+
+		if (UI_process(ui) != 0) {
+			log_msg(LOG_MSG_ERR, MSG_MEM);
+			ret = VerifyFailed;
+			goto cleanup;
+		}
+
+		if (vfy_trust != 'y') {
+			ret = Reject;
+			goto cleanup;
+		}
+	}
+
+cleanup:
+	if (ui != NULL)
+		UI_free(ui);
+
+	if (err != NULL)
+		pkgerr_free(err);
+
+	return (ret);
+}
+/*
+ *	Name:	is_ca_cert
+ *	Desc:	Determines if a given certificate has the attributes
+ *		of a CA certificate
+ *	Returns: B_TRUE if certificate has attributes of a CA cert
+ *		B_FALSE otherwise
+ */
+static boolean_t
+is_ca_cert(X509 *x)
+{
+
+	/*
+	 * X509_check_purpose causes the extensions that we
+	 * care about to be decoded and stored in the X509
+	 * structure, so we must call it first
+	 * before checking for CA extensions in the X509
+	 * structure
+	 */
+	(void) X509_check_purpose(x, X509_PURPOSE_ANY, 0);
+
+	/* keyUsage if present should allow cert signing */
+	if ((x->ex_flags & EXFLAG_KUSAGE) &&
+	    !(x->ex_kusage & KU_KEY_CERT_SIGN)) {
+		return (B_FALSE);
+	}
+
+	/* If basicConstraints says not a CA then say so */
+	if (x->ex_flags & EXFLAG_BCONS) {
+		if (!(x->ex_flags & EXFLAG_CA)) {
+			return (B_FALSE);
+		}
+	}
+
+	/* no explicit not-a-CA flags set, so assume that it is */
+	return (B_TRUE);
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/usr/src/cmd/svr4pkg/pkgadm/certs.c	Fri Jun 05 10:28:40 2009 -0400
@@ -0,0 +1,239 @@
+/*
+ * 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
+ * or http://www.opensolaris.org/os/licensing.
+ * 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 2009 Sun Microsystems, Inc.  All rights reserved.
+ * Use is subject to license terms.
+ */
+
+
+#include <stdio.h>
+#include <limits.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <string.h>
+#include <pkglocs.h>
+#include <locale.h>
+#include <libintl.h>
+#include <signal.h>
+#include <sys/stat.h>
+#include <sys/statvfs.h>
+#include <sys/types.h>
+#include <fcntl.h>
+#include <libintl.h>
+#include <dirent.h>
+#include <openssl/err.h>
+#include <openssl/pkcs7.h>
+#include <openssl/pkcs12.h>
+#include <openssl/x509.h>
+#include <openssl/pem.h>
+#include <openssl/x509v3.h>
+
+#include <pkglib.h>
+#include <p12lib.h>
+#include <install.h>
+#include <libadm.h>
+#include <libinst.h>
+#include "pkgadm.h"
+#include "pkgadm_msgs.h"
+
+
+/*
+ * Function:	load_cert_and_key
+ * Description:	Loads a public key certificate and associated private key
+ *		from a stream.
+ * Parameters:	err	- Where to write errors to for underlying library calls
+ *		incert - File to read certs and keys from
+ *		format - The format of the file
+ *		passarg - How to collect password if needed to decrypt file
+ *		key - Location to store resulting key if found
+ *		cert - Location to store resulting cert if found.
+ *
+ * Returns:	f one or more certificates are found in the file,
+ *		and one or more keys are found, then the first
+ *		certificate is used, and the keys are searched for a
+ *		match.  If no key matches the cert, then only the cert
+ *		is returned.  If no certs are found, but one or more
+ *		keys are found, then the first key is returned.
+ */
+int
+load_cert_and_key(PKG_ERR *err, FILE *incert,
+    keystore_encoding_format_t format, char *passarg, EVP_PKEY **key,
+    X509 **cert)
+{
+	X509 *tmpcert = NULL;
+	EVP_PKEY *tmpkey = NULL;
+	STACK_OF(EVP_PKEY)	*keys = NULL;
+	STACK_OF(X509)		*certs = NULL;
+	int i, ret = 0;
+	keystore_passphrase_data	data;
+	unsigned long crypto_err;
+
+	if (key) *key = NULL;
+	if (cert) *cert = NULL;
+
+	switch (format) {
+	case KEYSTORE_FORMAT_DER:
+		/* first try to load a DER cert, which cannot contain a key */
+		if ((tmpcert = d2i_X509_fp(incert, NULL)) == NULL) {
+			log_msg(LOG_MSG_ERR, MSG_PARSE);
+			ret = 1;
+		}
+		break;
+	case KEYSTORE_FORMAT_PEM:
+	default:
+		data.err = err;
+		set_passphrase_passarg(passarg);
+		set_passphrase_prompt(gettext("Enter PEM passphrase:"));
+		if (sunw_PEM_contents(incert, pkg_passphrase_cb,
+		    &data, &keys, &certs) < 0) {
+			/* print out openssl-generated PEM errors */
+			while ((crypto_err = ERR_get_error()) != 0) {
+				log_msg(LOG_MSG_ERR,
+				    ERR_reason_error_string(crypto_err));
+			}
+			ret = 1;
+			goto cleanup;
+		}
+
+		/* take the first cert in the file, if any */
+		if (cert && (certs != NULL)) {
+			if (sk_X509_num(certs) != 1) {
+				log_msg(LOG_MSG_ERR, MSG_MULTIPLE_CERTS);
+				ret = 1;
+				goto cleanup;
+			} else {
+				tmpcert = sk_X509_value(certs, 0);
+			}
+		}
+
+		if (key && (keys != NULL)) {
+			if (tmpcert != NULL) {
+				/*
+				 * if we found a cert and some keys,
+				 * only return the key that
+				 * matches the cert
+				 */
+				for (i = 0; i < sk_EVP_PKEY_num(keys); i++) {
+					if (X509_check_private_key(tmpcert,
+					    sk_EVP_PKEY_value(keys, i))) {
+						tmpkey =
+						    sk_EVP_PKEY_value(keys, i);
+						break;
+					}
+				}
+			} else {
+				if (sk_EVP_PKEY_num(keys) > 0) {
+					tmpkey = sk_EVP_PKEY_value(keys, 0);
+				}
+			}
+		}
+		break;
+	}
+
+	/* set results */
+	if (key && tmpkey) {
+		*key = tmpkey;
+		tmpkey = NULL;
+	}
+
+	if (cert && tmpcert) {
+		*cert = tmpcert;
+		tmpcert = NULL;
+	}
+
+cleanup:
+	if (tmpcert != NULL) {
+		X509_free(tmpcert);
+	}
+	if (tmpkey != NULL) {
+		sunw_evp_pkey_free(tmpkey);
+	}
+	return (ret);
+}
+
+/*
+ * Function:	load_all_certs
+ * Description:	Loads alll certificates from a stream.
+ * Parameters:	err	- Where to write errors to for underlying library calls
+ *		incert - File to read certs and keys from
+ *		format - The format of the file
+ *		passarg - How to collect password if needed to decrypt file
+ *		certs - Location to store resulting cert if found.
+ *
+ * Returns:	0 - success, all certs placed in ''certs'
+ *		non-zero failure, errors in 'err'
+ */
+int
+load_all_certs(PKG_ERR *err, FILE *incert,
+    keystore_encoding_format_t format, char *passarg, STACK_OF(X509) **certs)
+{
+	X509 *tmpcert = NULL;
+	STACK_OF(X509) *tmpcerts = NULL;
+	int ret = 0;
+	keystore_passphrase_data	data;
+	unsigned long crypto_err;
+	if (certs) *certs = NULL;
+
+	switch (format) {
+	case KEYSTORE_FORMAT_DER:
+		/* first try to load a DER cert, which cannot contain a key */
+		if ((tmpcert = d2i_X509_fp(incert, NULL)) == NULL) {
+		    log_msg(LOG_MSG_ERR, MSG_PARSE);
+			ret = 1;
+			goto cleanup;
+		}
+
+		if ((tmpcerts = sk_X509_new_null()) == NULL) {
+			log_msg(LOG_MSG_ERR, MSG_MEM);
+			ret = 1;
+			goto cleanup;
+		}
+		sk_X509_push(tmpcerts, tmpcert);
+		break;
+	case KEYSTORE_FORMAT_PEM:
+	default:
+		data.err = err;
+		set_passphrase_prompt(MSG_PEM_PASSPROMPT);
+		set_passphrase_passarg(passarg);
+		if (sunw_PEM_contents(incert, pkg_passphrase_cb,
+		    &data, NULL, &tmpcerts) < 0) {
+			/* print out openssl-generated PEM errors */
+			while ((crypto_err = ERR_get_error()) != 0) {
+				log_msg(LOG_MSG_ERR,
+				    ERR_reason_error_string(crypto_err));
+			}
+		}
+		break;
+	}
+
+	/* set results */
+	if (certs && tmpcerts) {
+		*certs = tmpcerts;
+		tmpcerts = NULL;
+	}
+
+cleanup:
+	if (tmpcerts != NULL) {
+		sk_X509_free(tmpcerts);
+	}
+	return (ret);
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/usr/src/cmd/svr4pkg/pkgadm/listcert.c	Fri Jun 05 10:28:40 2009 -0400
@@ -0,0 +1,245 @@
+/*
+ * 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
+ * or http://www.opensolaris.org/os/licensing.
+ * 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 2009 Sun Microsystems, Inc.  All rights reserved.
+ * Use is subject to license terms.
+ */
+
+
+#include <stdio.h>
+#include <stdarg.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/types.h>
+#include <unistd.h>
+#include <signal.h>
+#include <locale.h>
+#include <sys/param.h>
+#include <openssl/bio.h>
+
+#include <libinst.h>
+#include <pkglib.h>
+#include <pkgerr.h>
+#include <keystore.h>
+#include "pkgadm.h"
+#include "pkgadm_msgs.h"
+
+/*
+ * Name:	listcert
+ * Desc:  Lists one or more certificates from the keystore
+ * Syntax:	listcert [-a app] [-f format] [-k keystore] \
+ *	[-n name] [-o outfile] [-P passarg] [-R altroot]
+ */
+int
+listcert(int argc, char **argv)
+{
+	int				i;
+	char				keystore_file[MAXPATHLEN] = "";
+	char				*keystore_base = NULL;
+	char				*homedir;
+	char				*passarg = NULL;
+	char				*altroot = NULL;
+	char				*prog = NULL;
+	char				*format_str = NULL;
+	keystore_encoding_format_t	format;
+	char				*alias = NULL;
+	char				*outfile_str = NULL;
+	FILE				*outfile = NULL;
+	int				ret = 1;
+	PKG_ERR				*err = NULL;
+	keystore_handle_t		keystore = NULL;
+
+	while ((i = getopt(argc, argv, ":a:f:k:n:o:P:R:")) != EOF) {
+		switch (i) {
+		case 'a':
+			prog = optarg;
+			break;
+		case 'f':
+			format_str = optarg;
+			break;
+		case 'k':
+			keystore_base = optarg;
+			break;
+		case 'n':
+			alias = optarg;
+			break;
+		case 'o':
+			outfile_str = optarg;
+			break;
+		case 'P':
+			passarg = optarg;
+			break;
+		case 'R':
+			altroot = optarg;
+			break;
+		case ':':
+			log_msg(LOG_MSG_ERR, MSG_MISSING_OPERAND, optopt);
+			/* fallthrough intentional */
+		case '?':
+		default:
+			log_msg(LOG_MSG_ERR, MSG_USAGE);
+			goto cleanup;
+		}
+	}
+
+	/* should be no arguments left */
+	if ((argc-optind) > 0) {
+		log_msg(LOG_MSG_ERR, MSG_USAGE);
+		goto cleanup;
+	}
+
+	/* figure out format */
+	if (format_str == NULL) {
+		format = KEYSTORE_FORMAT_TEXT;
+	} else {
+		if (ci_streq(format_str, "text")) {
+			format = KEYSTORE_FORMAT_TEXT;
+		} else if (ci_streq(format_str, "pem")) {
+			format = KEYSTORE_FORMAT_PEM;
+		} else if (ci_streq(format_str, "der")) {
+			format = KEYSTORE_FORMAT_DER;
+		} else {
+			log_msg(LOG_MSG_ERR, MSG_BAD_FORMAT, format_str);
+			goto cleanup;
+		}
+	}
+
+	/* open output file */
+	if (outfile_str == NULL) {
+		outfile = stdout;
+		outfile_str = "stdout";
+	} else {
+		if ((outfile = fopen(outfile_str, "w+")) == NULL) {
+			log_msg(LOG_MSG_ERR, MSG_OPEN_WRITE, outfile_str);
+			goto cleanup;
+		}
+	}
+
+	/* set up proper keystore */
+	if (altroot != NULL) {
+	    if (strlcpy(keystore_file, altroot, MAXPATHLEN) >= MAXPATHLEN) {
+		log_msg(LOG_MSG_ERR, MSG_TOO_LONG, altroot);
+		goto cleanup;
+	    }
+
+	    if (strlcat(keystore_file, "/", MAXPATHLEN) >= MAXPATHLEN) {
+		log_msg(LOG_MSG_ERR, MSG_TOO_LONG, altroot);
+		goto cleanup;
+	    }
+	}
+
+	if (keystore_base == NULL) {
+		if (geteuid() == 0 || altroot != NULL) {
+				/*
+				 * If we have an alternate
+				 * root, then we have no choice but to use
+				 * root's keystore on that alternate root,
+				 * since there is no way to resolve a
+				 * user's home dir given an alternate root
+				 */
+			if (strlcat(keystore_file, PKGSEC,
+			    MAXPATHLEN) >= MAXPATHLEN) {
+				log_msg(LOG_MSG_ERR, MSG_TOO_LONG,
+				    keystore_file);
+				goto cleanup;
+			}
+		} else {
+			if ((homedir = getenv("HOME")) == NULL) {
+				/*
+				 * not superuser, but no home dir, so
+				 * use superuser's keystore
+				 */
+				if (strlcat(keystore_file, PKGSEC,
+				    MAXPATHLEN) >= MAXPATHLEN) {
+					log_msg(LOG_MSG_ERR, MSG_TOO_LONG,
+					    keystore_file);
+					goto cleanup;
+				}
+			} else {
+				if (strlcat(keystore_file, homedir,
+				    MAXPATHLEN) >= MAXPATHLEN) {
+					log_msg(LOG_MSG_ERR, MSG_TOO_LONG,
+					    homedir);
+					goto cleanup;
+				}
+				if (strlcat(keystore_file, "/.pkg/security",
+				    MAXPATHLEN) >= MAXPATHLEN) {
+					log_msg(LOG_MSG_ERR, MSG_TOO_LONG,
+					    keystore_file);
+					goto cleanup;
+				}
+			}
+		}
+	} else {
+		if (strlcat(keystore_file, keystore_base,
+		    MAXPATHLEN) >= MAXPATHLEN) {
+		    log_msg(LOG_MSG_ERR, MSG_TOO_LONG,
+			keystore_base);
+		    goto cleanup;
+		}
+	}
+	err = pkgerr_new();
+
+	/* now load the key store */
+	log_msg(LOG_MSG_DEBUG, "Loading keystore <%s>", keystore_file);
+
+	set_passphrase_prompt(MSG_KEYSTORE_PASSPROMPT);
+	set_passphrase_passarg(passarg);
+	if (open_keystore(err, keystore_file, prog,
+	    pkg_passphrase_cb, KEYSTORE_DFLT_FLAGS,
+	    &keystore) != 0) {
+		log_pkgerr(LOG_MSG_ERR, err);
+		log_msg(LOG_MSG_ERR, MSG_PRINT, outfile_str);
+		goto cleanup;
+	}
+
+	/* list the certs */
+	log_msg(LOG_MSG_DEBUG, "Listing certificates");
+	if (print_certs(err, keystore, alias, format, outfile) != 0) {
+		log_pkgerr(LOG_MSG_ERR, err);
+	    log_msg(LOG_MSG_ERR, MSG_PRINT, outfile_str);
+		goto cleanup;
+	}
+
+	/* now close it out */
+	log_msg(LOG_MSG_DEBUG, "Closing keystore");
+	set_passphrase_prompt(MSG_KEYSTORE_PASSOUTPROMPT);
+	set_passphrase_passarg(passarg);
+	if (close_keystore(err, keystore, pkg_passphrase_cb) != 0) {
+		log_pkgerr(LOG_MSG_ERR, err);
+		log_msg(LOG_MSG_ERR, MSG_PRINT, outfile_str);
+		goto cleanup;
+	}
+
+	/* everything worked */
+	ret = 0;
+
+	/* fallthrough intentional */
+cleanup:
+	if (outfile != NULL)
+		(void) fclose(outfile);
+
+	if (err != NULL)
+		pkgerr_free(err);
+
+	return (ret);
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/usr/src/cmd/svr4pkg/pkgadm/lock.c	Fri Jun 05 10:28:40 2009 -0400
@@ -0,0 +1,2150 @@
+/*
+ * 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
+ * or http://www.opensolaris.org/os/licensing.
+ * 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 2009 Sun Microsystems, Inc.  All rights reserved.
+ * Use is subject to license terms.
+ */
+
+
+/*
+ * Module: lock.c
+ * Program: pkgadm (/usr/bin/pkgadm)
+ * Synopsis: implements the zone/package administrative lock interface
+ * Public methods:
+ *	admin_lock
+ * Usage:
+ *  Acquire: -a [ -e | -s ] [ -o obj ] [ -k key ] [ -R root ] [ -q ] \
+ *		[ -w ] [ -W timeout ]
+ *  Release: -r -o object -k key [ -R altRoot ] [ -q ]
+ *  Status: [ -o object ] [ -k key ] [ -R altRoot ] [ -q ]
+ */
+
+/* enable extentions to standard Unix libraries */
+
+#define	__EXTENSIONS__
+
+/* unix system includes */
+
+#include <stdio.h>
+#include <stdarg.h>
+#include <stdlib.h>
+#include <string.h>
+#include <strings.h>
+#include <sys/types.h>
+#include <wait.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+#include <unistd.h>
+#include <signal.h>
+#include <locale.h>
+#include <libgen.h>
+#include <sys/param.h>
+#include <openssl/bio.h>
+#include <errno.h>
+#include <assert.h>
+#include <time.h>
+#include <fnmatch.h>
+#include <zone.h>
+
+/* local includes */
+
+#include <libinst.h>
+#include <pkglib.h>
+#include <pkgerr.h>
+#include <keystore.h>
+#include "pkgadm.h"
+#include "pkgadm_msgs.h"
+
+/* definition and conversion of sleep units */
+
+#define	SECONDS(x)		((unsigned int)(x))
+#define	MINUTES(x)		((unsigned int)(seconds(x)*60))
+
+/* define how waits are timed */
+
+#define	WAITER_INITIAL		SECONDS(1)
+#define	WAITER_MAX		SECONDS(60)
+#define	WAITER_NEXT(x)		((x)*2)
+
+typedef unsigned int		WAITER_T;
+
+/*
+ * The administrative lock file resides in /tmp
+ * It does not survive a reboot
+ * It consists of fixed length records
+ * Each record has the following information:
+ * 	record number - record position within the lock file
+ * 	lock count - number of lock holders maintaining this lock
+ * 	lock object - object being locked
+ * 	lock key - key needed to manipulate existing lock
+ *	lock exclusive - is the lock exclusive (single locker only)
+ */
+
+#define	LOCK_OBJECT_MAXLEN	512-1
+#define	LOCK_KEY_MAXLEN		37
+
+#define	LOCK_DIRECTORY		"/tmp"
+
+/*
+ * this is the "well known name" of the lock file that is used by the
+ * package, patch, and zone administration commands to synchronize their
+ * various efforts - it must live in a temporary directory that is cleared
+ * on system reboot but it is NOT a temporary file in that it survives
+ * the process that creates and updates it - if the format of the lock
+ * file ever changes, this path should be updated with a later "uuid"
+ * so that previous (incompatible) pkgadm's will not use the later data.
+ */
+
+#define	LOCK_FILENAME	\
+	"/tmp/.ai.pkg.zone.lock-afdb66cf-1dd1-11b2-a049-000d560ddc3e"
+
+/* mode to use for LOCK_FILENAME */
+
+#define	LOCK_FILEMODE	\
+	(S_ISGID|S_IRUSR|S_IWUSR|S_IRGRP|S_IWGRP|S_IROTH|S_IWOTH)
+
+#define	LOCK_SLEEP_INTERVAL	SECONDS(2)
+
+/* lock contents types */
+
+typedef unsigned long RECORDNUM_T;
+
+#define	RECORDNUM_NONE	0xFFFFFFFF
+
+/* actual lock data */
+
+struct _adminLock
+{
+	RECORDNUM_T	lockRecordNum;
+	unsigned long	lockCount;
+	unsigned long	lockExclusive;
+	pid_t		lockPid;
+	zoneid_t	lockZoneId;
+	char		lockKey[LOCK_KEY_MAXLEN+1];
+	char		lockObject[LOCK_OBJECT_MAXLEN+1];
+};
+
+typedef struct _adminLock ADMINLOCK_T;
+
+/* size of an individual "lock" */
+
+#define	LOCK_SIZE		sizeof (ADMINLOCK_T)
+
+/* union to allow lock to be accessed as raw or structured data */
+
+union _lockRecord
+{
+	char		_lrLockData[LOCK_SIZE];
+	ADMINLOCK_T	_lrLock;
+};
+
+typedef union _lockRecord LOCK_T;
+
+/* return codes from "_findLock" */
+
+typedef unsigned long FINDLOCK_T;
+
+#define	FINDLOCK_FOUND		((FINDLOCK_T)0)
+#define	FINDLOCK_ERROR		((FINDLOCK_T)-1)
+#define	FINDLOCK_NOTFOUND	((FINDLOCK_T)-2)
+#define	FINDLOCK_KEYMISMATCH	((FINDLOCK_T)-3)
+#define	FINDLOCK_LOCKED		((FINDLOCK_T)-4)
+#define	FINDLOCK_NOTLOCKED	((FINDLOCK_T)-5)
+#define	FINDLOCK_LOCKACQUIRED	((FINDLOCK_T)-6)
+
+/*
+ * Forward declarations
+ */
+
+/* local main function implementation methods */
+
+static FINDLOCK_T	lock_acquire(LOCK_T *a_lock, int *a_fd, char *a_root,
+				char *a_key, char *a_object, int a_quiet,
+				int a_wait, long a_timeout, int a_exclusive,
+				char *a_altRoot, pid_t a_pid, zoneid_t a_zid);
+static int		lock_release(int a_fd, char *a_key, char *a_object,
+				int a_quiet);
+static int		lock_status(int a_fd, char *a_key, char *a_object,
+				int a_quiet);
+
+/* local utility functions */
+
+static int		_lockMatch(char *a_s1Lock, char *a_s2Lock);
+static FINDLOCK_T	_findLock(LOCK_T *a_theLock, RECORDNUM_T *r_recordNum,
+				int a_fd, char *a_object, char *a_key);
+static int		_decrementLockCount(int a_fd, LOCK_T *a_theLock);
+static int		_addLock(char *r_key, int a_fd, char *a_object,
+				int a_exclusive, pid_t a_pid, zoneid_t a_zid);
+static int		_incrementLockCount(int a_fd, LOCK_T *a_theLock);
+static FINDLOCK_T	_lock_acquire(LOCK_T *a_lock, int a_fd, char *a_key,
+				char *a_object, int a_quiet, int a_exclusive,
+				pid_t a_pid, zoneid_t a_zid);
+static char		*_getUniqueId(void);
+static int		_openLockFile(char *a_root);
+static void		sighup_handler(int a_signo);
+static void		sigint_handler(int a_signo);
+static boolean_t	_validateLock(int a_fd, LOCK_T *a_theLock, int a_quiet);
+
+static int		signal_received = 0;
+
+/*
+ * main methods with external entry points
+ */
+
+/*
+ * Name:	admin_lock
+ * Synopsis:	main entry point for pkgadm "lock" subcommand
+ * Description:	Control zone/package administrative locking
+ * Returns: 0 on success, non-zero otherwise.
+ */
+
+int
+admin_lock(int argc, char **argv)
+{
+	FINDLOCK_T		tResult;
+	LOCK_T			theLock;
+	char			*RFlag = "/";	/* altRoot */
+	char			*endptr;
+	char			*kFlag = "";	/* key */
+	char			*oFlag = "";	/* object */
+	char			*p;
+	char			c;
+	int			aFlag = 0;	/* acquire lock */
+	int			eFlag = 0;	/* exclusive lock */
+	int			exclusive = 1;	/* exclusive vs shared lock */
+	int			fd;
+	int			qFlag = 0;	/* quiet */
+	int			rFlag = 0;	/* release lock */
+	int			result;
+	int			sFlag = 0;	/* shared lock */
+	int			tFlag = 0;	/* test comparison */
+	int			wFlag = 0;	/* wait */
+	long			WFlag = 0;	/* wait timeout */
+	pid_t			pFlag = 0;	/* process # */
+	struct sigaction	nact;
+	struct sigaction	oact;
+	void			(*funcSighup)();
+	void			(*funcSigint)();
+	zoneid_t		zFlag = -1;	/* zone i.d. */
+
+	while ((c = getopt(argc, argv, ":aek:o:p:qrR:stwW:z:")) != EOF) {
+		switch (c) {
+		case 'a':	/* acquire lock */
+			aFlag++;
+			break;
+
+		case 'e':	/* exclusive lock */
+			eFlag++;
+			break;
+
+		case 'k':	/* lock-key */
+			kFlag = optarg;
+			if (strlen(optarg) > LOCK_KEY_MAXLEN) {
+				log_msg(LOG_MSG_ERR,
+					MSG_LOCK_kARG_TOOLONG,
+					strlen(optarg), LOCK_KEY_MAXLEN);
+				return (1);
+			}
+			break;
+
+		case 'o':	/* object */
+			oFlag = optarg;
+			if (strlen(optarg) > LOCK_OBJECT_MAXLEN) {
+				log_msg(LOG_MSG_ERR,
+					MSG_LOCK_oARG_TOOLONG,
+					strlen(optarg), LOCK_OBJECT_MAXLEN);
+				return (1);
+			}
+			break;
+
+		case 'p':	/* process i.d. */
+			errno = 0;
+			endptr = 0;
+			pFlag = strtol(optarg, &endptr, 10);
+			if ((endptr != (char *)NULL) && (*endptr != '\0')) {
+				log_msg(LOG_MSG_ERR, MSG_LOCK_pFLAG_BADINT,
+							optarg, *endptr);
+				return (1);
+			}
+			if ((pFlag == 0) && (errno != 0)) {
+				log_msg(LOG_MSG_ERR,
+					MSG_LOCK_pFLAG_ERROR,
+					optarg,  strerror(errno));
+				return (1);
+			}
+			break;
+
+		case 'q':	/* quiet */
+			qFlag++;
+			break;
+
+		case 'r':	/* release lock */
+			rFlag++;
+			break;
+
+		case 'R':	/* alternative root */
+			/* if root directory is not absolute path, error */
+			if (*optarg != '/') {
+				log_msg(LOG_MSG_ERR,
+					MSG_LOCK_RARG_NOT_ABSOLUTE, optarg);
+				return (1);
+			}
+
+			/* if root directory does not exist, create it */
+			if (access(optarg, F_OK) != 0) {
+
+				/* create top level root directory */
+				if (mkdirp(optarg, 0755) != 0) {
+					log_msg(LOG_MSG_ERR,
+						MSG_LOCK_ALTROOT_CANTCREATE,
+						optarg, strerror(errno));
+					return (1);
+				}
+			}
+
+			/* if $ALTROOT/tmp directory does not exist create it */
+			p = pkgstrPrintf("%s/tmp", optarg);
+			if (access(p, F_OK) != 0) {
+
+				/* create $ALTROOT/tmp directory */
+				if (mkdirp(p, 0777) != 0) {
+					log_msg(LOG_MSG_ERR,
+						MSG_LOCK_ALTROOT_CANTCREATE,
+						p, strerror(errno));
+					return (1);
+				}
+			}
+
+			/* if $ALTROOT/tmp directory cannot be created, exit */
+			if (access(p, F_OK) != 0) {
+				log_msg(LOG_MSG_ERR, MSG_LOCK_ALTROOT_NONEXIST,
+					optarg, strerror(errno));
+				return (1);
+			}
+
+			(void) free(p);
+
+			RFlag = optarg;
+			break;
+
+		case 's':	/* shared */
+			sFlag++;
+			break;
+
+		case 't':	/* test comparison */
+			tFlag++;
+			break;
+
+		case 'w':	/* wait */
+			wFlag++;
+			break;
+
+		case 'W':	/* wait with timeout */
+			errno = 0;
+			endptr = 0;
+			WFlag = strtol(optarg, &endptr, 10);
+			if ((endptr != (char *)NULL) && (*endptr != '\0')) {
+				log_msg(LOG_MSG_ERR, MSG_LOCK_WFLAG_BADINT,
+							optarg, *endptr);
+				return (1);
+			}
+			if ((WFlag == 0) && (errno != 0)) {
+				log_msg(LOG_MSG_ERR,
+					MSG_LOCK_WFLAG_ERROR,
+					optarg,  strerror(errno));
+				return (1);
+			}
+			wFlag++;
+			break;
+
+		case 'z':	/* zone i.d. */
+			errno = 0;
+			endptr = 0;
+			zFlag = strtol(optarg, &endptr, 10);
+			if ((endptr != (char *)NULL) && (*endptr != '\0')) {
+				log_msg(LOG_MSG_ERR, MSG_LOCK_zFLAG_BADINT,
+							optarg, *endptr);
+				return (1);
+			}
+			if ((zFlag == 0) && (errno != 0)) {
+				log_msg(LOG_MSG_ERR,
+					MSG_LOCK_zFLAG_ERROR,
+					optarg,  strerror(errno));
+				return (1);
+			}
+			break;
+
+		case ':':
+			log_msg(LOG_MSG_ERR, MSG_MISSING_OPERAND, optopt);
+			/* LINTED fallthrough on case statement */
+		case '?':
+
+		default:
+			log_msg(LOG_MSG_ERR, MSG_USAGE);
+			return (1);
+		}
+	}
+
+	/*
+	 * validate arguments
+	 */
+
+	/* if -t option is specified, override all other options */
+
+	if (tFlag) {
+		int	rs;
+		int	rx;
+		int	a;
+
+		/* only 2 or 3 args are valid */
+
+		a = argc-optind;
+		if ((a < 2) || (a > 3)) {
+			(void) fprintf(stderr, MSG_T_OPTION_ARGS, argc-optind);
+			return (1);
+		}
+
+		/* if 3rd argument given, it is return value to check */
+
+		if (a == 3) {
+			rs = atoi(argv[optind+2]);
+		}
+		rx = _lockMatch(argv[optind+0], argv[optind+1]);
+
+		/* if 3rd argument not given, code to check is code returned */
+
+		if (a == 2) {
+			rs = rx;
+		}
+
+		/* report results */
+
+		if (a == 2) {
+			(void) fprintf(stderr, MSG_T_RESULT_TWO,
+				rx, argv[optind+0], argv[optind+1]);
+			return (rx);
+		}
+
+		if (rx != rs) {
+			(void) fprintf(stderr, MSG_T_RESULT_THREE,
+				rs, rx, argv[optind+0], argv[optind+1]);
+		}
+
+		/* always successful */
+
+		return (rx == rs ? 0 : 1);
+	}
+
+	/* must be no non-option arguments left */
+
+	if ((argc-optind) > 0) {
+		log_msg(LOG_MSG_ERR, MSG_USAGE);
+		return (1);
+	}
+
+	/* -a and -r cannot be used together */
+
+	if (aFlag && rFlag) {
+		log_msg(LOG_MSG_ERR, MSG_LOCK_ar_TOGETHER);
+		return (1);
+	}
+
+	/* -e and -s cannot be used together */
+
+	if (eFlag && sFlag) {
+		log_msg(LOG_MSG_ERR, MSG_LOCK_es_TOGETHER);
+		return (1);
+	}
+
+	/* -e can only be used if -a is used */
+
+	if (!aFlag && eFlag) {
+		log_msg(LOG_MSG_ERR, MSG_LOCK_e_without_a);
+		return (1);
+	}
+
+	/* -s can only be used if -a is used */
+
+	if (!aFlag && sFlag) {
+		log_msg(LOG_MSG_ERR, MSG_LOCK_s_without_a);
+		return (1);
+	}
+
+	/*
+	 * perform the requested operation
+	 */
+
+	/*
+	 * hook SIGINT and SIGHUP interrupts into quit.c's trap handler
+	 */
+
+	/* hold SIGINT/SIGHUP interrupts */
+
+	(void) sighold(SIGHUP);
+	(void) sighold(SIGINT);
+
+	/* connect sigint_handler() to SIGINT */
+
+	nact.sa_handler = sigint_handler;
+	nact.sa_flags = SA_RESTART;
+	(void) sigemptyset(&nact.sa_mask);
+
+	if (sigaction(SIGINT, &nact, &oact) < 0) {
+		funcSigint = SIG_DFL;
+	} else {
+		funcSigint = oact.sa_handler;
+	}
+
+	/* connect sighupt_handler() to SIGHUP */
+
+	nact.sa_handler = sighup_handler;
+	nact.sa_flags = SA_RESTART;
+	(void) sigemptyset(&nact.sa_mask);
+
+	if (sigaction(SIGHUP, &nact, &oact) < 0) {
+		funcSighup = SIG_DFL;
+	} else {
+		funcSighup = oact.sa_handler;
+	}
+
+	/* release hold on signals */
+
+	(void) sigrelse(SIGHUP);
+	(void) sigrelse(SIGINT);
+
+	/* open the lock file */
+
+	fd = _openLockFile(RFlag);
+	if (fd < 0) {
+		return (1);
+	}
+
+	if (aFlag) {
+		/* set "exclusive" mode based on -e/-s flag used */
+
+		if (sFlag) {
+			exclusive = 0;
+		} else if (eFlag) {
+			exclusive = 1;
+		}
+
+		/* acquire lock */
+
+		tResult = lock_acquire(&theLock, &fd, RFlag, kFlag, oFlag,
+			qFlag, wFlag, WFlag, exclusive, RFlag, pFlag, zFlag);
+
+		switch (tResult) {
+		case FINDLOCK_LOCKACQUIRED:
+			(void) fprintf(stdout, "%s\n",
+				theLock._lrLock.lockKey);
+			result = 0;
+			break;
+		case FINDLOCK_LOCKED:
+			(void) fprintf(stdout, "%s\n",
+				theLock._lrLock.lockObject);
+			result = 1;
+			break;
+		default:
+			result = 1;
+			break;
+		}
+
+	} else if (rFlag) {
+		/* release lock */
+		result = lock_release(fd, kFlag, oFlag, qFlag);
+	} else {
+		/* lock status */
+		result = lock_status(fd, kFlag, oFlag, qFlag);
+	}
+
+	/* close the lock file */
+
+	(void) close(fd);
+
+	/* return results of operation */
+
+	return (result);
+}
+
+/*
+ * local main function implementation methods
+ */
+
+/*
+ * Name:	lock_acquire
+ * Description:	implement lock acquisition implementing the wait/timeouts
+ *		Calls _lock_acquire to attempt lock acquisition.
+ * Arguments:
+ *	a_theLock - lock object filled with contents of existing lock
+ *	a_fd - file descriptor opened on the lock file
+ *	a_root - root of file system to manipulate locks on
+ *	a_key - key associated with lock to acquire
+ *	a_object - object associated with lock to acquire
+ *	a_wait - wait if lock cannot be acquired flag:
+ *			== 0 - do not wait
+ *			!= 0 - wait
+ *	a_timeout - timeout if waiting to acquire busy lock:
+ *			== 0 - no timeout (wait forever)
+ *			!= 0 - max # seconds to wait to acquire busy lock
+ *	a_quiet - quiet mode enabled flag
+ *	a_exclusive - exclusive/shared lock flag
+ *	a_pid - if != 0 process i.d. to associate with this lock
+ *	a_zid - if >= 0 - zone i.d. to associate with this lock
+ * Returns: int
+ *		== 0 - successful
+ *		!= 0 - not successful
+ */
+
+static FINDLOCK_T
+lock_acquire(LOCK_T *a_theLock, int *a_fd, char *a_root, char *a_key,
+	char *a_object, int a_quiet, int a_wait, long a_timeout,
+	int a_exclusive, char *a_altRoot, pid_t a_pid, zoneid_t a_zid)
+{
+	int		notified = 0;
+	FINDLOCK_T	result;
+	time_t		timeout;
+	int		closeOnExit = 0;
+
+	/* reset the lock */
+
+	bzero(a_theLock, sizeof (LOCK_T));
+
+	/* open file if not open */
+
+	if ((*a_fd) < 0) {
+		(*a_fd) = _openLockFile(a_altRoot);
+		if ((*a_fd) < 0) {
+			return (FINDLOCK_ERROR);
+		}
+		closeOnExit++;
+	}
+
+	/* compute time after which acquire times out */
+
+	timeout = time((time_t *)NULL) + a_timeout;
+
+	for (;;) {
+		time_t	curtime;
+
+		/* attempt to aquire the lock */
+
+		result = _lock_acquire(a_theLock, *a_fd, a_key, a_object,
+				a_quiet, a_exclusive, a_pid, a_zid);
+
+		/* return result if any result other than object is locked */
+
+		switch (result) {
+		case FINDLOCK_LOCKACQUIRED:
+
+			/* close lock file if opened in this function */
+
+			if (closeOnExit) {
+				(void) close(*a_fd);
+				*a_fd = -1;
+			}
+
+			return (FINDLOCK_LOCKACQUIRED);
+
+		case FINDLOCK_FOUND:
+		case FINDLOCK_NOTFOUND:
+		case FINDLOCK_KEYMISMATCH:
+		case FINDLOCK_NOTLOCKED:
+		case FINDLOCK_ERROR:
+		default:
+			/* close lock file if opened in this function */
+
+			if (closeOnExit) {
+				(void) close(*a_fd);
+				*a_fd = -1;
+			}
+
+			return (result);
+
+		case FINDLOCK_LOCKED:
+			;
+			/* FALLTHROUGH */
+		}
+
+		/*
+		 * object locked OR SIGINT/SIGHUP interrupt received;
+		 * return error if not waiting for lock OR signal received
+		 */
+
+		if ((a_wait == 0) || (signal_received != 0)) {
+			log_msg(a_quiet ? LOG_MSG_DEBUG : LOG_MSG_ERR,
+				MSG_LOCK_ACQUIRE_BUSY_FIRST,
+				a_exclusive ? MSG_LOCK_EXC : MSG_LOCK_SHR,
+				a_object, a_key,
+				a_theLock->_lrLock.lockObject,
+				a_theLock->_lrLock.lockExclusive ?
+						MSG_LOCK_EXC : MSG_LOCK_SHR,
+				a_theLock->_lrLock.lockExclusive !=
+					a_exclusive ? "" :
+					MSG_LOCK_ACQUIRE_BUSY_ADDITIONAL);
+
+			/* close lock file if opened in this function */
+
+			if (closeOnExit) {
+				(void) close(*a_fd);
+				*a_fd = -1;
+			}
+
+			return (FINDLOCK_LOCKED);
+		}
+
+		/* waiting for lock - if timeout specified see if time left */
+
+		if (a_timeout > 0) {
+			curtime = time((time_t *)NULL);
+			if (curtime > timeout) {
+				log_msg(a_quiet ? LOG_MSG_DEBUG : LOG_MSG_ERR,
+					MSG_LOCK_ACQUIRE_TIMEDOUT,
+					a_exclusive ?
+						MSG_LOCK_EXC : MSG_LOCK_SHR,
+					a_object, a_key);
+
+				/* close lock file if opened in this function */
+
+				if (closeOnExit) {
+					(void) close(*a_fd);
+					*a_fd = -1;
+				}
+
+				return (FINDLOCK_ERROR);
+			}
+		}
+
+		/*
+		 * waiting to aquire lock:
+		 * - notify waiting (one time only)
+		 * - close lock file
+		 * - sleep
+		 * - open lock file
+		 * - try again
+		 */
+
+		/* notify once */
+
+		if (notified++ == 0) {
+			log_msg(a_quiet ? LOG_MSG_DEBUG : LOG_MSG_WRN,
+				MSG_LOCK_ACQUIRE_WAITING,
+				a_object);
+		}
+
+		/* close lock file */
+
+		(void) close(*a_fd);
+
+		/* wait (sleep) */
+
+		(void) sleep(LOCK_SLEEP_INTERVAL);
+
+		/* open the lock file and try again */
+
+		*a_fd = _openLockFile(a_root);
+		if (*a_fd < 0) {
+			log_msg(LOG_MSG_ERR, MSG_LOCK_ACQUIRE_REOPEN_FAILED,
+				a_object);
+
+			/* close lock file if opened in this function */
+
+			if (closeOnExit) {
+				(void) close(*a_fd);
+				*a_fd = -1;
+			}
+
+			return (FINDLOCK_ERROR);
+		}
+	}
+}
+
+/*
+ * Name:	lock_release
+ * Description:	implement lock release
+ * Arguments:
+ *	a_fd - file descriptor opened on the lock file
+ *	a_key - key associated with lock to release
+ *	a_object - object associated with lock to release
+ *	a_quiet - quiet mode enabled flag
+ * Returns: int
+ *		== 0 - successful
+ *		!= 0 - not successful
+ */
+
+static int
+lock_release(int a_fd, char *a_key, char *a_object, int a_quiet)
+{
+	RECORDNUM_T	recordNum;
+	LOCK_T		theLock;
+	FINDLOCK_T	result;
+
+	/* entry debugging info */
+
+	log_msg(LOG_MSG_DEBUG, MSG_LOCK_RELEASE_ENTRY,
+		a_key, a_object, a_quiet);
+
+	/* find the lock to be released */
+
+	result = _findLock(&theLock, &recordNum, a_fd, a_object, a_key);
+
+	log_msg(LOG_MSG_DEBUG, MSG_LOCK_RELEASE_FINDRESULT,
+		result, recordNum);
+
+	/* determine how to release the lock if found */
+
+	switch (result) {
+		/*
+		 * object is not locked but a key was specified
+		 */
+		case FINDLOCK_NOTLOCKED:
+			log_msg(a_quiet ? LOG_MSG_DEBUG : LOG_MSG_ERR,
+				MSG_LOCK_RELEASE_NOTLOCKED,
+				a_object, a_key);
+			return (result);
+
+		/*
+		 * object is locked and no matching key was specified
+		 */
+		case FINDLOCK_LOCKED:
+			log_msg(a_quiet ? LOG_MSG_DEBUG : LOG_MSG_ERR,
+				MSG_LOCK_RELEASE_LOCKED,
+				a_object, a_key);
+			return (result);
+
+		/*
+		 * object is not locked
+		 */
+		case FINDLOCK_NOTFOUND:
+			log_msg(a_quiet ? LOG_MSG_DEBUG : LOG_MSG_ERR,
+				MSG_LOCK_RELEASE_NOTFOUND,
+				a_object, a_key);
+			return (result);
+
+		/*
+		 * object is locked and specified key does not match
+		 */
+		case FINDLOCK_KEYMISMATCH:
+			log_msg(a_quiet ? LOG_MSG_DEBUG : LOG_MSG_ERR,
+				MSG_LOCK_RELEASE_KEYMISMATCH,
+				a_object);
+			return (result);
+
+		/*
+		 * error determining if object is locked
+		 */
+		case FINDLOCK_ERROR:
+			log_msg(a_quiet ? LOG_MSG_DEBUG : LOG_MSG_ERR,
+				MSG_LOCK_RELEASE_ERROR,
+				a_object, a_key);
+			perror(LOCK_FILENAME);
+			return (result);
+
+		/*
+		 * object is locked and specified key matches
+		 */
+		case FINDLOCK_FOUND:
+			log_msg(LOG_MSG_DEBUG, MSG_LOCK_RELEASE_FOUND,
+				a_object, a_key);
+			(void) _decrementLockCount(a_fd, &theLock);
+			break;
+
+		/*
+		 * unknown return
+		 */
+		default:
+			result = FINDLOCK_ERROR;
+			break;
+
+	}
+	return (result);
+}
+
+/*
+ * Name:	lock_status
+ * Description:	implement lock status display/inquiry
+ * Arguments:
+ *	a_fd - file descriptor opened on the lock file
+ *	a_key - key associated with lock to look up
+ *	a_object - object associated with lock to look up
+ *	a_quiet - quiet mode enabled flag
+ * Returns: int
+ *		== 0 - successful
+ *		!= 0 - not successful
+ */
+
+static int
+lock_status(int a_fd, char *a_key, char *a_object, int a_quiet)
+{
+	ADMINLOCK_T	*pll;
+	LOCK_T		theLock;
+	RECORDNUM_T	recordNum = 0;
+	char		*pld;
+	int		found = 0;
+	long		pls;
+
+	/* entry debugging info */
+
+	log_msg(LOG_MSG_DEBUG, MSG_LOCK_STATUS_ENTRY,
+		a_key, a_object);
+
+	/* localize references to lock object */
+
+	pld = &theLock._lrLockData[0];
+	pll = &theLock._lrLock;
+	pls = sizeof (theLock._lrLockData);
+
+	bzero(pld, pls);
+
+	/* read and process each lock */
+
+	for (; pread(a_fd, pld, pls, pls*recordNum) == pls; recordNum++) {
+		/* debug info on this lock */
+
+		log_msg(LOG_MSG_DEBUG, MSG_LOCK_STATUS_READRECORD,
+			recordNum, pll->lockCount,
+			pll->lockObject, pll->lockKey, pll->lockPid,
+			pll->lockZoneId);
+
+		/* ignore if key specified and key does not match */
+
+		if ((*a_key != '\0') &&
+			(strcmp(pll->lockKey, a_key) != 0)) {
+			continue;
+		}
+
+		/* ignore if object specified and object does not match */
+
+		if ((*a_object != '\0') &&
+			(strcmp(pll->lockObject, a_object) != 0)) {
+			continue;
+		}
+
+		found++;
+
+		/* process next lock if quiet operation */
+
+		if (a_quiet != 0) {
+			continue;
+		}
+
+		/* output header if first lock object */
+
+		if (found == 1) {
+			(void) fprintf(stdout,
+				"%2s %2s %3s %8s %3s %9s %37s %s\n",
+				"i#", "l#", "cnt", "pid", "zid", "lock-type",
+				"---------------lock-key-------------",
+				"lock-object");
+		}
+
+		/* output status line for this lock object */
+
+		(void) fprintf(stdout,
+			"%2ld %2ld %3ld %8ld %3d %9s %37s %s\n",
+			recordNum, pll->lockRecordNum, pll->lockCount,
+			pll->lockPid, pll->lockZoneId,
+			pll->lockExclusive ? MSG_LOCK_EXC : MSG_LOCK_SHR,
+			pll->lockKey,
+			*pll->lockObject == '\0' ? "*" : pll->lockObject);
+	}
+
+	/* return == 0 if found, != 0 if not found */
+
+	return (found == 0 ? 1 : 0);
+}
+
+/*
+ * local utility functions
+ */
+
+/*
+ * Name:	_lock_acquire
+ * Description:	implement lock acquisition without wait/timeouts
+ * Arguments:
+ *	a_theLock - lock object filled with contents of existing lock
+ *	a_fd - file descriptor opened on the lock file
+ *	a_key - key associated with lock to acquire
+ *	a_object - object associated with lock to acquire
+ *	a_quiet - quiet mode enabled flag
+ *	a_exclusive - exclusive/shared lock flag
+ *	a_pid - if != 0 process i.d. to associate with this lock
+ *	a_zid - if >= 0 zone i.d. to associate with this lock
+ * Returns: FINDLOCK_T
+ */
+
+static FINDLOCK_T
+_lock_acquire(LOCK_T *a_theLock, int a_fd, char *a_key,
+	char *a_object, int a_quiet, int a_exclusive, pid_t a_pid,
+	zoneid_t a_zid)
+{
+	RECORDNUM_T	recordNum;
+	FINDLOCK_T	result;
+	char		key[LOCK_KEY_MAXLEN+1] = {'\0'};
+
+	/* entry debugging info */
+
+	log_msg(LOG_MSG_DEBUG, MSG_LOCK_ACQUIRE_ENTRY,
+		a_key, a_object, a_quiet, a_exclusive);
+
+	/* is the specified object already locked? */
+
+	for (;;) {
+		result = _findLock(a_theLock, &recordNum, a_fd, a_object,
+			a_key);
+
+		if (result != FINDLOCK_LOCKED) {
+			break;
+		}
+
+		if (_validateLock(a_fd, a_theLock, a_quiet) == B_TRUE) {
+			break;
+		}
+	}
+
+
+	/* debug info on result of find of lock */
+
+	log_msg(LOG_MSG_DEBUG, MSG_LOCK_ACQUIRE_FINDRESULT,
+		a_exclusive ? MSG_LOCK_EXC : MSG_LOCK_SHR,
+		result, recordNum);
+
+	/* determine how to acquire the lock */
+
+	switch (result) {
+		/*
+		 * object is not locked but a key was specified
+		 */
+		case FINDLOCK_NOTLOCKED:
+			log_msg(a_quiet ? LOG_MSG_DEBUG : LOG_MSG_ERR,
+				MSG_LOCK_ACQUIRE_NOTLOCKED,
+				a_exclusive ? MSG_LOCK_EXC : MSG_LOCK_SHR,
+				a_object, a_key);
+			break;
+
+		/*
+		 * object is locked and no key was specified:
+		 * - if lock is exclusively held, return "locked"
+		 * - if exclusive lock requested, return "locked"
+		 * - otherwise lock is shared and shared lock requested,
+		 *   - increment lock count and return the key
+		 */
+		case FINDLOCK_LOCKED:
+			/* return error if current lock exclusive */
+
+			if (a_theLock->_lrLock.lockExclusive) {
+				break;
+			}
+
+			/* return error if requesting exclusive lock */
+
+			if (a_exclusive) {
+				break;
+			}
+
+			/* shared requesting shared - add to shared lock */
+
+			log_msg(LOG_MSG_DEBUG,
+				MSG_LOCK_ACQUIRE_LOCKED_SHARED,
+				a_object, a_key);
+
+			/* increment shared lock count */
+
+			if (_incrementLockCount(a_fd, a_theLock) == 0) {
+				result = FINDLOCK_LOCKACQUIRED;
+			} else {
+				result = FINDLOCK_ERROR;
+			}
+
+			break;
+
+		/*
+		 * object is not locked
+		 */
+		case FINDLOCK_NOTFOUND:
+			log_msg(LOG_MSG_DEBUG,
+				MSG_LOCK_ACQUIRE_NOTFOUND,
+				a_exclusive ? MSG_LOCK_EXC : MSG_LOCK_SHR,
+				a_object);
+
+			if (_addLock(key, a_fd, a_object, a_exclusive,
+							a_pid, a_zid) == 0) {
+				(void) strncpy(a_theLock->_lrLock.lockKey, key,
+					sizeof (a_theLock->_lrLock.lockKey));
+				result = FINDLOCK_LOCKACQUIRED;
+			} else {
+				result = FINDLOCK_ERROR;
+			}
+			break;
+
+		/*
+		 * object is locked, key specified, specified key does not match
+		 */
+		case FINDLOCK_KEYMISMATCH:
+			log_msg(a_quiet ? LOG_MSG_DEBUG : LOG_MSG_ERR,
+				MSG_LOCK_ACQUIRE_KEYMISMATCH,
+				a_exclusive ? MSG_LOCK_EXC : MSG_LOCK_SHR,
+				a_object);
+			break;
+
+		/*
+		 * error determining if object is locked
+		 */
+		case FINDLOCK_ERROR:
+			log_msg(LOG_MSG_ERR, MSG_LOCK_ACQUIRE_ERROR,
+				a_object, a_key, strerror(errno));
+			break;
+
+		/*
+		 * object is locked and specified key matches
+		 */
+		case FINDLOCK_FOUND:
+			/* return locked if object currently locked */
+			if (a_exclusive != a_theLock->_lrLock.lockExclusive) {
+				result = FINDLOCK_LOCKED;
+				break;
+			}
+
+			log_msg(LOG_MSG_DEBUG, MSG_LOCK_ACQUIRE_FOUND_INC,
+				a_object, a_key,
+				a_exclusive ? MSG_LOCK_EXC : MSG_LOCK_SHR);
+
+			/* increment shared lock */
+
+			if (_incrementLockCount(a_fd, a_theLock) == 0) {
+				result = FINDLOCK_LOCKACQUIRED;
+			} else {
+				result = FINDLOCK_ERROR;
+			}
+			break;
+
+		/*
+		 * unknown return
+		 */
+		default:
+			result = FINDLOCK_ERROR;
+			break;
+	}
+
+	return (result);
+}
+
+/*
+ * Name:	_openLockFile
+ * Description:	open the lock file, acquiring exclusive record locks
+ * Arguments:
+ *	a_root - root of file system to manipulate locks on
+ * Returns: int
+ *		>= 0 - successful - file descriptor lock file opened on
+ *		< 0 - not successful
+ */
+
+static int
+_openLockFile(char *a_root)
+{
+	WAITER_T	waiter;
+	char		lockpath[MAXPATHLEN];
+	int		fd;
+	int		result;
+
+	/* entry debugging info */
+
+	log_msg(LOG_MSG_DEBUG, MSG_LOCK_OPENFILE_ENTRY,
+		a_root, LOCK_FILENAME);
+
+	/* generate path to lock directory */
+
+	(void) snprintf(lockpath, sizeof (lockpath), "%s/%s",
+		a_root, LOCK_DIRECTORY);
+
+	if (access(lockpath, F_OK) != 0) {
+		log_msg(LOG_MSG_ERR, MSG_LOCK_ROOTDIR_INVALID,
+			lockpath, strerror(errno));
+		return (-1);
+	}
+
+	/* generate path to lock file */
+
+	(void) snprintf(lockpath, sizeof (lockpath),
+		"%s/%s", a_root, LOCK_FILENAME);
+
+	/* wait for open to succeed up to limits */
+
+	for (waiter = WAITER_INITIAL;
+		waiter < WAITER_MAX;
+		waiter = WAITER_NEXT(waiter)) {
+
+		/* LINTED O_CREAT without O_EXCL specified in call to open() */
+		fd = open(lockpath, O_CREAT|O_RDWR, LOCK_FILEMODE);
+
+		/* break out of loop if file opened */
+
+		if (fd >= 0) {
+			break;
+		}
+
+		/* failed - exit loop if due to access (permissions) failure */
+
+		if (errno == EACCES) {
+			break;
+		}
+
+		/* file is busy - wait and try again */
+
+		if (waiter == WAITER_INITIAL) {
+			log_msg(LOG_MSG_DEBUG,
+				MSG_LOCK_OPENFILE_SLEEPING,
+				strerror(errno), waiter);
+		}
+
+		(void) sleep(waiter);
+	}
+
+	/* if open filed generate error message and return error */
+
+	if (fd < 0) {
+		log_msg(LOG_MSG_DEBUG, MSG_LOCK_OPENFILE_FAILURE,
+			strerror(errno));
+		perror(lockpath);
+		return (-1);
+	}
+
+	/*
+	 * lock file opened - acquire exclusive section lock on entire file;
+	 * wait for lockf to succeed up to limits
+	 */
+
+	for (waiter = WAITER_INITIAL;
+		waiter < WAITER_MAX;
+		waiter = WAITER_NEXT(waiter)) {
+
+		/* acquire exclusive section lock on entire file */
+
+		result = lockf(fd, F_LOCK, 0xFFFFF);
+
+		/* break out of loop if entire file locked */
+
+		if (result == 0) {
+			break;
+		}
+
+		/* file is busy - wait and try again */
+
+		if (waiter == WAITER_INITIAL) {
+			log_msg(LOG_MSG_DEBUG, MSG_LOCK_OPENFILE_SLEEP2,
+				strerror(errno), waiter);
+		}
+
+		(void) sleep(waiter);
+	}
+
+	/* if section lock failed generate error message and return error */
+
+	if (result < 0) {
+		log_msg(LOG_MSG_DEBUG, MSG_LOCK_OPENFILE_FAIL2,
+			strerror(errno));
+		perror(lockpath);
+		(void) close(fd);
+		return (-1);
+	}
+
+	/* file opened and locked - return success */
+
+	log_msg(LOG_MSG_DEBUG, MSG_LOCK_OPENFILE_SUCCESS, fd);
+
+	return (fd);
+}
+
+/*
+ * Name:	_lockMatch
+ * Description:	Compare two lock objects using file name match criteria
+ * Arguments:
+ *	a_s1Lock - first lock object to compare against the second
+ *	a_s2Lock - second lock object to compare against the first
+ * Returns:
+ * 	== 0 - the locks match at some level
+ *	!= 0 - the locks do not match at any level
+ */
+
+static int
+_lockMatch(char *a_s1Lock, char *a_s2Lock)
+{
+	boolean_t	s1Sfx = B_FALSE;
+	boolean_t	s2Sfx = B_FALSE;
+	char		*final1Lock = (char *)NULL;
+	char		*final2Lock = (char *)NULL;
+	char		s1Buf[MAXPATHLEN] = {'\0'};
+	char		s1Prefix[MAXPATHLEN] = {'\0'};
+	char		s2Buf[MAXPATHLEN] = {'\0'};
+	char		s2Prefix[MAXPATHLEN] = {'\0'};
+	int		result = 0;
+	int		s1Cnt;
+	int		s2Cnt;
+
+	/* entry assertions */
+
+	assert(a_s1Lock != (char *)NULL);
+	assert(a_s2Lock != (char *)NULL);
+
+	/* entry debugging info */
+
+	log_msg(LOG_MSG_DEBUG, MSG_LCKMCH_ENTRY, a_s1Lock, a_s2Lock);
+
+	/*
+	 * attempt to find a common anchor between the two locks; that is,
+	 * find the first node in the first lock that matches any node
+	 * in the second lock; for example:
+	 * --> a/b/c vs b/c/d
+	 * -> common anchor is "b"; comparison would expand to:
+	 * --> a/b/c/? vs ?/b/c/d
+	 */
+
+	/* process each node in the first lock */
+
+	for (s1Cnt = 0; ; s1Cnt++) {
+		/* get next first lock node */
+
+		pkgstrGetToken_r((char *)NULL, a_s1Lock, s1Cnt, "/",
+			s1Buf, sizeof (s1Buf));
+
+		log_msg(LOG_MSG_DEBUG, MSG_LCKMCH_FSTNODE, s1Cnt, s1Buf);
+
+		/* exit if no more nodes left */
+
+		if (s1Buf[0] == '\0') {
+			break;
+		}
+
+		/* discover "." prefix for this node */
+
+		pkgstrGetToken_r((char *)NULL, s1Buf, 0, ".", s1Prefix,
+			sizeof (s1Prefix));
+
+		s1Sfx = (strlen(s1Prefix) == strlen(s1Buf) ? B_FALSE : B_TRUE);
+
+		/* search each second lock node; look for the first node lock */
+
+		for (s2Cnt = 0; ; s2Cnt++) {
+			/* get next second lock node */
+
+			pkgstrGetToken_r((char *)NULL, a_s2Lock, s2Cnt, "/",
+				s2Buf, sizeof (s2Buf));
+
+			log_msg(LOG_MSG_DEBUG, MSG_LCKMCH_SCNDNODE, s2Cnt,
+				s2Buf);
+
+			/* exit if no nodes left */
+
+			if (s2Buf[0] == '\0') {
+				break;
+			}
+
+			/* discover "." prefix for this node */
+
+			pkgstrGetToken_r((char *)NULL, s2Buf, 0, ".", s2Prefix,
+				sizeof (s2Prefix));
+
+			s2Sfx = (strlen(s2Prefix) ==
+					strlen(s2Buf) ? B_FALSE : B_TRUE);
+
+			/*
+			 * process this pair of nodes:
+			 * if both nodes do not have a prefix, then directly
+			 * compare the nodes (e.g. a/b vs c/d: a vs c, b vs d)
+			 * and break out of the loop if there is a match;
+			 * otherwise, compare prefixes and break out of the
+			 * loop if there is a match (e.g. a.* / b.* vs
+			 * vs c.* / d.*: a.* vs c.*, a.* vs d.*, b.* vs c.*,
+			 * b.* vs d.*).
+			 */
+
+			log_msg(LOG_MSG_DEBUG, MSG_LCKMCH_NODES, s1Buf,
+				s1Prefix, s1Sfx, s2Buf, s2Prefix, s2Sfx);
+
+			if ((s1Sfx == B_FALSE) || (s2Sfx == B_FALSE)) {
+				/* one doesnt have a prefix direct comparison */
+
+				if (strcmp(s1Buf, s2Buf) == 0) {
+					log_msg(LOG_MSG_DEBUG,
+						MSG_LCKMCH_DIRMCH,
+						s1Buf, s2Buf);
+					break;
+				}
+
+				/* nodes do not directly match, continue */
+
+				log_msg(LOG_MSG_DEBUG, MSG_LCKMCH_DIRNOMCH,
+						s1Buf, s2Buf);
+				continue;
+			}
+
+			/* both have prefix, compare prefixes */
+
+			if (strcmp(s1Prefix, s2Prefix) == 0) {
+				log_msg(LOG_MSG_DEBUG, MSG_LCKMCH_PFXMCH,
+					s1Prefix, s2Prefix);
+				break;
+			}
+
+			/* prefixes do not match, continue */
+
+			log_msg(LOG_MSG_DEBUG, MSG_LCKMCH_PFXNOMCH, s1Prefix,
+				s2Prefix);
+		}
+
+		/*
+		 * match found if not at the end of the second lock node list,
+		 * break out of loop because some match between the two lock
+		 * objects has been found
+		 */
+
+		if (s2Buf[0] != '\0') {
+			break;
+		}
+	}
+
+	/*
+	 * at this point, either a match has been found between the nodes in
+	 * the two lock objects, or there is no commonality at all between
+	 * the two lock objects.
+	 *
+	 * s1Buf[0] == '\0' && s2Buf[0] == '\0':
+	 * --> nothing in first lock matches anything in second lock:
+	 * ----> (s1Cnt == 1) || (s2Cnt == 1) && (s1Sfx == B_FALSE)
+	 * ----> || (s2Sfx == B_FALSE)
+	 * --------> an absolute lock do not match
+	 * ----> else both object locks have nothing in common - match
+	 *
+	 * s2Buf[0] != '\0' && s1Buf[0] != '\0' && s1Cnt > 0 && s2Cnt > 0
+	 * --> locks have incompatible overlaps - no match, such as:
+	 * ---->  a.* / b.* / c.* / d.*   and   y.* / b.* / c.*
+	 *
+	 * s1Cnt == 0 && s2Cnt == 0:
+	 * --> locks begin with same node - do comparison
+	 *
+	 * s1Cnt != 0 && s2Cnt == 0 && s2Buf[0] != '\0'
+	 * --> second lock is subset of first lock
+	 *
+	 * s2Cnt == 0 && s2Buf[0] != '\0':
+	 * --> s1Buf[s1Cnt] matches s2Buf[0] - second is subset of first
+	 *
+	 * s2Cnt != 0 && s1Cnt == 0 && s1Buf[0] != '\0':
+	 * --> first lock is subset of second lock
+	 *
+	 */
+
+	log_msg(LOG_MSG_DEBUG, MSG_LCKMCH_FSTLCK, s1Cnt, s1Buf,
+		s1Prefix, s1Sfx);
+	log_msg(LOG_MSG_DEBUG, MSG_LCKMCH_SCNDLCK, s2Cnt, s2Buf,
+		s2Prefix, s2Sfx);
+
+	/* process any direct comparisons that might be possible */
+
+	if ((s1Buf[0] == '\0') && (s2Buf[0] == '\0')) {
+		/* nothing in first matches anything in second lock */
+
+		if (((s1Cnt == 1) || (s2Cnt == 1)) &&
+			((s1Sfx == B_FALSE) || (s2Sfx == B_FALSE))) {
+			/* two absolute locks match (e.g. 'file' and 'dir') */
+			log_msg(LOG_MSG_DEBUG, MSG_LCKMCH_ABSNOMCH, a_s1Lock,
+				a_s2Lock);
+			return (1);
+		}
+
+		/* two object locks have nothing in common: match */
+
+		log_msg(LOG_MSG_DEBUG, MSG_LCKMCH_OBJMCH, a_s1Lock, a_s2Lock);
+
+		return (0);
+	}
+
+	if ((s2Buf[0] != '\0') && (s1Buf[0] != '\0') &&
+						(s1Cnt > 0) && (s2Cnt > 0)) {
+		/* incompatible overlapping objects */
+
+		log_msg(LOG_MSG_DEBUG, MSG_LCKMCH_OVLPNOMCH, a_s1Lock, a_s2Lock,
+			s1Cnt+1, s1Buf);
+
+		return (1);
+	}
+
+	/*
+	 * must compare each node of each lock to determine match;
+	 * start off at the first byte of both locks
+	 */
+
+	final1Lock = a_s1Lock;
+	final2Lock = a_s2Lock;
+
+	if ((s1Cnt == 0) && (s2Cnt == 0)) {
+		/* both have first match - start comparison from the begining */
+
+		log_msg(LOG_MSG_DEBUG, MSG_LCKMCH_SAME, a_s1Lock, a_s2Lock,
+			s1Buf);
+
+	} else if ((s1Cnt != 0) && (s2Cnt == 0) && (s2Buf[0] != '\0')) {
+		/* second lock begins somewhere inside of the first lock */
+
+		log_msg(LOG_MSG_DEBUG, MSG_LCKMCH_SCNDSUB, a_s2Lock, a_s1Lock,
+			s1Cnt+1, s1Buf);
+
+		/* advance first lock to matching node in second lock */
+
+		if (strchr(a_s1Lock, '/') != (char *)NULL) {
+			for (; s1Cnt > 0 && (*final1Lock != '\0');
+				final1Lock++) {
+				if (*final1Lock == '/') {
+					s1Cnt--;
+				}
+			}
+		}
+	} else if ((s2Cnt != 0) && (s1Cnt == 0) && (s1Buf[0] != '\0')) {
+		/* first lock begins somewhere inside of the second lock */
+
+		log_msg(LOG_MSG_DEBUG, MSG_LCKMCH_FRSTSUB, a_s1Lock, a_s2Lock,
+			s2Cnt+1, s2Buf);
+
+		/* advance second lock to matching node in first lock */
+
+		if (strchr(a_s2Lock, '/') != (char *)NULL) {
+			for (; s2Cnt > 0 && (*final2Lock != '\0');
+				final2Lock++) {
+				if (*final2Lock == '/') {
+					s2Cnt--;
+				}
+			}
+		}
+	} else {
+		/* unknown condition (probably impossible): directly compare */
+
+		log_msg(LOG_MSG_ERR, MSG_LCKMCH_DONTKNOW, a_s1Lock, a_s2Lock);
+	}
+
+	/*
+	 * locks have common node - compare from that node forward
+	 */
+
+	log_msg(LOG_MSG_DEBUG, MSG_LCKMCH_READY, final1Lock, final2Lock);
+
+	/* compare each node (prefix) - success when no more nodes to compare */
+
+	for (s1Cnt = 0; ; s1Cnt++) {
+		/* get next node from first lock */
+
+		pkgstrGetToken_r((char *)NULL, final1Lock, s1Cnt, "/", s1Buf,
+			sizeof (s1Buf));
+
+		/* success if at end of lock */
+
+		if (s1Buf[0] == '\0') {
+			break;
+		}
+
+		/* get next node from second lock */
+
+		pkgstrGetToken_r((char *)NULL, final2Lock, s1Cnt, "/", s2Buf,
+			sizeof (s2Buf));
+
+		/* success if at end of lock */
+
+		if (s2Buf[0] == '\0') {
+			break;
+		}
+
+		/* compare both nodes */
+
+		result = fnmatch(s1Buf, s2Buf, 0);
+		if (result != 0) {
+			result = fnmatch(s2Buf, s1Buf, 0);
+		}
+
+		/* failure if nodes do not match */
+
+		if (result != 0) {
+			log_msg(LOG_MSG_DEBUG, MSG_LCKMCH_NODEFAIL,
+				s1Cnt, s1Buf, s2Buf);
+			return (1);
+		}
+
+		/* nodes match, continue and compare next set of nodes */
+
+		log_msg(LOG_MSG_DEBUG, MSG_LCKMCH_NODEOK, s1Cnt, s1Buf, s2Buf);
+	}
+
+	/* no more nodes to compare - locks match */
+
+	log_msg(LOG_MSG_DEBUG, MSG_LCKMCH_MATCHOK, final1Lock, final2Lock);
+
+	return (0);
+}
+
+/*
+ * Name:	_findLock
+ * Description:	Locate specified lock in lock file
+ * Arguments:
+ *	a_theLock - lock object filled with contents of lock (if found)
+ *	r_recordNum - will contain record number if lock found
+ *		- will be RECORDNUM_NONE if lock not found
+ *	a_fd - file descriptor opened on the lock file
+ *	a_key - key associated with lock to look up
+ *	a_object - object associated with lock to look up
+ * Returns:
+ *	FINDLOCK_FOUND - specified lock found; a_theLock contains contents
+ *		of found lock, r_recordNum contain record number of lock
+ *	FINDLOCK_ERROR - failed - error occurred looking up the lock
+ *	FINDLOCK_NOTFOUND - specified object is not locked
+ *	FINDLOCK_KEYMISMATCH - object lock found but specified key doesnt match
+ *	FINDLOCK_LOCKED - object lock found but no key specified
+ *	FINDLOCK_NOTLOCKED - object not locked
+ */
+
+static FINDLOCK_T
+_findLock(LOCK_T *a_theLock, RECORDNUM_T *r_recordNum,
+	int a_fd, char *a_object, char *a_key)
+{
+	ADMINLOCK_T	*pll;
+	char		*pld;
+	int		recordNum = 0;
+	long		pls;
+	off_t		pos;
+
+	/* reset returned record number to "none" */
+
+	*r_recordNum = RECORDNUM_NONE;
+
+	/* localize references to lock object */
+
+	pld = &a_theLock->_lrLockData[0];
+	pll = &a_theLock->_lrLock;
+	pls = sizeof (a_theLock->_lrLockData);
+
+	/* zero out returned lock data */
+
+	bzero(pld, pls);
+
+	/* debug info before processing lock file */
+
+	log_msg(LOG_MSG_DEBUG, MSG_LOCK_FINDLOCK_ENTRY,
+		a_object, a_key);
+
+	/* rewind to beginning of lock file */
+
+	pos = lseek(a_fd, 0L, SEEK_SET);
+	if (pos == (off_t)-1) {
+		log_msg(LOG_MSG_ERR, MSG_LOCK_FINDLOCK_LSEEK_FAILURE,
+			a_object, a_key, strerror(errno));
+		return (FINDLOCK_ERROR);
+	}
+
+	/* read and process each lock */
+
+	for (; pread(a_fd, pld, pls, pls*recordNum) == pls; recordNum++) {
+		/* debug info on this lock */
+
+		log_msg(LOG_MSG_DEBUG, MSG_LOCK_FINDLOCK_READRECORD,
+			recordNum, pll->lockCount,
+			pll->lockObject, pll->lockKey, pll->lockPid,
+			pll->lockZoneId);
+
+		/* continue if object is not the one we are looking for */
+
+		if (_lockMatch(a_object, pll->lockObject) != 0) {
+			continue;
+		}
+
+		/*
+		 * object found; return locked if searching for no key
+		 */
+
+		if (*a_key == '\0') {
+			/* no key specified - object is locked */
+			*r_recordNum = recordNum;
+			return (FINDLOCK_LOCKED);
+		}
+
+		/*
+		 * object found and keys present; see if keys match
+		 */
+
+		if (strcmp(pll->lockKey, a_key) != 0) {
+			/* keys do not match */
+			*r_recordNum = recordNum;
+			return (FINDLOCK_KEYMISMATCH);
+		}
+
+		/* object found and keys match - return match */
+
+		log_msg(LOG_MSG_DEBUG, MSG_LOCK_FINDLOCK_FOUND);
+
+		*r_recordNum = recordNum;
+		return (FINDLOCK_FOUND);
+	}
+
+	/* object not locked - return error if key supplied */
+
+	if (*a_key != '\0') {
+		return (FINDLOCK_NOTLOCKED);
+	}
+
+	/* object not locked and key not supplied - no lock found */
+
+	log_msg(LOG_MSG_DEBUG, MSG_LOCK_FINDLOCK_NOTFOUND);
+
+	return (FINDLOCK_NOTFOUND);
+}
+
+/*
+ * Name:	_addLock
+ * Description:	Add a new lock to the lock file
+ * Arguments:
+ *	r_key - if lock acquired key is placed here
+ *	a_fd - file descriptor opened on the lock file
+ *	a_object - object to lock
+ *	a_exclusive - type of lock to add:
+ *		== 0 - shared lock
+ *		!= 0 - exclusive lock
+ *	a_pid - if != 0 process i.d. to associate with this lock
+ *	a_zid - if >= 0 zone i.d. to associate with this lock
+ * Returns:	int
+ *			== 0 - success
+ *			!= 0 - failure
+ */
+
+static int
+_addLock(char *r_key, int a_fd, char *a_object, int a_exclusive, pid_t a_pid,
+	zoneid_t a_zid)
+{
+	LOCK_T	theLock;
+	char	*key;
+	off_t	pos;
+	ssize_t	result;
+
+	/* get unique i.d. for this lock */
+
+	key = _getUniqueId();
+
+	/* determine record number for next record in lock file */
+
+	pos = lseek(a_fd, 0L, SEEK_END);
+	if (pos == (off_t)-1) {
+		log_msg(LOG_MSG_ERR, MSG_LOCK_ADDLOCK_LSEEK_FAILURE,
+			a_exclusive ? MSG_LOCK_EXC : MSG_LOCK_SHR,
+			a_object, strerror(errno));
+		return (1);
+	}
+
+	/* allocate storace for this lock */
+
+	bzero(&theLock, sizeof (theLock));
+
+	/* fill in components of the lock */
+
+	(void) strlcpy(theLock._lrLock.lockObject, a_object,
+							LOCK_OBJECT_MAXLEN);
+	(void) strlcpy(theLock._lrLock.lockKey, key, LOCK_KEY_MAXLEN);
+	theLock._lrLock.lockCount = 1;
+	theLock._lrLock.lockPid = (a_pid > 0 ? a_pid : 0);
+	theLock._lrLock.lockRecordNum = (pos == 0 ? 0 : (pos/sizeof (LOCK_T)));
+	theLock._lrLock.lockExclusive = a_exclusive;
+	theLock._lrLock.lockZoneId = (a_zid >= 0 ? a_zid : -1);
+
+	/* debug info on new lock */
+
+	log_msg(LOG_MSG_DEBUG, MSG_LOCK_ADDLOCK_ADDING,
+		a_exclusive ? MSG_LOCK_EXC : MSG_LOCK_SHR,
+		pos, theLock._lrLock.lockObject, theLock._lrLock.lockKey,
+		theLock._lrLock.lockPid, theLock._lrLock.lockZoneId);
+
+	/* write the new lock record to the end of the lock file */
+
+	result = pwrite(a_fd, &theLock, LOCK_SIZE, pos);
+	if (result != LOCK_SIZE) {
+		log_msg(LOG_MSG_ERR, MSG_LOCK_ADDLOCK_PWRITE_FAILURE,
+			a_exclusive ? MSG_LOCK_EXC : MSG_LOCK_SHR,
+			a_object, strerror(errno));
+		return (1);
+	}
+
+	/* output the key assigned to standard out */
+
+	(void) strncpy(r_key, key, LOCK_KEY_MAXLEN);
+
+	return (0);
+}
+
+static int
+_incrementLockCount(int a_fd, LOCK_T *a_theLock)
+{
+	ADMINLOCK_T	*pll;
+	char		*pld;
+	long		pls;
+	ssize_t		result;
+
+	/* localize references to lock object */
+
+	pld = &a_theLock->_lrLockData[0];
+	pll = &a_theLock->_lrLock;
+	pls = sizeof (a_theLock->_lrLockData);
+
+	/* debug info on incrementing lock */
+
+	log_msg(LOG_MSG_DEBUG, MSG_LOCK_INCLOCK_ENTRY,
+		a_theLock->_lrLock.lockExclusive ?
+				MSG_LOCK_EXC : MSG_LOCK_SHR,
+		pll->lockRecordNum, pll->lockCount);
+
+	/* increment lock count */
+
+	pll->lockCount++;
+
+	/* write out updated lock */
+
+	result = pwrite(a_fd, pld, pls, pll->lockRecordNum*pls);
+	if (result != pls) {
+		log_msg(LOG_MSG_ERR, MSG_LOCK_INCLOCK_PWRITE_FAILURE,
+			a_theLock->_lrLock.lockExclusive ?
+					MSG_LOCK_EXC : MSG_LOCK_SHR,
+			a_theLock->_lrLock.lockObject,
+			strerror(errno));
+		return (1);
+	}
+
+	/* debug info lock incremented */
+
+	log_msg(LOG_MSG_DEBUG, MSG_LOCK_INCLOCK_DONE,
+		pll->lockRecordNum, pll->lockCount,
+		pll->lockObject, pll->lockKey);
+
+	return (0);
+}
+
+/*
+ * Name:	_validateLock
+ * Description:	determine if a specified lock is valid; if the lock is not valid
+ *		then remove the lock
+ * Arguments:	a_fd - file descriptor opened on the lock file
+ *		a_theLock - lock object to validate
+ * Returns:	boolean_t
+ *			B_TRUE - the lock is valid
+ *			B_FALSE - the lock is not valid and has been removed
+ */
+
+static boolean_t
+_validateLock(int a_fd, LOCK_T *a_theLock, int a_quiet)
+{
+	ADMINLOCK_T	*pll;
+	char		*pld;
+	long		pls;
+	char		path[MAXPATHLEN];
+
+	/* localize references to lock object */
+
+	pld = &a_theLock->_lrLockData[0];
+	pll = &a_theLock->_lrLock;
+	pls = sizeof (a_theLock->_lrLockData);
+
+	/* return true if no process i.d. associated with lock */
+
+	if (pll->lockPid <= 0) {
+		log_msg(LOG_MSG_DEBUG, MSG_VALID_NOPID, pll->lockObject);
+		return (B_TRUE);
+	}
+
+	/* see if the zone i.d. matches */
+
+	if (pll->lockZoneId != getzoneid()) {
+		log_msg(LOG_MSG_DEBUG, MSG_VALID_BADZID, pll->lockObject,
+		pll->lockZoneId, getzoneid());
+		return (B_TRUE);
+	} else {
+		log_msg(LOG_MSG_DEBUG, MSG_VALID_ZIDOK, pll->lockObject,
+		pll->lockZoneId, getzoneid());
+	}
+
+	/* see if the process is still active */
+
+	pkgstrPrintf_r(path, sizeof (path), "/proc/%d", pll->lockPid);
+	if (access(path, F_OK) == 0) {
+		log_msg(LOG_MSG_DEBUG, MSG_VALID_OK, pll->lockObject,
+			pll->lockPid, path);
+		return (B_TRUE);
+	}
+
+	log_msg(LOG_MSG_DEBUG, MSG_VALID_NOTOK, pll->lockObject, pll->lockPid,
+		path);
+
+	/* delete this lock */
+
+	log_msg(a_quiet ? LOG_MSG_DEBUG : LOG_MSG_WRN,
+		MSG_VALID_STALE, pll->lockObject, pll->lockPid,
+		pll->lockZoneId);
+
+	_decrementLockCount(a_fd, a_theLock);
+
+	return (B_FALSE);
+}
+
+static int
+_decrementLockCount(int a_fd, LOCK_T *a_theLock)
+{
+	ADMINLOCK_T	*pll;
+	LOCK_T		tmpLock;
+	RECORDNUM_T	lastRecord;
+	char		*pld;
+	long		pls;
+	off_t		lastPos;
+	ssize_t		result;
+	int		res;
+
+	/* localize references to lock object */
+
+	pld = &a_theLock->_lrLockData[0];
+	pll = &a_theLock->_lrLock;
+	pls = sizeof (a_theLock->_lrLockData);
+
+	/* decrement lock count */
+
+	pll->lockCount--;
+
+	/* if lock count > 0 then write out and leave locked */
+
+	if (pll->lockCount > 0) {
+		log_msg(LOG_MSG_DEBUG, MSG_LOCK_DECLOCK_DECING,
+			a_theLock->_lrLock.lockExclusive ?
+				MSG_LOCK_EXC : MSG_LOCK_SHR,
+			pll->lockRecordNum, pll->lockCount);
+
+		result = pwrite(a_fd, pld, pls, pll->lockRecordNum*pls);
+		if (result != pls) {
+			log_msg(LOG_MSG_ERR, MSG_LOCK_DECLOCK_PWRITE_FAILURE,
+				a_theLock->_lrLock.lockExclusive ?
+						MSG_LOCK_EXC : MSG_LOCK_SHR,
+				a_theLock->_lrLock.lockObject,
+				strerror(errno));
+			return (1);
+		}
+
+		log_msg(LOG_MSG_DEBUG, MSG_LOCK_DECLOCK_DONE,
+			pll->lockRecordNum, pll->lockCount,
+			pll->lockObject, pll->lockKey);
+
+		return (0);
+	}
+
+	/*
+	 * lock count zero - erase the record
+	 */
+
+	/* find last record in the lock file */
+
+	lastPos = lseek(a_fd, 0L, SEEK_END);	/* get size of lock file */
+	if (lastPos == (off_t)-1) {
+		log_msg(LOG_MSG_ERR, MSG_LOCK_DECLOCK_LSEEK_FAILURE,
+			a_theLock->_lrLock.lockExclusive ?
+					MSG_LOCK_EXC : MSG_LOCK_SHR,
+			a_theLock->_lrLock.lockObject,
+			strerror(errno));
+		return (1);
+	}
+
+	lastRecord = (lastPos/pls)-1;	/* convert size to record # */
+
+	log_msg(LOG_MSG_DEBUG, MSG_LOCK_DECLOCK_REMOVE,
+		lastPos, lastRecord, pll->lockRecordNum);
+
+	/* see if removing last record of file */
+
+	if (lastRecord == pll->lockRecordNum) {
+		/* debug info removing last record */
+
+		log_msg(LOG_MSG_DEBUG, MSG_LOCK_DECLOCK_LASTONE,
+			a_theLock->_lrLock.lockExclusive ?
+				MSG_LOCK_EXC : MSG_LOCK_SHR,
+			lastRecord, lastPos-pls);
+
+		/* removing last record of file, truncate */
+
+		res = ftruncate(a_fd, lastPos-pls);
+		if (res == -1) {
+			log_msg(LOG_MSG_ERR, MSG_LOCK_DECLOCK_FTRUNCATE_FAILURE,
+				a_theLock->_lrLock.lockExclusive ?
+						MSG_LOCK_EXC : MSG_LOCK_SHR,
+				a_theLock->_lrLock.lockObject,
+				strerror(errno));
+				return (1);
+		}
+		return (0);
+	}
+
+	/*
+	 * not removing last record of file:
+	 * read last record, truncate file one record,
+	 * replace record to be removed with last record read
+	 */
+
+	log_msg(LOG_MSG_DEBUG, MSG_LOCK_DECLOCK_REMOVING,
+		pll->lockRecordNum, lastRecord, lastPos-pls);
+
+	/* read in the last record */
+
+	result = pread(a_fd, tmpLock._lrLockData, pls, lastRecord*pls);
+	if (result != pls) {
+		log_msg(LOG_MSG_ERR, MSG_LOCK_DECLOCK_PREAD_FAILURE,
+			a_theLock->_lrLock.lockExclusive ?
+					MSG_LOCK_EXC : MSG_LOCK_SHR,
+			a_theLock->_lrLock.lockObject,
+			strerror(errno));
+		return (1);
+
+	}
+
+	/* truncate lock file removing the last record (just read in) */
+
+	res = ftruncate(a_fd, lastPos-pls);
+	if (res == -1) {
+		log_msg(LOG_MSG_ERR, MSG_LOCK_DECLOCK_FTRUNCATE_FAILURE,
+			a_theLock->_lrLock.lockExclusive ?
+					MSG_LOCK_EXC : MSG_LOCK_SHR,
+			a_theLock->_lrLock.lockObject,
+			strerror(errno));
+			return (1);
+	}
+
+	/* update record to indicate its new position in the lock file */
+
+	tmpLock._lrLock.lockRecordNum = pll->lockRecordNum;
+
+	/* write out the updated record to the new location */
+
+	result = pwrite(a_fd, tmpLock._lrLockData, pls, pll->lockRecordNum*pls);
+	if (result != pls) {
+		log_msg(LOG_MSG_ERR, MSG_LOCK_DECLOCK_PWRITE_FAILURE,
+			a_theLock->_lrLock.lockExclusive ?
+					MSG_LOCK_EXC : MSG_LOCK_SHR,
+			a_theLock->_lrLock.lockObject,
+			strerror(errno));
+		return (1);
+	}
+
+	return (0);
+}
+
+/*
+ * Name:	_getUniqueId
+ * Description:	Generate a unique ID that can be used as a key for a new lock
+ * Arguments:	None
+ * Returns:	char *
+ *			== NULL - error, no key generated
+ *			!= NULL - generated key
+ * NOTE:    	Any results returned is placed in new storage for the
+ *		calling method. The caller must use 'lu_memFree' to dispose
+ *		of the storage once the results are no longer needed.
+ */
+
+static char *
+_getUniqueId(void)
+{
+	char		*args[10];
+	char		*execResults;
+	char		newkey[LOCK_KEY_MAXLEN];
+	hrtime_t	hretime;
+	int		b;
+	int		execStatus;
+	struct tm	tstruct;
+	time_t		thetime;
+
+	/*
+	 * try and use makeuuid to generate a unique i.d. Such a unique i.d.
+	 * will look like:
+	 *		7814e3c1-1dd2-11b2-9fe8-000d560ddc82
+	 */
+
+	args[0] = "makeuuid";
+	args[1] = (char *)NULL;
+
+	b = e_ExecCmdList(&execStatus, &execResults, (char *)NULL,
+		"/usr/bin/makeuuid", (char *)NULL);
+
+	if ((b == 0) && (execStatus == 0) && (*execResults != '\0')) {
+		char	*p;
+		p = strpbrk(execResults, " \t\n");
+		if (p != (char *)NULL) {
+			*p = '\0';
+		}
+		log_msg(LOG_MSG_DEBUG, MSG_LOCK_GENUID_MAKEUUID,
+			execResults);
+		return (execResults);
+	}
+
+	/*
+	 * cannot run makeuuid - generate own unique key - the key is the
+	 * same length as unique uid but contains different information that
+	 * is as unique as can be made - include current hires time (nanosecond
+	 * real timer. Such a unique i.d. will look like:
+	 *		0203104092-1145345-0004e94d6af481a0
+	 */
+
+	hretime = gethrtime();
+
+	thetime = time((time_t *)NULL);
+	(void) localtime_r(&thetime, &tstruct);
+
+	(void) snprintf(newkey, sizeof (newkey),
+		"%02d%02d%02d%03d-%02d%02d%02d%d-%016llx", tstruct.tm_mday,
+		tstruct.tm_mon, tstruct.tm_year, tstruct.tm_yday,
+		tstruct.tm_hour, tstruct.tm_min, tstruct.tm_sec,
+		tstruct.tm_wday, hretime);
+
+	log_msg(LOG_MSG_DEBUG, MSG_LOCK_GENUID_INTERNAL, newkey);
+	return (strdup(newkey));
+}
+
+/*
+ * Name:	sigint_handler
+ * Synopsis:	SIGINT interrupt handler
+ * Description:	Catch the "SIGINT" signal; increment signal_received
+ *		global variable,
+ * Arguments:	signo - [RO, *RO] - (int)
+ *			Signal number that was caught
+ * Returns:	void
+ */
+
+static void
+sigint_handler(int a_signo)
+{
+	signal_received++;
+}
+
+/*
+ * Name:	sighup_handler
+ * Synopsis:	SIGHUP interrupt handler
+ * Description:	Catch the "SIGHUP" signal; increment signal_received
+ *		global variable,
+ * Arguments:	signo - [RO, *RO] - (int)
+ *			Signal number that was caught
+ * Returns:	void
+ */
+
+static void
+sighup_handler(int a_signo)
+{
+	signal_received++;
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/usr/src/cmd/svr4pkg/pkgadm/main.c	Fri Jun 05 10:28:40 2009 -0400
@@ -0,0 +1,247 @@
+/*
+ * 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
+ * or http://www.opensolaris.org/os/licensing.
+ * 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 2009 Sun Microsystems, Inc.  All rights reserved.
+ * Use is subject to license terms.
+ */
+
+
+/* unix system includes */
+
+#include <stdio.h>
+#include <stdarg.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/types.h>
+#include <unistd.h>
+#include <locale.h>
+#include <sys/param.h>
+#include <openssl/bio.h>
+
+#include <pkglib.h>
+#include <pkgerr.h>
+#include <keystore.h>
+#include "pkgadm.h"
+#include "pkgadm_msgs.h"
+
+/* initial error message buffer size */
+
+#define	ERR_BUFSIZE		2048
+
+/* Local Function Prototypes */
+
+static void			print_version();
+int				get_dbstatus(int argc, char **argv);
+
+/* holds subcommands and their definitions */
+struct cmd {
+	char		*c_name;
+	int		(*c_func)(int, char **);
+};
+
+struct cmd  cmds[] = {
+	{ "dbstatus",		get_dbstatus},
+	{ "lock",		admin_lock},
+	/* last one must be all NULLs */
+	{ NULL, NULL }
+};
+
+struct cmd cert_cmds[] = {
+	{ "addcert",		addcert},
+	{ "listcert",		listcert},
+	{ "removecert",		removecert},
+	/* last one must be all NULLs */
+	{ NULL, NULL }
+};
+
+
+/*
+ * Function:	main
+ *
+ * Return:	0	- subprocessing successful
+ *			  scripts and reboot
+ *	[other]	- subprocessing-specific failure
+ */
+int
+main(int argc, char **argv)
+{
+	char	cur_cmd;
+	int	newargc;
+	char	**newargv;
+	int	i;
+
+	/* Should be defined by cc -D */
+#if	!defined(TEXT_DOMAIN)
+#define	TEXT_DOMAIN "SYS_TEST"
+#endif
+
+	/* set the default text domain for messaging */
+	(void) setlocale(LC_ALL, "");
+	(void) textdomain(TEXT_DOMAIN);
+
+	if (getenv("PKGADM_VERBOSE")) {
+	    set_verbose(B_TRUE);
+	}
+
+	/* Superficial check of the arguments. */
+	if (argc <= 1) {
+		log_msg(LOG_MSG_INFO, MSG_USAGE);
+		return (1);
+	}
+
+	/* first, process any arguments that can appear before the subcommand */
+	while ((i = getopt(argc, argv, "vV?")) != EOF) {
+		switch (i) {
+		case 'v':	/* verbose mode enabled */
+			set_verbose(B_TRUE);
+			break;
+		case 'V':
+			print_version();
+			return (0);
+		case '?':
+			log_msg(LOG_MSG_INFO, MSG_USAGE);
+			return (0);
+		}
+	}
+
+	/* OK, hand it off to the subcommand processors */
+	for (cur_cmd = 0; cmds[cur_cmd].c_name != NULL; cur_cmd++) {
+		if (ci_streq(argv[optind], cmds[cur_cmd].c_name)) {
+			/* make subcommand the first option */
+			newargc = argc - optind;
+			newargv = argv + optind;
+			opterr = optind = 1; optopt = 0;
+			return (cmds[cur_cmd].c_func(newargc, newargv));
+		}
+	}
+
+	/* initialize security library */
+	sec_init();
+
+	/* OK, hand it off to the subcommand processors */
+	for (cur_cmd = 0; cert_cmds[cur_cmd].c_name != NULL; cur_cmd++) {
+		if (ci_streq(argv[optind], cert_cmds[cur_cmd].c_name)) {
+			/* make subcommand the first option */
+			newargc = argc - optind;
+			newargv = argv + optind;
+			opterr = optind = 1; optopt = 0;
+			return (cert_cmds[cur_cmd].c_func(newargc, newargv));
+		}
+	}
+
+	/* bad subcommand */
+	log_msg(LOG_MSG_ERR, MSG_BAD_SUB, argv[optind]);
+	log_msg(LOG_MSG_INFO, MSG_USAGE);
+	return (1);
+}
+
+/*
+ * Name:	set_verbose
+ * Description:	Turns on verbose output
+ * Scope:	public
+ * Arguments:	verbose = B_TRUE indicates verbose mode
+ * Returns:	none
+ */
+void
+set_verbose(boolean_t setting)
+{
+	log_set_verbose(setting);
+}
+
+/*
+ * Name:	get_verbose
+ * Description:	Returns whether or not to output verbose messages
+ * Scope:	public
+ * Arguments:	none
+ * Returns:	B_TRUE - verbose messages should be output
+ */
+boolean_t
+get_verbose()
+{
+	return (log_get_verbose());
+}
+
+/*
+ * Name:	log_pkgerr
+ * Description:	Outputs pkgerr messages to logging facility.
+ * Scope:	public
+ * Arguments:	type - the severity of the message
+ *		err - error stack to dump to facility
+ * Returns:	none
+ */
+void
+log_pkgerr(LogMsgType type, PKG_ERR *err)
+{
+	int i;
+	for (i = 0; i < pkgerr_num(err); i++) {
+		log_msg(type, "%s", pkgerr_get(err, i));
+	}
+}
+
+/*
+ * Name:	print_Version
+ * Desc:  Prints Version of packaging tools
+ * Arguments: none
+ * Returns: none
+ */
+static void
+print_version()
+{
+	/* ignore any and all arguments, print version only */
+	(void) fprintf(stdout, "%s\n", SUNW_PKGVERS);
+}
+
+/*
+ * usage
+ *
+ * Outputs the usage string.
+ *
+ * Return:1
+ * Side effects: none
+ */
+static int
+usage()
+{
+	log_msg(LOG_MSG_INFO, MSG_USAGE);
+	return (1);
+}
+
+/*
+ * get_dbstatus
+ *
+ * Return 'text' as the db status.
+ * Use the command line to determine if there is an alternate root.
+ *
+ * Return: 0 on success, nonzero on failure
+ * Side effects: none
+ */
+int
+get_dbstatus(int argc, char **argv)
+{
+	/* Either accept 1 argument or 3 arguments where the second is -R */
+	if (argc != 1 && (argc != 3 || strcmp(argv[1], "-R")))
+		return (usage());
+
+	(void) printf("%s\n", PKGADM_DBSTATUS_TEXT);
+
+	return (0);
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/usr/src/cmd/svr4pkg/pkgadm/pkgadm.h	Fri Jun 05 10:28:40 2009 -0400
@@ -0,0 +1,84 @@
+/*
+ * 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
+ * or http://www.opensolaris.org/os/licensing.
+ * 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 2007 Sun Microsystems, Inc.  All rights reserved.
+ * Use is subject to license terms.
+ */
+
+#ifndef _PKGADM_H
+#define	_PKGADM_H
+
+
+/*
+ * Module:	patchutil.h
+ * Description:	This module contains the interfaces for patchadd
+ */
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#include <pkgerr.h>
+#include <keystore.h>
+#include "pkglib.h"
+#include "libinst.h"
+
+/* version of packaging interface */
+#define	SUNW_PKGVERS	"1.0"
+
+/* string comparitor abbreviators */
+
+#define	ci_streq(a, b)		(strcasecmp((a), (b)) == 0)
+#define	ci_strneq(a, b, c)	(strncasecmp((a), (b), (c)) == 0)
+#define	streq(a, b)		(strcmp((a), (b)) == 0)
+#define	strneq(a, b, c)		(strncmp((a), (b), (c)) == 0)
+
+/* max l10n message length we will display */
+#define	MSG_MAX			1024
+
+/* main.c */
+extern	void		log_msg(LogMsgType, const char *, ...);
+extern	void		log_pkgerr(LogMsgType, PKG_ERR *);
+extern	void		set_verbose(boolean_t);
+extern	boolean_t	get_verbose(void);
+/* lock.c */
+extern int		admin_lock(int, char **);
+/* listcert.c */
+extern int		listcert(int, char **);
+/* importcert.c */
+extern int		addcert(int, char **);
+/* removecert.c */
+extern int		removecert(int, char **);
+
+/* certs.c */
+extern int		load_cert_and_key(PKG_ERR *, FILE *,
+    keystore_encoding_format_t, char *, EVP_PKEY **, X509 **);
+extern int		load_all_certs(PKG_ERR *, FILE *,
+    keystore_encoding_format_t, char *, STACK_OF(X509) **);
+
+#define	PKGADM_DBSTATUS_TEXT	"text"
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* _PKGADM_H */
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/usr/src/cmd/svr4pkg/pkgadm/pkgadm_msgs.h	Fri Jun 05 10:28:40 2009 -0400
@@ -0,0 +1,625 @@
+/*
+ * 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
+ * or http://www.opensolaris.org/os/licensing.
+ * 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 2007 Sun Microsystems, Inc.  All rights reserved.
+ * Use is subject to license terms.
+ */
+
+#ifndef	_PKGADM_MSGS_H
+#define	_PKGADM_MSGS_H
+
+
+#include <libintl.h>
+
+#ifdef	__cplusplus
+extern "C" {
+#endif
+
+#ifdef	lint
+#define	gettext(x)	x
+#endif
+
+/* generic messages */
+#define	MSG_BAD_SUB		gettext(\
+	"\"%s\" is not a valid subcommand")
+
+#define	MSG_MISSING_OPERAND	gettext(\
+	"-%c requires an operand")
+
+#define	MSG_USAGE		gettext(\
+"usage:\n" \
+"\n" \
+"pkgadm addcert  [-ty] [-a app] [-k keystore] [-e keyfile]\n" \
+"\t[-f format] [-n name] [-P passarg] [-p input_passarg]\n" \
+"\t[-R rootpath] certfile\n" \
+"\n" \
+"\t- Adds a trusted CA certificate or user certificate\n" \
+"\tand private key\n" \
+"\n" \
+"pkgadm removecert [-a app] [-k keystore] -n name [-P passarg]\n" \
+"\t[-R rootpath]\n" \
+"\n" \
+"\t- Removes a trusted CA certificate or user certificate\n" \
+"\tand private key\n" \
+"\n" \
+"pkgadm listcert  [-a app] [-f format] [-k keystore] -n name\n" \
+"\t[-P passarg] [-o outfile] [-R rootpath]\n" \
+"\n" \
+"\t- Prints trusted CA certificates or user certificates\n" \
+"\n" \
+"pkgadm dbstatus [-R rootpath]\n" \
+"\n" \
+"\t- Returns 'text' - the text install database in use since Solaris 2.0\n" \
+"\t  is the current install database in use.\n" \
+"\n" \
+"pkgadm -V\n" \
+"\t- Displays packaging tools version\n" \
+"\n" \
+"pkgadm -?\n" \
+"\t- Shows this help message\n")
+
+#define	MSG_WARNING		gettext(\
+	"WARNING")
+
+#define	MSG_ERROR		gettext(\
+	"ERROR")
+
+#define	MSG_T_OPTION_ARGS	gettext(\
+	"-t option takes 2 or 3 arguments, not %d!\n")
+
+#define	MSG_T_RESULT_TWO	gettext(\
+	"result <%d>: <%s> ~= <%s>\n")
+
+#define	MSG_T_RESULT_THREE	gettext(\
+	"required <%d> actual <%d> <%30s> ~- <%30s>\n")
+
+#define	MSG_KEYSTORE_PASSPROMPT	gettext(\
+	"Enter Keystore Password: ")
+
+#define	MSG_KEYSTORE_PASSOUTPROMPT	gettext(\
+	"Type a Keystore protection Password.\n" \
+	"Press ENTER for no protection password (not recommended): ")
+
+#define	MSG_PEM_PASSPROMPT	gettext(\
+	"Enter PEM Passphrase: ")
+
+#define	MSG_ERROR		gettext(\
+	"ERROR")
+
+/* warnings */
+
+#define	CREATE_PKGDIR_WARN	gettext(\
+	"Creating directory <%s>\n")
+
+#define	MSG_WRN_UNKNOWN	gettext(\
+	"Signer <%s> has unsupported signature, ignoring")
+
+#define	MSG_VALID_STALE		gettext(\
+	"Removing stale lock on <%s> pid <%ld> zid <%ld>")
+
+/* errors */
+
+#define	MSG_FATAL			gettext(\
+	"Fatal Error")
+
+#define	MSG_TOO_LONG			gettext(\
+	"Length of <%s> exceeds maximum allowed length")
+
+#define	MSG_INTERNAL			gettext(\
+	"Intenal Error <%s>")
+
+#define	MSG_OPEN			gettext(\
+	"Cannot open <%s> for reading")
+
+#define	MSG_OPEN_WRITE			gettext(\
+	"Cannot open <%s> for writing")
+
+#define	MSG_BAD_PASSARG			gettext(\
+	"Invalid password retrieval method <%s>")
+
+#define	MSG_BAD_PASS			gettext(\
+	"Invalid password")
+
+#define	ERR_LOG_FAIL			gettext(\
+	"Failed to log message using format <%s>")
+
+#define	MSG_BAD_FORMAT			gettext(\
+	"Invalid format: <%s>")
+
+#define	MSG_USER_NAME			gettext(\
+	"An alias is required when adding user certificates")
+
+#define	MSG_TRUSTED_NAME		gettext(\
+	"Trusted certificates cannot have an explicit alias")
+
+#define	MSG_MULTIPLE_TRUST		gettext(\
+	"Found multiple certificates in <%s>.  You must explicitly trust " \
+	"them using <%s>")
+
+#define	MSG_NO_MULTIPLE_TRUST		gettext(\
+	"Found multiple certificates in <%s>.  You must explicitly trust " \
+	"them using <%s>")
+
+#define	MSG_TRUSTED_KEY			gettext(\
+	"Cannot supply private key when adding trusted certificates")
+
+#define	MSG_TRUST_KEY_FOUND		gettext(\
+	"One or more private keys were found in trusted certificate file <%s>")
+
+#define	MSG_ADDCERT_ABORT		gettext(\
+	"Addition of trusted certificate aborted by user request")
+
+
+#define	MSG_NEED_KEY			gettext(\
+	"No private key found in <%s>, must specify one with -e")
+
+#define	MSG_NO_PRIVKEY		gettext(\
+	"No private key found in <%s>")
+
+#define	MSG_NO_CERTS		gettext(\
+	"No certificates found in <%s>")
+
+#define	MSG_MULTIPLE_CERTS	gettext(\
+	"Multiple certificates found in <%s>")
+
+#define	MSG_NO_ADDCERT	gettext(\
+	"Cannot add certificate(s) from <%s>.  No changes have been made.")
+
+#define	MSG_NO_ADDKEY	gettext(\
+	"Cannot add private key from <%s>.  No changes have been made.")
+
+#define	MSG_NO_REMOVECERT	gettext(\
+	"Cannot remove certificate with alias <%s>")
+
+#define	MSG_VERIFY_TRUST	gettext(\
+	"Are you sure you want to trust this certificate? ")
+
+#define	MSG_VERIFY_NOT_CA	gettext(\
+	"\n" \
+	"This certificate does not appear to be issued and signed\n" \
+	"by a certificate authority (CA). CA Certificates are normally\n" \
+	"self-signed and have CA Basic Constraints.\n" \
+	"Are you sure you want to trust this certificate? ")
+
+#define	MSG_PARSE	gettext(\
+	"Parsing error")
+
+#define	MSG_TRUSTED	gettext(\
+	"Certificate(s) from <%s> are now trusted")
+
+#define	MSG_TRUSTING	gettext(\
+	"Trusting certificate <%s>")
+
+#define	MSG_ADDED	gettext(\
+	"Successfully added Certificate <%s> with alias <%s>")
+
+#define	MSG_REMOVED	gettext(\
+	"Successfully removed Certificate(s) with alias <%s>")
+
+#define	MSG_MEM		gettext(\
+	"Out of memory")
+
+#define	MSG_PRINT		gettext(\
+	"Cannot print certificates to <%s>")
+
+#define	MSG_PROBLEM_CONVERT	gettext(\
+	"Does %s/var/sadm exist?  Can the user write to it? (%s)")
+
+#define	MSG_CONTENTS_FORMAT	gettext(\
+	"Operation failed due to corrupted install contents data file.")
+
+#define	MSG_MKDIR_FAILED	gettext(\
+	"Could not mkdir for path %s.  %s.")
+
+#define	MSG_RENAME_FAILED	gettext(\
+	"Could not rename %s to %s\n%s")
+
+#define	MSG_REMOVE_FAILED	gettext(\
+	"Could not remove %s\n%s")
+
+#define	MSG_FILE_ACCESS		gettext(\
+	"Operation failed: unable to access file %s: %s")
+
+#define	MSG_NOT_READABLE	gettext(\
+	"Operation failed: unable to read file %s")
+
+#define	MSG_PATCH_UPGD gettext(\
+	"Operation failed: unable to process patch information\n")
+
+#define	MSG_BUILD_INDEXES gettext(\
+	"Operation failed: unable to build indexes\n")
+
+#define	MSG_FILE_NAME_TOO_LONG gettext(\
+	"Operation failed: file name too long: %s\n")
+
+#define	MSG_ZONES_MISSING_REQUEST	gettext(\
+	"Must specify operation to perform\n")
+
+#define	MSG_LOCK_ALTROOT_CANTCREATE	gettext(\
+	"lock: cannot create alternative root directory <%s>: %s\n")
+
+#define	MSG_LOCK_ALTROOT_NONEXIST	gettext(\
+	"lock: argument to -R <%s> is not a directory: %s\n")
+
+#define	MSG_LOCK_ROOTDIR_INVALID	gettext(\
+	"lock: lock file base directory <%s> not valid: %s\n")
+
+#define	MSG_LOCK_WFLAG_BADINT	gettext(\
+	"The integer value <%s> given to the -W option includes an " \
+	"invalid character: \"%c\"\n")
+
+#define	MSG_LOCK_pFLAG_BADINT	gettext(\
+	"The integer value <%s> given to the -p option includes an " \
+	"invalid character: \"%c\"\n")
+
+#define	MSG_LOCK_zFLAG_BADINT	gettext(\
+	"The integer value <%s> given to the -z option includes an " \
+	"invalid character: \"%c\"\n")
+
+#define	MSG_LOCK_nFLAG_BADINT	gettext(\
+	"The integer value <%s> given to the -n option includes an " \
+	"invalid character: \"%c\"\n")
+
+#define	MSG_LOCK_ar_TOGETHER	gettext(\
+	"lock: The -a and -r options cannot be used together: "\
+	"specify only one.\n")
+
+#define	MSG_LOCK_kARG_TOOLONG	gettext(\
+	"Argument to -k is <%d> characters: may not exceed <%d> characters\n")
+
+#define	MSG_LOCK_oARG_TOOLONG	gettext(\
+	"Argument to -o is <%d> characters: may not exceed <%d> characters\n")
+
+#define	MSG_LOCK_RARG_NOT_ABSOLUTE	gettext(\
+	"Argument to -R must be absolute path: %s")
+
+#define	MSG_LOCK_WFLAG_ERROR	gettext(\
+	"Argument to -W has problem with wait interval <%s>: %s")
+
+#define	MSG_LOCK_pFLAG_ERROR	gettext(\
+	"Argument to -p has problem with process i.d. value <%s>: %s")
+
+#define	MSG_LOCK_zFLAG_ERROR	gettext(\
+	"Argument to -p has problem with zone i.d. value <%s>: %s")
+
+#define	MSG_LOCK_nFLAG_ERROR	gettext(\
+	"Argument to -n has problem with maximum number of retries " \
+	"value <%s>: %s")
+
+#define	MSG_LOCK_es_TOGETHER	gettext(\
+	"lock: The -e and -s options cannot be used together: "\
+	"specify only one.\n")
+
+#define	MSG_LOCK_ak_TOGETHER	gettext(\
+	"lock: The -k option cannot be used with the -a option.\n")
+
+#define	MSG_LOCK_e_without_a	gettext(\
+	"lock: The -e option can only be used with the -a option.\n")
+
+#define	MSG_LOCK_s_without_a	gettext(\
+	"lock: The -s option can only be used with the -a option.\n")
+
+#define	MSG_LOCK_ACQUIRE_KEYMISMATCH	gettext(\
+	"cannot acquire %s lock on <%s>: object locked and specified key " \
+	"does not match")
+
+#define	MSG_LOCK_ACQUIRE_ERROR	gettext(\
+	"cannot determine if object <%s> key <%s> is locked: %s")
+
+#define	MSG_LOCK_ACQUIRE_TIMEDOUT	gettext(\
+	"cannot acquire %s lock on <%s> key <%s>: object locked, no key " \
+	"was specified, and the wait timed out")
+
+#define	MSG_LOCK_ACQUIRE_WAITING	gettext(\
+	"object <%s> is locked: waiting for object to become available")
+
+#define	MSG_LOCK_ACQUIRE_REOPEN_FAILED	gettext(\
+	"cannot reopen lock file after waiting for lock on object " \
+	"<%s> to be released")
+
+#define	MSG_LOCK_RELEASE_NOTLOCKED	gettext(\
+	"cannot release lock on <%s> key <%s>: object not locked and " \
+	"a key was specified")
+
+#define	MSG_LOCK_RELEASE_LOCKED		gettext(\
+	"cannot release lock on <%s> key <%s>: object locked but no " \
+	"key was specified")
+
+#define	MSG_LOCK_RELEASE_NOTFOUND	gettext(\
+	"cannot release lock on <%s> key <%s>: object is not locked")
+
+#define	MSG_LOCK_RELEASE_KEYMISMATCH	gettext(\
+	"cannot release lock on <%s>: object locked and specified key " \
+	"does not match")
+
+#define	MSG_LOCK_RELEASE_ERROR		gettext(\
+	"cannot determine if object <%s> key <%s> is locked")
+
+#define	MSG_LOCK_EXEC_ACCESS	gettext(\
+	"cannot execute command <%s>: %s")
+
+#define	MSG_LOCK_EXEC_NOINPUT	gettext(\
+	"cannot open input file <%s>: %s")
+
+#define	MSG_LOCK_EXEC_NOPIPE	gettext(\
+	"cannot create pipe: %s")
+
+#define	MSG_LOCK_FINDLOCK_LSEEK_FAILURE	gettext(\
+	"cannot find lock <%s> key <%s>: lseek failure: %s")
+
+#define	MSG_LOCK_ADDLOCK_PWRITE_FAILURE	gettext(\
+	"cannot create %s lock for object <%s>: pwrite failure: %s")
+
+#define	MSG_LOCK_ADDLOCK_LSEEK_FAILURE	gettext(\
+	"cannot create %s lock for object <%s>: lseek failure: %s")
+
+#define	MSG_LOCK_INCLOCK_PWRITE_FAILURE	gettext(\
+	"cannot increment %s lock for object <%s>: pwrite failure: %s")
+
+#define	MSG_LOCK_DECLOCK_PWRITE_FAILURE	gettext(\
+	"cannot decrement %s lock for object <%s>: pwrite failure: %s")
+
+#define	MSG_LOCK_DECLOCK_PREAD_FAILURE	gettext(\
+	"cannot decrement %s lock for object <%s>: pread failure: %s")
+
+#define	MSG_LOCK_DECLOCK_LSEEK_FAILURE	gettext(\
+	"cannot decrement %s lock for object <%s>: lseek failure: %s")
+
+#define	MSG_LOCK_DECLOCK_FTRUNCATE_FAILURE	gettext(\
+	"cannot decrement %s lock for object <%s>: ftruncate failure: %s")
+
+/*
+ * i18n:
+ * next two messages grouped together
+ */
+
+#define	MSG_LOCK_ACQUIRE_BUSY_QUASI	gettext(\
+	"cannot acquire %s lock on <%s> key <%s>: object matches wildcard " \
+	"<%s> lock%s")
+#define	MSG_LOCK_ACQUIRE_BUSY_FIRST	gettext(\
+	"cannot acquire %s lock on <%s> key <%s>: object <%s> is locked <%s>%s")
+
+/*
+ * i18n: note this message may be appended to the previous message
+ * by supplying it to the final "%s" at the end of the line above;
+ * that is either:
+ *  cannot acquire %s lock on <%s> key <%s>: object is locked <%s>
+ * or:
+ *  cannot acquire %s lock on <%s> [...] is locked <%s> and no key specified
+ */
+
+#define	MSG_LOCK_ACQUIRE_BUSY_ADDITIONAL	gettext(\
+	" and no key specified")
+
+/*
+ * i18n: note these two "messages" are inserted into other
+ * messages, such as:
+ * 	cannot acquire %s lock on <%s>
+ * will be either:
+ *	cannot acquire shared lock on <%s>
+ * or
+ *	cannot acquire exclusive lock on <%s>
+ */
+
+#define	MSG_LOCK_EXC	gettext(\
+	"exclusive")
+
+#define	MSG_LOCK_SHR		gettext(\
+	"shared")
+
+/*
+ * i18n: note these messages are "debugging" messages and will normally
+ * not be seen unless debugging has been enabled for problem root causing
+ * so they are not meant to be perfectly "human readable"
+ */
+
+#define	MSG_VALID_NOPID		gettext(\
+	"validate lock <%s>: VALID (no pid)")
+
+#define	MSG_VALID_BADZID	gettext(\
+	"validate lock <%s>: VALID (lock zid <%ld> this zid <%ld>)")
+
+#define	MSG_VALID_ZIDOK	gettext(\
+	"validate lock <%s>: zone i.d.s match (lock zid <%ld> this zid <%ld>)")
+
+#define	MSG_VALID_OK		gettext(\
+	"validate lock <%s> pid <%ld> path <%s>: VALID")
+
+#define	MSG_VALID_NOTOK		gettext(\
+	"validate lock <%s> pid <%ld> path <%s>: NOT VALID")
+
+#define	MSG_LCKMCH_ENTRY	gettext(\
+	"lockMatch: *** BEGIN *** compare objects <%s> <%s>")
+
+#define	MSG_LCKMCH_FSTNODE	gettext(\
+	"lockMatch: first lock node (%d) <%s>")
+
+#define	MSG_LCKMCH_SCNDNODE	gettext(\
+	"lockMatch: second lock node (%d) <%s>")
+
+#define	MSG_LCKMCH_NODES	gettext(\
+	"lockMatch: first lock node <%s> prefix <%s> (%d) second lock " \
+	" node <%s> prefix <%s> (%d)")
+
+#define	MSG_LCKMCH_DIRMCH	gettext(\
+	"lockMatch: no prefix direct comparison: match: <%s> <%s>")
+
+#define	MSG_LCKMCH_DIRNOMCH	gettext(\
+	"lockMatch: no prefix direct comparison: NO MATCH: <%s> <%s>")
+
+#define	MSG_LCKMCH_PFXMCH	gettext(\
+	"lockMatch: prefix comparison: match: <%s> <%s>")
+
+#define	MSG_LCKMCH_PFXNOMCH	gettext(\
+	"lockMatch: prefix comparison: NO MATCH: <%s> <%s>")
+
+#define	MSG_LCKMCH_FSTLCK	gettext(\
+	"lockMatch: first lock index (%d) last scanned node <%s> prefix " \
+	"<%s> (%d)")
+
+#define	MSG_LCKMCH_SCNDLCK	gettext(\
+	"lockMatch: second lock index (%d) last scanned node <%s> prefix " \
+	"<%s> (%d)")
+
+#define	MSG_LCKMCH_ABSNOMCH	gettext(\
+	"lockMatch: absolute locks: NO MATCH: <%s> <%s>")
+
+#define	MSG_LCKMCH_OBJMCH	gettext(\
+	"lockMatch: object locks: match: <%s> <%s>")
+
+#define	MSG_LCKMCH_OVLPNOMCH	gettext(\
+	"lockMatch: nonmatching overlapping objects: <%s> <%s> before " \
+	"(%d) <%s>")
+
+#define	MSG_LCKMCH_SAME	gettext(\
+	"lockMatch: locks begin with same node - compare: <%s> <%s> at <%s>")
+
+#define	MSG_LCKMCH_SCNDSUB	gettext(\
+	"lockMatch: second lock <%s> subset of <%s> at (%d) <%s>")
+
+#define	MSG_LCKMCH_FRSTSUB	gettext(\
+	"lockMatch: first lock <%s> subset of <%s> at (%d) <%s>")
+
+#define	MSG_LCKMCH_DONTKNOW	gettext(\
+	"lockMatch: unable to determine how to compare locks: <%s> <%s>: " \
+	"using direct comparision")
+
+#define	MSG_LCKMCH_READY	gettext(\
+	"lockMatch: comparing nodes locks <%s> <%s>")
+
+#define	MSG_LCKMCH_NODEFAIL	gettext(\
+	"lockMatch: node (%d) comparison: NO MATCH: <%s> != <%s>")
+
+#define	MSG_LCKMCH_NODEOK	gettext(\
+	"lockMatch: node (%d) comparision: match: <%s> == <%s>")
+
+#define	MSG_LCKMCH_MATCHOK	gettext(\
+	"lockMatch: locks match: <%s> == <%s>")
+
+#define	MSG_LOCK_EXEC_RESULTS	gettext(\
+	"command <%s> executed: pid <%d> errno <0x%04x> status <0x%04x> " \
+	"final status <0x%04x> output <%s>")
+
+#define	MSG_LOCK_GENUID_MAKEUUID	gettext(\
+	"generated new unique key using makeuuid: %s")
+
+#define	MSG_LOCK_GENUID_INTERNAL	gettext(\
+	"generated new unique key using date: %s")
+
+#define	MSG_LOCK_DECLOCK_DECING	gettext(\
+	"decrement <%s> lock count record <%d> count <%d>")
+
+#define	MSG_LOCK_DECLOCK_DONE	gettext(\
+	"decrement lock record <%d> count <%d> object <%s> key <%s>")
+
+#define	MSG_LOCK_DECLOCK_REMOVE	gettext(\
+	"decrement lock remove record lastPos %ld last record %d " \
+	"current record %d")
+
+#define	MSG_LOCK_DECLOCK_LASTONE	gettext(\
+	"decrement lock removing <%s> lock last record <%d> " \
+	"truncating to <%ld>")
+
+#define	MSG_LOCK_DECLOCK_REMOVING	gettext(\
+	"decrement lock removing record <%d> last record <%d> " \
+	"truncating to <%ld>")
+
+#define	MSG_LOCK_INCLOCK_ENTRY	gettext(\
+	"increment <%s> lock count record <%d> count <%d>")
+
+#define	MSG_LOCK_INCLOCK_DONE	gettext(\
+	"increment lock record <%d> count <%d> object <%s> key <%s>")
+
+#define	MSG_LOCK_ADDLOCK_ADDING	gettext(\
+	"adding %s lock pos <%d> object <%s> key <%s> pid <%ld> zid <%ld>")
+
+#define	MSG_LOCK_FINDLOCK_ENTRY	gettext(\
+	"find lock object <%s> key <%s>")
+
+#define	MSG_LOCK_FINDLOCK_READRECORD	gettext(\
+	"find lock read record <%d>: count <%d> object <%s> key <%s> pid " \
+	"<%ld> zid <%ld>")
+
+#define	MSG_LOCK_FINDLOCK_FOUND	gettext(\
+	"find lock record found")
+
+#define	MSG_LOCK_FINDLOCK_NOTFOUND	gettext(\
+	"find lock record not found")
+
+#define	MSG_LOCK_OPENFILE_ENTRY	gettext(\
+	"open lock file root <%s> file <%s>")
+
+#define	MSG_LOCK_OPENFILE_SLEEPING	gettext(\
+	"open lock file busy <%s>: sleeping <%d>")
+
+#define	MSG_LOCK_OPENFILE_FAILURE	gettext(\
+	"open lock file could not be opened: %s")
+
+#define	MSG_LOCK_OPENFILE_SLEEP2	gettext(\
+	"open lock file cannot obtain record lock <%s>: sleeping <%d>")
+
+#define	MSG_LOCK_OPENFILE_FAIL2	gettext(\
+	"open lock file could not obtain record lock: <%s>")
+
+#define	MSG_LOCK_OPENFILE_SUCCESS	gettext(\
+	"open lock file: opened and locked fd <%d>")
+
+#define	MSG_LOCK_STATUS_READRECORD	gettext(\
+	"status read record <%d>: count <%d> object <%s> key <%s> pid <%ld> " \
+	"zid <%ld>")
+
+#define	MSG_LOCK_STATUS_ENTRY	gettext(\
+	"status key=<%s> object=<%s>")
+
+#define	MSG_LOCK_RELEASE_FOUND		gettext(\
+	"object <%s> key <%s> is locked: decrementing lock count")
+
+#define	MSG_LOCK_RELEASE_ENTRY	gettext(\
+	"release lock key=<%s> object=<%s> quiet=<%d>")
+
+#define	MSG_LOCK_RELEASE_FINDRESULT	gettext(\
+	"release lock result <%d> record <%d>")
+
+#define	MSG_LOCK_ACQUIRE_FOUND_INC	gettext(\
+	"object <%s> key <%s> is locked: incrementing <%s> lock count")
+
+#define	MSG_LOCK_ACQUIRE_ENTRY	gettext(\
+	"acquire lock key=<%s> object=<%s> quiet=<%d> exclusive=<%d>")
+
+#define	MSG_LOCK_ACQUIRE_FINDRESULT	gettext(\
+	"acquire %s lock result <%d> record <%d>")
+
+#define	MSG_LOCK_ACQUIRE_LOCKED_SHARED	gettext(\
+	"object <%s> key <%s> is locked but shared: incrementing lock count")
+
+#define	MSG_LOCK_ACQUIRE_NOTLOCKED	gettext(\
+	"cannot acquire %s lock on <%s> key <%s>: object not locked " \
+	"and non-matching key specified")
+
+#define	MSG_LOCK_ACQUIRE_NOTFOUND	gettext(\
+	"acquiring %s lock on object <%s>")
+
+#ifdef	__cplusplus
+}
+#endif
+
+#endif /* _PKGADM_MSGS_H */
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/usr/src/cmd/svr4pkg/pkgadm/removecert.c	Fri Jun 05 10:28:40 2009 -0400
@@ -0,0 +1,201 @@
+/*
+ * 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
+ * or http://www.opensolaris.org/os/licensing.
+ * 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 2009 Sun Microsystems, Inc.  All rights reserved.
+ * Use is subject to license terms.
+ */
+
+
+#include <stdio.h>
+#include <stdarg.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/types.h>
+#include <unistd.h>
+#include <signal.h>
+#include <locale.h>
+#include <sys/param.h>
+#include <openssl/bio.h>
+
+#include <libinst.h>
+#include <pkglib.h>
+#include <pkgerr.h>
+#include <keystore.h>
+#include "pkgadm.h"
+#include "pkgadm_msgs.h"
+
+/*
+ * Name:	removecert
+ * Desc:  Removes a user certificate and associated private key,
+ *	or a trusted certificate, from the keystore.
+ * Syntax:	addcert [-a app] [-k keystore] -n name [-P passarg] [-R altroot]
+ */
+int
+removecert(int argc, char **argv)
+{
+	int i;
+	char	keystore_file[MAXPATHLEN] = "";
+	char	*keystore_base = NULL;
+	char	*homedir;
+	char	*passarg = NULL;
+	char	*altroot = NULL;
+	char	*prog = NULL;
+	char	*alias = NULL;
+	int	ret = 1;
+	PKG_ERR	*err = NULL;
+	keystore_handle_t	keystore = NULL;
+
+	while ((i = getopt(argc, argv, ":a:k:n:P:R:")) != EOF) {
+		switch (i) {
+		case 'a':
+			prog = optarg;
+			break;
+		case 'k':
+			keystore_base = optarg;
+			break;
+		case 'n':
+			alias = optarg;
+			break;
+		case 'P':
+			passarg = optarg;
+			break;
+		case 'R':
+			altroot = optarg;
+			break;
+		case ':':
+			log_msg(LOG_MSG_ERR, MSG_MISSING_OPERAND, optopt);
+			/* fallthrough intentional */
+		case '?':
+		default:
+			log_msg(LOG_MSG_ERR, MSG_USAGE);
+			goto cleanup;
+		}
+	}
+
+	/* we require a name */
+	if (alias == NULL) {
+		log_msg(LOG_MSG_ERR, MSG_USAGE);
+		goto cleanup;
+	}
+
+	/* should be no arguments left */
+	if ((argc-optind) > 0) {
+		log_msg(LOG_MSG_ERR, MSG_USAGE);
+		goto cleanup;
+	}
+
+	/* set up proper keystore */
+	if (keystore_base == NULL) {
+		if (geteuid() == 0 || altroot != NULL) {
+				/*
+				 * If we have an alternate
+				 * root, then we have no choice but to use
+				 * root's keystore on that alternate root,
+				 * since there is no way to resolve a
+				 * user's home dir given an alternate root
+				 */
+			if (strlcat(keystore_file, PKGSEC,
+			    MAXPATHLEN) >= MAXPATHLEN) {
+				log_msg(LOG_MSG_ERR, MSG_TOO_LONG,
+				    keystore_file);
+				goto cleanup;
+			}
+		} else {
+			if ((homedir = getenv("HOME")) == NULL) {
+				/*
+				 * not superuser, but no home dir, so
+				 * use superuser's keystore
+				 */
+				if (strlcat(keystore_file, PKGSEC,
+				    MAXPATHLEN) >= MAXPATHLEN) {
+					log_msg(LOG_MSG_ERR, MSG_TOO_LONG,
+					    keystore_file);
+					goto cleanup;
+				}
+			} else {
+				if (strlcat(keystore_file, homedir,
+				    MAXPATHLEN) >= MAXPATHLEN) {
+					log_msg(LOG_MSG_ERR, MSG_TOO_LONG,
+					    homedir);
+					goto cleanup;
+				}
+				if (strlcat(keystore_file, "/.pkg/security",
+				    MAXPATHLEN) >= MAXPATHLEN) {
+					log_msg(LOG_MSG_ERR, MSG_TOO_LONG,
+					    keystore_file);
+					goto cleanup;
+				}
+			}
+		}
+	} else {
+		if (strlcat(keystore_file, keystore_base,
+		    MAXPATHLEN) >= MAXPATHLEN) {
+		    log_msg(LOG_MSG_ERR, MSG_TOO_LONG,
+			keystore_base);
+		    goto cleanup;
+		}
+	}
+
+	err = pkgerr_new();
+
+	/* now load the key store */
+	log_msg(LOG_MSG_DEBUG, "Loading keystore <%s>", keystore_file);
+
+	set_passphrase_prompt(MSG_KEYSTORE_PASSPROMPT);
+	set_passphrase_passarg(passarg);
+
+	if (open_keystore(err, keystore_file, prog, pkg_passphrase_cb,
+	    KEYSTORE_ACCESS_READWRITE | KEYSTORE_PATH_HARD, &keystore) != 0) {
+		log_pkgerr(LOG_MSG_ERR, err);
+		goto cleanup;
+	}
+
+	/* now remove the selected certs */
+	log_msg(LOG_MSG_DEBUG, "Removing certificate(s) with name <%s>",
+	    alias);
+	if (delete_cert_and_keys(err, keystore, alias) != 0) {
+		log_pkgerr(LOG_MSG_ERR, err);
+		log_msg(LOG_MSG_ERR, MSG_NO_REMOVECERT, alias);
+		goto cleanup;
+	}
+
+	/* now write it back out */
+	log_msg(LOG_MSG_DEBUG, "Closing keystore");
+	set_passphrase_prompt(MSG_KEYSTORE_PASSOUTPROMPT);
+	set_passphrase_passarg(passarg);
+	if (close_keystore(err, keystore, pkg_passphrase_cb) != 0) {
+		log_pkgerr(LOG_MSG_ERR, err);
+		log_msg(LOG_MSG_ERR, MSG_NO_REMOVECERT, alias);
+		goto cleanup;
+	}
+
+	log_msg(LOG_MSG_INFO, MSG_REMOVED, alias);
+
+	ret = 0;
+	/* fallthrough intentional */
+cleanup:
+
+	if (err != NULL)
+		pkgerr_free(err);
+
+	return (ret);
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/usr/src/cmd/svr4pkg/pkgchk/Makefile	Fri Jun 05 10:28:40 2009 -0400
@@ -0,0 +1,45 @@
+#
+# 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
+# or http://www.opensolaris.org/os/licensing.
+# 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 2009 Sun Microsystems, Inc.  All rights reserved.
+# Use is subject to license terms.
+#
+
+PROG=		pkgchk
+
+OBJS=		checkmap.o	\
+		ckentry.o	\
+		main.o
+
+include $(SRC)/cmd/svr4pkg/Makefile.svr4pkg
+
+LDLIBS	+=	-ll -lpkg -linstzones -ladm
+LDLIBS	+=	-lnsl -lsocket
+
+
+.KEEP_STATE:
+all:            $(PROG)
+
+install:	all $(ROOTUSRSBINPROG)
+
+include $(SRC)/cmd/svr4pkg/Makefile.svr4pkg.targ
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/usr/src/cmd/svr4pkg/pkgchk/checkmap.c	Fri Jun 05 10:28:40 2009 -0400
@@ -0,0 +1,425 @@
+/*
+ * 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
+ * or http://www.opensolaris.org/os/licensing.
+ * 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 2006 Sun Microsystems, Inc.  All rights reserved.
+ * Use is subject to license terms.
+ */
+
+/* Copyright (c) 1984, 1986, 1987, 1988, 1989 AT&T */
+/* All Rights Reserved */
+
+
+#include <stdio.h>
+#include <string.h>
+#include <limits.h>
+#include <errno.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <dirent.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <sys/param.h>
+#include <sys/mman.h>
+#include <pkgstrct.h>
+#include <pkglocs.h>
+#include <locale.h>
+#include <libintl.h>
+#include <pkglib.h>
+#include "libadm.h"
+#include "libinst.h"
+
+extern int	qflag, lflag, Lflag, pkgcnt;
+extern short	npaths;
+
+extern char	*basedir, *pathlist[], *ppathlist[], **pkg, **environ;
+
+extern short	used[];
+extern struct cfent **eptlist;
+
+/* ocfile.c */
+extern int	socfile(VFP_T **vfp);	/* simple open & lock of DB. */
+extern int	relslock(void);		/* unlock the database. */
+
+/* ckentry.c */
+extern int	ckentry(int envflag, int maptyp, struct cfent *ept, VFP_T *vfp);
+
+#define	NXTENTRY(P, VFP) \
+		(maptyp ? srchcfile((P), "*", (VFP), (VFP_T *)NULL) : \
+		gpkgmapvfp((P), (VFP)))
+
+#define	MSG_ARCHIVE	"NOTE: some pathnames are in private formats " \
+			    "and cannot be verified"
+#define	WRN_NOPKG	"WARNING: no pathnames were associated with <%s>"
+#define	WRN_NOPATH	"WARNING: no information associated with pathname <%s>"
+#define	EMPTY_PKG "WARNING: Package <%s> is installed but empty"
+#define	ERR_NOMEM	"unable to allocate dynamic memory, errno=%d"
+#define	ERR_PKGMAP	"unable to open pkgmap file <%s>"
+#define	ERR_ENVFILE	"unable to open environment file <%s>"
+
+static struct cfent entry;
+
+static int	shellmatch(char *, char *);
+static int is_partial_path_in_DB(char *, char *);
+
+int	selpath(char *, int);
+int	selpkg(char *);
+
+/*
+ * This routine checks all files which are referenced in the pkgmap which is
+ * identified by the mapfile arg. When the package is installed, the mapfile
+ * may be the contents file or a separate pkgmap (maptyp tells the function
+ * which it is). The variable uninst tells the function whether the package
+ * is in the installed state or not. The envfile entry is usually a pkginfo
+ * file, but it could be any environment parameter list.
+ */
+
+int
+checkmap(int maptyp, int uninst, char *mapfile, char *envfile,
+		char *pkginst, char *path, int pathtype)
+{
+	FILE		*fp;
+	char		*cl = NULL;
+	char		*value;
+	char		param[MAX_PKG_PARAM_LENGTH];
+	int		count;
+	int		errflg;
+	int		n;
+	int		selected;
+	struct pinfo	*pinfo;
+	VFP_T		*vfp = (VFP_T *)NULL;
+
+	if (envfile != NULL) {
+		if ((fp = fopen(envfile, "r")) == NULL) {
+			progerr(gettext(ERR_ENVFILE), envfile);
+			return (-1);
+		}
+		param[0] = '\0';
+		while (value = fpkgparam(fp, param)) {
+			if (strcmp("PATH", param) != 0) {
+				/*
+				 * If checking an uninstalled package, we
+				 * only want two parameters. If we took all
+				 * of them, including path definitions, we
+				 * wouldn't be looking in the right places in
+				 * the reloc and root directories.
+				 */
+				if (uninst) {
+					if ((strncmp("PKG_SRC_NOVERIFY", param,
+					    16) == 0) && value) {
+						logerr(gettext(MSG_ARCHIVE));
+						putparam(param, value);
+					}
+					if ((strncmp("CLASSES", param,
+					    7) == 0) && value)
+						putparam(param, value);
+				} else
+					putparam(param, value);
+			}
+
+			free(value);
+
+			param[0] = '\0';
+		}
+		(void) fclose(fp);
+		basedir = getenv("BASEDIR");
+	}
+
+	/*
+	 * If we are using a contents file for the map, this locks the
+	 * contents file in order to freeze the database and assure it
+	 * remains synchronized with the file system against which it is
+	 * being compared. There is no practical way to lock another pkgmap
+	 * on some unknown medium so we don't bother.
+	 */
+	if (maptyp) {	/* If this is the contents file */
+		if (!socfile(&vfp)) {
+			progerr(gettext(ERR_PKGMAP), "contents");
+			return (-1);
+		}
+	} else {
+		if (vfpOpen(&vfp, mapfile, "r", VFP_NONE) != 0) {
+			progerr(gettext(ERR_PKGMAP), mapfile);
+			return (-1);
+		}
+	}
+
+	if ((cl = getenv("CLASSES")) != NULL)
+		cl_sets(qstrdup(cl));
+
+	errflg = count = 0;
+
+	do {
+		if ((n = NXTENTRY(&entry, vfp)) == 0) {
+			break;
+		}
+		/*
+		 * Search for partial paths in the ext DB.
+		 */
+		if (pathtype) {
+			/* LINTED warning: statement has no consequent: if */
+			if (is_partial_path_in_DB(entry.path, path)) {
+				/* Check this entry */
+				;
+			} else if (entry.ftype == 's' ||
+						entry.ftype == 'l') {
+				if (is_partial_path_in_DB(
+				/* LINTED warning: statement has no consequen */
+					entry.ainfo.local, path)) {
+					/* Check this entry */
+					;
+				} else {
+					continue;
+				}
+			} else {
+				/* Skip to next DB entry */
+				continue;
+			}
+		}
+
+		if (n < 0) {
+			char	*errstr = getErrstr();
+			logerr(gettext("ERROR: garbled entry"));
+			logerr(gettext("pathname: %s"),
+			    (entry.path && *entry.path) ? entry.path :
+			    "Unknown");
+			logerr(gettext("problem: %s"),
+			    (errstr && *errstr) ? errstr : "Unknown");
+			exit(99);
+		}
+		if (n == 0)
+			break; /* done with file */
+
+		/*
+		 * The class list may not be complete for good reason, so
+		 * there's no complaining if this returns an index of -1.
+		 */
+		if (cl != NULL)
+			entry.pkg_class_idx = cl_idx(entry.pkg_class);
+
+		if (maptyp && pkginst != NULL) {
+			/*
+			 * check to see if the entry we just read
+			 * is associated with one of the packages
+			 * we have listed on the command line
+			 */
+			selected = 0;
+			pinfo = entry.pinfo;
+			while (pinfo) {
+				if (selpkg(pinfo->pkg)) {
+					selected++;
+					break;
+				}
+				pinfo = pinfo->next;
+			}
+			if (!selected)
+				continue; /* not selected */
+		}
+
+		/*
+		 * Check to see if the pathname associated with the entry
+		 * we just read is associated with the list of paths we
+		 * supplied on the command line
+		 */
+		if (!selpath(entry.path, pathtype))
+			continue; /* not selected */
+
+		/*
+		 * Determine if this is a package object wanting
+		 * verification. Metafiles are always checked, otherwise, we
+		 * rely on the class to discriminate.
+		 */
+		if (entry.ftype != 'i')
+			/* If there's no class list... */
+			if (cl != NULL)
+				/*
+				 * ... or this entry isn't in that class list
+				 * or it's in a private format, then don't
+				 * check it.
+				 */
+				if (entry.pkg_class_idx == -1 ||
+				    cl_svfy(entry.pkg_class_idx) == NOVERIFY)
+					continue;
+
+		count++;
+		if (ckentry((envfile ? 1 : 0), maptyp, &entry, vfp))
+			errflg++;
+	} while (n != 0);
+
+	(void) vfpClose(&vfp);
+
+	if (maptyp)
+		relslock();
+
+	if (environ) {
+		/* free up environment resources */
+		for (n = 0; environ[n]; n++)
+			free(environ[n]);
+		free(environ);
+		environ = NULL;
+	}
+
+	if (maptyp) {
+		/*
+		 * make sure each listed package was associated with
+		 * an entry from the prototype or pkgmap
+		 */
+		(void) selpkg(NULL);
+	}
+	if (!qflag && !lflag && !Lflag) {
+		/*
+		 * make sure each listed pathname was associated with an entry
+		 * from the prototype or pkgmap
+		 */
+		(void) selpath(NULL, pathtype);
+	}
+	return (errflg);
+}
+
+int
+selpkg(char *p)
+{
+	static char *selected;
+	char buf[80];
+	char *root;
+	register int i;
+
+	if (p == NULL) {
+		if (selected == NULL) {
+			if (pkgcnt) {
+				for (i = 0; i < pkgcnt; ++i) {
+					/* bugid 1227628 */
+					root = get_inst_root();
+					if (root)
+						(void) snprintf(buf,
+						sizeof (buf),
+						"%s/var/sadm/pkg/%s/pkginfo",
+						root, pkg[i]);
+					else
+						(void) snprintf(buf,
+						sizeof (buf),
+						"/var/sadm/pkg/%s/pkginfo",
+						pkg[i]);
+
+					if (access(buf, F_OK))
+						logerr(gettext(WRN_NOPKG),
+							pkg[i]);
+					else
+						logerr(gettext(EMPTY_PKG),
+							pkg[i]);
+				}
+			}
+		} else {
+			for (i = 0; i < pkgcnt; ++i) {
+				if (selected[i] == NULL) {
+					root = get_inst_root();
+					if (root)
+						(void) snprintf(buf,
+						sizeof (buf),
+						"%s/var/sadm/pkg/%s/pkginfo",
+						root, pkg[i]);
+					else
+						(void) snprintf(buf,
+						sizeof (buf),
+						"/var/sadm/pkg/%s/pkginfo",
+						pkg[i]);
+
+					if (access(buf, F_OK))
+						logerr(gettext(WRN_NOPKG),
+							pkg[i]);
+					else
+						logerr(gettext(EMPTY_PKG),
+							pkg[i]);
+				}
+			}
+		}
+		return (0); /* return value not important */
+	} else if (pkgcnt == 0)
+		return (1);
+	else if (selected == NULL) {
+		selected =
+		    (char *)calloc((unsigned)(pkgcnt+1), sizeof (char));
+		if (selected == NULL) {
+			progerr(gettext(ERR_NOMEM), errno);
+			exit(99);
+			/*NOTREACHED*/
+		}
+	}
+
+	for (i = 0; i < pkgcnt; ++i) {
+		if (pkgnmchk(p, pkg[i], 0) == 0) {
+			if (selected != NULL)
+				selected[i] = 'b';
+			return (1);
+		}
+	}
+	return (0);
+}
+
+int
+selpath(char *path, int partial_path)
+{
+	int n;
+
+	if (!npaths)
+		return (1); /* everything is selectable */
+
+	for (n = 0; n < npaths; n++) {
+		if (path == NULL) {
+			if (!used[n])
+				logerr(gettext(WRN_NOPATH),
+					partial_path ? ppathlist[n] :
+					pathlist[n]);
+		} else if (partial_path) {
+			used[n] = 1;
+			return (1);
+		} else if (!shellmatch(pathlist[n], path)) {
+			used[n] = 1;
+			return (1);
+		}
+	}
+	return (0); /* not selected */
+}
+
+static int
+shellmatch(char *spec, char *path)
+{
+	/* Check if the value is NULL */
+	if (spec == NULL || path == NULL)
+		return (1);
+
+	while (*spec && (*spec == *path)) {
+		spec++, path++;
+	}
+	if ((*spec == *path) || (*spec == '*'))
+		return (0);
+	return (1);
+}
+
+static int
+is_partial_path_in_DB(char *srcpath, char *trgtpath)
+{
+	if (strstr(srcpath, trgtpath) == NULL) {
+		return (0);
+	} else {
+		return (1);
+	}
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/usr/src/cmd/svr4pkg/pkgchk/ckentry.c	Fri Jun 05 10:28:40 2009 -0400
@@ -0,0 +1,314 @@
+/*
+ * 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
+ * or http://www.opensolaris.org/os/licensing.
+ * 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 2006 Sun Microsystems, Inc.  All rights reserved.
+ * Use is subject to license terms.
+ */
+
+/* Copyright (c) 1984, 1986, 1987, 1988, 1989 AT&T */
+/* All Rights Reserved */
+
+
+#include <stdio.h>
+#include <memory.h>
+#include <string.h>
+#include <limits.h>
+#include <dirent.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <pkgstrct.h>
+#include <locale.h>
+#include <libintl.h>
+#include <unistd.h>
+#include <stdlib.h>
+#include "pkglib.h"
+#include "install.h"
+#include "libadm.h"
+#include "libinst.h"
+
+extern int	Lflag, lflag, aflag, cflag, fflag, qflag, nflag, xflag, vflag;
+extern char	*basedir, *device, pkgspool[];
+
+#define	NXTENTRY(P, VFP) \
+		(maptyp ? srchcfile((P), "*", (VFP), (VFP_T *)NULL) :\
+		gpkgmapvfp((P), (VFP)))
+
+#define	ERR_SPOOLED	"ERROR: unable to locate spooled object <%s>"
+#define	MSG_NET_OBJ	"It is remote and may be available from the network."
+#define	ERR_RMHIDDEN	"unable to remove hidden file"
+#define	ERR_HIDDEN	"ERROR: hidden file in exclusive directory"
+
+static char	*findspool(struct cfent *ept);
+static int	xdir(int maptyp, VFP_T *vfp, char *dirname);
+
+int
+ckentry(int envflag, int maptyp, struct cfent *ept, VFP_T *vfp)
+{
+	int	a_err, c_err,
+		errflg;
+	char	*path;
+	char	*ir = get_inst_root();
+
+	if (ept->ftype != 'i') {
+		if (envflag)
+			mappath(2, ept->path);
+		if (!device)
+			basepath(ept->path, maptyp ? NULL : basedir, ir);
+	}
+	canonize(ept->path);
+	if (strchr("sl", ept->ftype)) {
+		if (envflag)				/* -e option */
+			mappath(2, ept->ainfo.local);
+		if (!RELATIVE(ept->ainfo.local)) {	/* Absolute Path */
+			if (!device) {
+				if (ept->ftype == 'l')	/* Hard Link */
+					basepath(ept->ainfo.local, NULL, ir);
+			}
+		}
+		if (!RELATIVE(ept->ainfo.local))	/* Absolute Path */
+			canonize(ept->ainfo.local);
+	}
+	if (envflag) {
+		if (!strchr("isl", ept->ftype)) {
+			mapvar(2, ept->ainfo.owner);
+			mapvar(2, ept->ainfo.group);
+		}
+	}
+
+	if (lflag) {
+		tputcfent(ept, stdout);
+		return (0);
+	} else if (Lflag)
+		return (putcfile(ept, stdout));
+
+	errflg = 0;
+	if (device) {
+		if (strchr("dxslcbp", ept->ftype))
+			return (0);
+		if ((path = findspool(ept)) == NULL) {
+			logerr(gettext(ERR_SPOOLED), ept->path);
+			return (-1);
+		}
+
+		/*
+		 * If the package file attributes are to be sync'd up with
+		 * the pkgmap, we fix the attributes here.
+		 */
+		if (fflag) {
+			a_err = 0;
+			/* Clear dangerous bits. */
+			ept->ainfo.mode = (ept->ainfo.mode & S_IAMB);
+			/*
+			 * Make sure the file is readable by the world and
+			 * writeable by root.
+			 */
+			ept->ainfo.mode |= 0644;
+			if (!strchr("in", ept->ftype)) {
+				/* Set the safe attributes. */
+				if (a_err = averify(fflag, &ept->ftype,
+				    path, &ept->ainfo)) {
+					errflg++;
+					if (!qflag || (a_err != VE_EXIST)) {
+						logerr(gettext("ERROR: %s"),
+						    ept->path);
+						logerr(getErrbufAddr());
+					}
+					if (a_err == VE_EXIST)
+						return (-1);
+				}
+			}
+		}
+		/* Report invalid modtimes by passing cverify a -1 */
+		c_err = cverify((!fflag ? (-1) : fflag),  &ept->ftype, path,
+			&ept->cinfo, 1);
+		if (c_err) {
+			logerr(gettext("ERROR: %s"), path);
+			logerr(getErrbufAddr());
+			return (-1);
+		}
+	} else {
+		a_err = 0;
+		if (aflag && !strchr("in", ept->ftype)) {
+			/* validate attributes */
+			if (a_err = averify(fflag, &ept->ftype, ept->path,
+			    &ept->ainfo)) {
+				errflg++;
+				if (!qflag || (a_err != VE_EXIST)) {
+					logerr(gettext("ERROR: %s"),
+					    ept->path);
+					logerr(getErrbufAddr());
+					if (maptyp && ept->pinfo->status ==
+					    SERVED_FILE)
+						logerr(gettext(MSG_NET_OBJ));
+				}
+				if (a_err == VE_EXIST)
+					return (-1);
+			}
+		}
+		if (cflag && strchr("fev", ept->ftype) &&
+		    (!nflag || ept->ftype != 'v') && /* bug # 1082144 */
+		    (!nflag || ept->ftype != 'e')) {
+			/* validate contents */
+			/* Report invalid modtimes by passing cverify a -1 */
+			if (c_err = cverify((!fflag ? (-1) : fflag),
+				&ept->ftype, ept->path, &ept->cinfo, 1)) {
+				errflg++;
+				if (!qflag || (c_err != VE_EXIST)) {
+					if (!a_err)
+						logerr(gettext("ERROR: %s"),
+						    ept->path);
+					logerr(getErrbufAddr());
+					if (maptyp && ept->pinfo->status ==
+					    SERVED_FILE)
+						logerr(gettext(MSG_NET_OBJ));
+				}
+				if (c_err == VE_EXIST)
+					return (-1);
+			}
+		}
+		if (xflag && (ept->ftype == 'x')) {
+			/* must do verbose here since ept->path will change */
+			path = strdup(ept->path);
+			if (xdir(maptyp, vfp, path))
+				errflg++;
+			(void) strcpy(ept->path, path);
+			free(path);
+		}
+	}
+	if (vflag)
+		(void) fprintf(stderr, "%s\n", ept->path);
+	return (errflg);
+}
+
+static int
+xdir(int maptyp, VFP_T *vfp, char *dirname)
+{
+	DIR		*dirfp;
+	char		badpath[PATH_MAX+1];
+	int		dirfound;
+	int		errflg;
+	int		len;
+	int		n;
+	struct cfent	mine;
+	struct dirent	*drp;
+	struct pinfo	*pinfo;
+	void		*pos;
+
+	pos = vfpGetCurrCharPtr(vfp);	/* get current position in file */
+
+	if ((dirfp = opendir(dirname)) == NULL) {
+		progerr(gettext("unable to open directory <%s>"), dirname);
+		return (-1);
+	}
+	len = strlen(dirname);
+
+	errflg = 0;
+	(void) memset((char *)&mine, '\0', sizeof (struct cfent));
+	while ((drp = readdir(dirfp)) != NULL) {
+		if (strcmp(drp->d_name, ".") == NULL ||
+		    strcmp(drp->d_name, "..") == NULL)
+			continue;
+		dirfound = 0;
+		while ((n = NXTENTRY(&mine, vfp)) != 0) {
+			if (n < 0) {
+				char	*errstr = getErrstr();
+				logerr(gettext("ERROR: garbled entry"));
+				logerr(gettext("pathname: %s"),
+				    (mine.path && *mine.path) ? mine.path :
+				    "Unknown");
+				logerr(gettext("problem: %s"),
+				    (errstr && *errstr) ? errstr : "Unknown");
+				exit(99);
+			}
+			if (strncmp(mine.path, dirname, len) ||
+			(mine.path[len] != '/'))
+				break;
+			if (strcmp(drp->d_name, &mine.path[len+1]) == NULL) {
+				dirfound++;
+				break;
+			}
+		}
+
+		vfpGetCurrCharPtr(vfp) = pos;
+
+		if (!dirfound) {
+			(void) snprintf(badpath, sizeof (badpath),
+				"%s/%s", dirname, drp->d_name);
+			if (fflag) {
+				if (unlink(badpath)) {
+					errflg++;
+					logerr(gettext("ERROR: %s"), badpath);
+					logerr(gettext(ERR_RMHIDDEN));
+				}
+			} else {
+				errflg++;
+				logerr(gettext("ERROR: %s"), badpath);
+				logerr(gettext(ERR_HIDDEN));
+			}
+		}
+	}
+
+	if (maptyp) {
+		/* clear memory we've used */
+		while ((pinfo = mine.pinfo) != NULL) {
+			mine.pinfo = pinfo->next;
+			free((char *)pinfo);
+		}
+	}
+
+	(void) closedir(dirfp);
+	return (errflg);
+}
+
+static char *
+findspool(struct cfent *ept)
+{
+	static char	path[2*PATH_MAX+1];
+	char		host[PATH_MAX+1];
+
+	(void) strcpy(host, pkgspool);
+	if (ept->ftype == 'i') {
+		if (strcmp(ept->path, "pkginfo"))
+			(void) strcat(host, "/install");
+	} else if (ept->path[0] == '/') {
+		(void) strcat(host, "/root");
+	} else {
+		(void) strcat(host, "/reloc");
+	}
+
+	(void) snprintf(path, sizeof (path), "%s/%s", host,
+		ept->path + (ept->path[0] == '/'));
+
+	if (access(path, 0) == 0) {
+		return (path);
+	}
+
+	if ((ept->ftype != 'i') && (ept->volno > 0)) {
+		(void) snprintf(path, sizeof (path),
+				"%s.%d/%s", host, ept->volno,
+			ept->path + (ept->path[0] == '/'));
+		if (access(path, 0) == 0) {
+			return (path);
+		}
+	}
+	return (NULL);
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/usr/src/cmd/svr4pkg/pkgchk/main.c	Fri Jun 05 10:28:40 2009 -0400
@@ -0,0 +1,622 @@
+/*
+ * 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
+ * or http://www.opensolaris.org/os/licensing.
+ * 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 2009 Sun Microsystems, Inc.  All rights reserved.
+ * Use is subject to license terms.
+ */
+
+/* Copyright (c) 1984, 1986, 1987, 1988, 1989 AT&T */
+/* All Rights Reserved */
+
+
+#include <stdio.h>
+#include <limits.h>
+#include <unistd.h>
+#include <stdlib.h>
+#include <string.h>
+#include <ctype.h>
+#include <dirent.h>
+#include <sys/stat.h>
+#include <pkginfo.h>
+#include <pkglocs.h>
+#include <sys/types.h>
+#include <pkgstrct.h>
+#include <pkgtrans.h>
+#include <locale.h>
+#include <libintl.h>
+#include <pkglib.h>
+#include <libadm.h>
+#include <libinst.h>
+
+#define	MAXPATHS	1024
+
+#define	MSG_CHK_STRM	"Checking uninstalled stream format package " \
+				"<%s> from <%s>\n"
+#define	MSG_CHK_DIR	"Checking uninstalled directory format package " \
+				"<%s> from <%s>\n"
+#define	MSG_NOTROOT	"NOTE: \"root\" permission may be required to " \
+				"validate all objects in the client filesystem."
+#define	MSG_CONT	"Continuing."
+
+#define	WRN_F_SPOOL	"WARNING: %s is spooled. Ignoring \"f\" argument"
+
+#define	ERR_ROOT_SET	"Could not set install root from the environment."
+#define	ERR_ROOT_CMD	"Command line install root contends with environment."
+#define	ERR_IOPEN	"unable to open input file <%s>"
+#define	ERR_IEMPTY	"no pathnames in file specified by -i option"
+#define	ERR_POPTION	"no pathname included with -p option"
+#define	ERR_PARTIAL_POPTION	"no pathname included with -P option"
+#define	ERR_MAXPATHS	"too many pathnames in option list (limit is %d)"
+#define	ERR_NOTROOT	"You must be \"root\" for \"%s -f\" to" \
+					"execute properly."
+#define	ERR_SEL_PKG "No packages selected for verification."
+#define	ERR_CAT_LNGTH "The category argument exceeds the SVr4 ABI\n" \
+		"        defined maximum supported length of 16 characters."
+#define	ERR_CAT_FND "Category argument <%s> cannot be found."
+#define	ERR_CAT_INV "Category argument <%s> is invalid."
+#define	ERR_TOO_MANY "too many pathnames in list, limit is %d"
+#define	ERR_PATHS_INVALID "Pathnames in %s are not valid."
+#define	ERR_MKDIR "unable to make directory <%s>"
+#define	ERR_USAGE	"usage:\n" \
+		"\t%s [-l|vqacnxf] [-R rootdir] [-p path[, ...] | " \
+		"-P path[, ...]]\n" \
+		"\t\t[-i file] [options]\n" \
+		"\t%s -d device [-f][-l|v] [-p path[, ...] | " \
+		"-P path[, ...]]\n" \
+		"\t\t[-V ...] [-M] [-i file] [-Y category[, ...] | " \
+		"pkginst [...]]\n" \
+		"\twhere options may include ONE of the " \
+		"following:\n " \
+		"\t\t-m pkgmap [-e envfile]\n" \
+		"\t\tpkginst [...]\n" \
+		"\t\t-Y category[, ...]\n"
+
+#define	LINK	1
+
+char	**pkg = NULL;
+int	pkgcnt = 0;
+char	*basedir;
+char	*pathlist[MAXPATHS], *ppathlist[MAXPATHS], pkgspool[PATH_MAX];
+short	used[MAXPATHS];
+short	npaths;
+struct cfent **eptlist;
+
+int	aflag = (-1);
+int	cflag = (-1);
+int	vflag = 0;
+int	nflag = 0;
+int	lflag = 0;
+int	Lflag = 0;
+int	fflag = 0;
+int	xflag = 0;
+int	qflag = 0;
+int	Rflag = 0;
+int	dflag = 0;
+char 	*device;
+
+char	*uniTmp;
+
+static char	*mapfile,
+		*spooldir,
+		*tmpdir,
+		*envfile;
+static int	errflg = 0;
+static int	map_client = 1;
+
+void	quit(int);
+static void	setpathlist(char *);
+static void	usage(void);
+
+extern	char	**environ;
+extern	char	*pkgdir;
+
+/* checkmap.c */
+extern int	checkmap(int, int, char *, char *, char *, char *, int);
+/* scriptvfy.c */
+extern int	checkscripts(char *inst_dir, int silent);
+
+int
+main(int argc, char *argv[])
+{
+	int	pkgfmt = 0;	/* Makes more sense as a pointer, but */
+				/*	18N is compromised. */
+	char	file[PATH_MAX+1],
+		*abi_sym_ptr,
+		*vfstab_file = NULL;
+	char *all_pkgs[4] = {"all", NULL};
+	char **category = NULL;
+	char *catg_arg = NULL;
+	int	c;
+	int	n = 0;
+	char	*prog,
+		*Rvalue,
+		*dvalue;
+	int dbcreate = 0;
+	int pathtype;
+
+	/* initialize locale mechanism */
+
+	(void) setlocale(LC_ALL, "");
+
+#if !defined(TEXT_DOMAIN)	/* Should be defined by cc -D */
+#define	TEXT_DOMAIN "SYS_TEST"
+#endif
+	(void) textdomain(TEXT_DOMAIN);
+
+	/* determine program name */
+
+	prog = set_prog_name(argv[0]);
+
+	/* establish installation root directory */
+
+	if (!set_inst_root(getenv("PKG_INSTALL_ROOT"))) {
+		progerr(gettext(ERR_ROOT_SET));
+		quit(1);
+	}
+
+	/* check if not ABI compliant mode */
+	abi_sym_ptr = getenv("PKG_NONABI_SYMLINKS");
+	if (abi_sym_ptr && strncasecmp(abi_sym_ptr, "TRUE", 4) == 0) {
+		set_nonABI_symlinks();
+	}
+
+	/* bugId 4012147 */
+	if ((uniTmp = getenv("PKG_NO_UNIFIED")) != NULL)
+		map_client = 0;
+
+	while ((c = getopt(argc, argv, "Y:R:e:p:d:nLli:vaV:Mm:cqxfQP:?"))
+			!= EOF) {
+		switch (c) {
+		case 'p':
+			pathlist[npaths] = strtok(optarg, " , ");
+			if (pathlist[npaths++] == NULL) {
+				progerr(gettext(ERR_POPTION));
+				quit(1);
+			}
+			while (pathlist[npaths] = strtok(NULL, " , ")) {
+				if (npaths++ >= MAXPATHS) {
+					progerr(gettext(ERR_MAXPATHS),
+						MAXPATHS);
+					quit(1);
+				}
+			}
+			break;
+
+		case 'd':
+			dvalue = optarg;
+			dflag = 1;
+			break;
+
+		case 'n':
+			nflag++;
+			break;
+
+		case 'M':
+			map_client = 0;
+			break;
+
+		/*
+		 * Allow admin to establish the client filesystem using a
+		 * vfstab-like file of stable format.
+		 */
+		case 'V':
+			vfstab_file = flex_device(optarg, 2);
+			map_client = 1;
+			break;
+
+		case 'f':
+			if (getuid()) {
+				progerr(gettext(ERR_NOTROOT), prog);
+				quit(1);
+			}
+			fflag++;
+			break;
+
+		case 'i':
+			setpathlist(optarg);
+			break;
+
+		case 'v':
+			vflag++;
+			break;
+
+		case 'l':
+			lflag++;
+			break;
+
+		case 'L':
+			Lflag++;
+			break;
+
+		case 'x':
+			if (aflag < 0)
+				aflag = 0;
+			if (cflag < 0)
+				cflag = 0;
+			xflag++;
+			break;
+
+		case 'q':
+			qflag++;
+			break;
+
+		case 'a':
+			if (cflag < 0)
+				cflag = 0;
+			aflag = 1;
+			break;
+
+		case 'c':
+			if (aflag < 0)
+				aflag = 0;
+			cflag = 1;
+			break;
+
+		case 'e':
+			envfile = optarg;
+			break;
+
+		case 'm':
+			mapfile = optarg;
+			break;
+
+		case 'R':
+			Rvalue = optarg;
+			Rflag = 1;
+			break;
+
+		case 'Y':
+			catg_arg = strdup(optarg);
+
+			if ((category = get_categories(catg_arg)) == NULL) {
+				progerr(gettext(ERR_CAT_INV), catg_arg);
+				quit(1);
+			} else if (is_not_valid_length(category)) {
+				progerr(gettext(ERR_CAT_LNGTH));
+				quit(1);
+			}
+			break;
+
+		case 'Q':
+			dbcreate++;
+			break;
+
+		case 'P':
+			ppathlist[npaths] = strtok(optarg, " , ");
+			if ((ppathlist[npaths] == NULL) ||
+			    (ppathlist[npaths][0] == '-')) {
+				progerr(gettext(ERR_PARTIAL_POPTION));
+				quit(1);
+			}
+			npaths++;
+			while (ppathlist[npaths] = strtok(NULL, " , ")) {
+				if (npaths++ >= MAXPATHS) {
+					progerr(gettext(ERR_MAXPATHS),
+						MAXPATHS);
+					quit(1);
+				}
+			}
+			break;
+
+		default:
+			usage();
+			/*NOTREACHED*/
+			/*
+			 * Although usage() calls a noreturn function,
+			 * needed to add return (1);  so that main() would
+			 * pass compilation checks. The statement below
+			 * should never be executed.
+			 */
+			return (1);
+		}
+	}
+
+	/* Check for incompatible options */
+	if (dflag && Rflag)
+		usage();
+
+	/* Check for root dir and device dir if set */
+	if (Rflag) {
+		if (!set_inst_root(Rvalue)) {
+			progerr(gettext(ERR_ROOT_CMD));
+			quit(1);
+		}
+	}
+
+	if (dflag)
+		device = flex_device(dvalue, 1);
+
+	if (lflag || Lflag) {
+		/* we're only supposed to list information */
+		if ((cflag >= 0) || (aflag >= 0) ||
+		qflag || xflag || fflag || nflag || vflag)
+			usage();
+	}
+
+	set_PKGpaths(get_inst_root());
+
+	if (catg_arg != NULL && device == NULL) {
+		if (argc - optind) {
+			usage();
+		}
+		pkg = gpkglist(pkgdir, all_pkgs, category);
+		if (pkg == NULL) {
+			progerr(gettext(ERR_CAT_FND), catg_arg);
+			quit(1);
+		} else {
+			for (pkgcnt = 0; pkg[pkgcnt] != NULL; pkgcnt++);
+		}
+	} else if (catg_arg != NULL && optind < argc) {
+		usage();
+	} else {
+		pkg = &argv[optind];
+		pkgcnt = (argc - optind);
+	}
+
+	environ = NULL;		/* Sever the parent environment. */
+
+	if (vcfile() == 0) {
+		quit(99);
+	}
+
+	errflg = 0;
+	if (mapfile) {
+		/* check for incompatible options */
+		if (device || pkgcnt)
+			usage();
+		put_path_params();	/* Restore what's needed. */
+
+		/* send pathtype if partial path */
+		pathtype = (ppathlist[0] != NULL) ? 1 : 0;
+		if (checkmap(0, (device != NULL), mapfile, envfile, NULL,
+		    NULL, pathtype))
+			errflg++;
+	} else if (device) {
+		/* check for incompatible options */
+		if ((cflag >= 0) || (aflag >= 0))
+			usage();
+		if (qflag || xflag || nflag || envfile)
+			usage();
+		tmpdir = NULL;
+		if ((spooldir = devattr(device, "pathname")) == NULL)
+			spooldir = device;
+		if (isdir(spooldir)) {
+			tmpdir = spooldir = qstrdup(tmpnam(NULL));
+			if (fflag) {
+				logerr(gettext(WRN_F_SPOOL), *pkg);
+				fflag = 0;
+			}
+			if (mkdir(spooldir, 0755)) {
+				progerr(gettext(ERR_MKDIR), spooldir);
+				quit(99);
+			}
+			if (n = pkgtrans(device, spooldir, pkg, PT_SILENT,
+				NULL, NULL))
+				quit(n);
+			if (catg_arg != NULL)
+				pkg = gpkglist(spooldir, all_pkgs, category);
+			else
+				pkg = gpkglist(spooldir, all_pkgs, NULL);
+			pkgfmt = 0;
+		} else {
+			if (catg_arg != NULL)
+				pkg = gpkglist(spooldir,
+					pkgcnt ? pkg : all_pkgs, category);
+			else
+				pkg = gpkglist(spooldir,
+					pkgcnt ? pkg : all_pkgs, NULL);
+			pkgfmt = 1;
+		}
+
+		/*
+		 * At this point pkg[] is the list of packages to check. They
+		 * are in directory format in spooldir.
+		 */
+		if (pkg == NULL) {
+			if (catg_arg != NULL) {
+				progerr(gettext(ERR_CAT_FND), catg_arg);
+				quit(1);
+			} else {
+				progerr(gettext(ERR_SEL_PKG));
+				quit(1);
+			}
+		}
+
+		aflag = 0;
+
+		for (n = 0; pkg[n]; n++) {
+			char locenv[PATH_MAX];
+
+	/*
+	 * *********************************************************************
+	 * this feature is removed starting with Solaris 10 - there is no built
+	 * in list of packages that should be run "the old way"
+	 * *********************************************************************
+	 */
+#ifdef	ALLOW_EXCEPTION_PKG_LIST
+			/* Until 2.9, set it from the execption list */
+			if (exception_pkg(pkg[n], LINK))
+				set_nonABI_symlinks();
+#endif
+
+			if (pkgfmt)
+				(void) printf(
+					gettext(MSG_CHK_DIR), pkg[n], device);
+			else
+				(void) printf(
+					gettext(MSG_CHK_STRM), pkg[n], device);
+
+			(void) snprintf(pkgspool, sizeof (pkgspool),
+				"%s/%s", spooldir, pkg[n]);
+			(void) snprintf(file, sizeof (file),
+				"%s/install", pkgspool);
+			/* Here we check the install scripts. */
+			(void) printf(
+				gettext("## Checking control scripts.\n"));
+			(void) checkscripts(file, 0);
+			/* Verify consistency with the pkgmap. */
+			(void) printf(
+				gettext("## Checking package objects.\n"));
+			(void) snprintf(file, sizeof (file),
+				"%s/pkgmap", pkgspool);
+			(void) snprintf(locenv, sizeof (locenv),
+				"%s/pkginfo", pkgspool);
+			envfile = locenv;
+
+			/*
+			 * NOTE : checkmap() frees the environ data and
+			 * pointer when it's through with them.
+			 */
+			if (checkmap(0, (device != NULL), file, envfile,
+					pkg[n], NULL, 0))
+				errflg++;
+			(void) printf(
+				gettext("## Checking is complete.\n"));
+		}
+	} else {
+		if (envfile)
+			usage();
+
+		put_path_params();	/* Restore what's needed. */
+
+		/*
+		 * If this is a check of a client of some sort, we'll need to
+		 * mount up the client's filesystems. If the caller isn't
+		 * root, this may not be possible.
+		 */
+		if (is_an_inst_root()) {
+			if (getuid()) {
+				logerr(gettext(MSG_NOTROOT));
+				logerr(gettext(MSG_CONT));
+			} else {
+				if (get_mntinfo(map_client, vfstab_file))
+					map_client = 0;
+				if (map_client)
+					mount_client();
+			}
+		}
+
+		(void) snprintf(file, sizeof (file),
+			"%s/contents", get_PKGADM());
+		if (ppathlist[0] != NULL) {
+			for (n = 0; ppathlist[n]; n++) {
+				if (checkmap(1, (device != NULL), file, NULL,
+						NULL, ppathlist[n], 1))
+					errflg++;
+			}
+		} else if (pkg[0] != NULL) {
+				if (checkmap(1, (device != NULL), file, NULL,
+					pkg[0], NULL, 0)) {
+					errflg++;
+				}
+		} else {
+			if (checkmap(1, (device != NULL), file, NULL,
+					NULL, NULL, 0)) {
+				errflg++;
+			}
+		}
+
+		if (map_client) {
+			unmount_client();
+		}
+	}
+	quit(errflg ? 1 : 0);
+	/* LINTED: no return */
+}
+
+static void
+setpathlist(char *file)
+{
+	int fd;
+	struct stat st;
+	FILE *fplist;
+	char pathname[PATH_MAX];
+	/*
+	 * This trap laid to catch a mismatch between the declaration above and
+	 * the hard-coded constant in the fscanf below
+	 */
+#if PATH_MAX != 1024
+#error "PATH_MAX changed, so we have a bug to fix"
+#endif
+
+	if (strcmp(file, "-") == 0) {
+		fplist = stdin;
+	} else {
+		if ((fd = open(file, O_RDONLY)) == -1) {
+			progerr(gettext(ERR_IOPEN), file);
+			quit(1);
+		}
+		if (fstat(fd, &st) == -1) {
+			progerr(gettext(ERR_IOPEN), file);
+			quit(1);
+		}
+		if (S_ISDIR(st.st_mode) || S_ISBLK(st.st_mode)) {
+			progerr(gettext(ERR_PATHS_INVALID), file);
+			quit(1);
+		}
+		if ((fplist = fdopen(fd, "r")) == NULL) {
+			progerr(gettext(ERR_IOPEN), file);
+			quit(1);
+		}
+	}
+	while (fscanf(fplist, "%1024s", pathname) == 1) {
+		if (*pathname == '\0') {
+			progerr(gettext(ERR_PATHS_INVALID), file);
+			quit(1);
+		}
+		pathlist[npaths] = qstrdup(pathname);
+		if (npaths++ > MAXPATHS) {
+			progerr(gettext(ERR_TOO_MANY), MAXPATHS);
+			quit(1);
+		}
+	}
+	if (npaths == 0) {
+		progerr(gettext(ERR_IEMPTY));
+		quit(1);
+	}
+	(void) fclose(fplist);
+}
+
+void
+quit(int n)
+{
+	/* cleanup any temporary directories */
+	(void) chdir("/");
+	if (tmpdir != NULL) {
+		(void) rrmdir(tmpdir);
+		free(tmpdir);
+		tmpdir = NULL;
+	}
+	(void) pkghead(NULL);
+	exit(n);
+	/*NOTREACHED*/
+}
+
+static void
+usage(void)
+{
+	char *prog = get_prog_name();
+
+	(void) fprintf(stderr, gettext(ERR_USAGE), prog, prog);
+	quit(1);
+	/*NOTREACHED*/
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/usr/src/cmd/svr4pkg/pkgcond/Makefile	Fri Jun 05 10:28:40 2009 -0400
@@ -0,0 +1,41 @@
+#
+# 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
+# or http://www.opensolaris.org/os/licensing.
+# 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 2009 Sun Microsystems, Inc.  All rights reserved.
+# Use is subject to license terms.
+#
+
+PROG=		pkgcond
+
+OBJS=		main.o
+
+include $(SRC)/cmd/svr4pkg/Makefile.svr4pkg
+
+LDLIBS  +=      -lpkg -linstzones -ladm
+
+
+.KEEP_STATE:
+all:            $(PROG)
+
+install:	all $(ROOTPROG)
+
+include $(SRC)/cmd/svr4pkg/Makefile.svr4pkg.targ
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/usr/src/cmd/svr4pkg/pkgcond/main.c	Fri Jun 05 10:28:40 2009 -0400
@@ -0,0 +1,4468 @@
+/*
+ * 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
+ * or http://www.opensolaris.org/os/licensing.
+ * 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 2009 Sun Microsystems, Inc.  All rights reserved.
+ * Use is subject to license terms.
+ */
+
+
+/*
+ * Program:	pkgcond
+ *
+ * Function:	Implements the package command suite public utility pkgcond(1M)
+ *
+ * Usage:	pkgcond [-nv] [-O debug] condition [ argument ]
+ *
+ *		command options:
+ *			-n - negate results of condition test
+ *			-v - verbose output of condition testing
+ *
+ *		<condition> may be any one of:
+ *			can_add_driver [path]
+ *			can_remove_driver [path]
+ *			can_update_driver [path]
+ *			is_alternative_root [path]
+ *			is_boot_environment [path]
+ *			is_diskless_client [path]
+ *			is_global_zone [path]
+ *			is_mounted_miniroot [path]
+ *			is_netinstall_image [path]
+ *			is_nonglobal_zone [path]
+ *			is_path_writable path
+ *			is_running_system [path]
+ *			is_sparse_root_nonglobal_zone [path]
+ *			is_what [path]
+ *			is_whole_root_nonglobal_zone [path]
+ *
+ *		<option(s)> are specific to the condition used
+ *
+ * Input:	depends on command
+ *
+ * Output:	depends on command
+ *
+ * Exit status:	If the -n option is not specified:
+ *		== 0 - the specified condition is true (or exists).
+ *		== 1 - the specified condition is false (or does not exist).
+ *		== 2 - command line usage errors (including bad keywords)
+ *		== 3 - command failed to perform the test due to a fatal error
+ *
+ *		If the -n option is specified:
+ *		== 0 - the specified condition is false (or does not exist).
+ *		== 1 - the specified condition is true (or exists).
+ *		== 2 - command line usage errors (including bad keywords)
+ *		== 3 - command failed to perform the test due to a fatal error
+ */
+
+#include <stdio.h>
+#include <sys/mnttab.h>
+#include <sys/mntent.h>
+#include <stdarg.h>
+#include <stdlib.h>
+#include <string.h>
+#include <strings.h>
+#include <fcntl.h>
+#include <ctype.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <unistd.h>
+#include <locale.h>
+#include <errno.h>
+#include <sys/param.h>
+#include <assert.h>
+
+#include <instzones_api.h>
+#include <pkglib.h>
+#include <install.h>
+#include <libinst.h>
+#include <libadm.h>
+#include <messages.h>
+#include "pkgcond.h"
+#include "pkgcond_msgs.h"
+
+/* Should be defined by cc -D */
+
+#if	!defined(TEXT_DOMAIN)
+#define	TEXT_DOMAIN "SYS_TEST"
+#endif
+
+/* commands to execute */
+
+#define	LS_CMD		"/usr/bin/ls"
+
+/*
+ * type definition and "types" for testPath()
+ */
+
+typedef enum {
+	TEST_EXISTS = 0x01,
+	TEST_NOT_EXISTS = 0x02,
+	TEST_IS_DIRECTORY = 0x04,
+	TEST_IS_FILE = 0x08,
+	TEST_NOT_DIRECTORY = 0x10,
+	TEST_NOT_FILE = 0x20,
+	TEST_IS_SYMBOLIC_LINK = 0x40,
+	TEST_NOT_SYMBOLIC_LINK = 0x80,
+	TEST_GLOBAL_TOKEN_IN_FILE = 0x100
+} TEST_TYPES;
+
+/* holds file system info */
+
+struct fsi_t {
+	char	*fsi_mntOptions;
+	char	*fsi_fsType;
+	char	*fsi_mntPoint;
+};
+typedef struct fsi_t	FSI_T;
+
+/* holds parsed global data */
+
+struct globalData_t {
+		/* sparse root (are any file systems mounted read-only)? */
+	boolean_t gd_srFsMountedRO;
+		/* initial install: PKG_INIT_INSTALL=true */
+	boolean_t gd_initialInstall;
+		/* global zone install: SUNW_PKG_INSTALL_ZONENAME=global */
+	boolean_t gd_globalZoneInstall;
+		/* non-global zone install: SUNW_PKG_INSTALL_ZONENAME!=global */
+	boolean_t gd_nonglobalZoneInstall;
+		/* non-global zone is in a mounted state */
+	boolean_t inMountedState;
+		/* sorted list of all mounted file systems */
+	FSI_T	*gd_fileSystemConfig;
+		/* number of mounted file systems in list */
+	long	gd_fileSystemConfigLen;
+		/* current zone name */
+	char	*gd_zoneName;
+		/* version of target: PATCH_CLIENT_VERSION */
+	char	*gd_patchClientVersion;
+		/* SUNW_PKGCOND_GLOBAL_DATA:parentZone:zoneName */
+	char	*gd_parentZoneName;
+		/* SUNW_PKGCOND_GLOBAL_DATA:parentZone:zoneType */
+	char	*gd_parentZoneType;
+		/* root path to target: PKG_INSTALL_ROOT */
+	char	*gd_installRoot;
+		/* SUNW_PKGCOND_GLOBAL_DATA:currentZone:zoneName */
+	char	*gd_currentZoneName;
+		/* SUNW_PKGCOND_GLOBAL_DATA:currentZone:zoneType */
+	char	*gd_currentZoneType;
+		/* list of inherited file systems */
+	char	**gd_inheritedFileSystems;
+		/* path provided on command line */
+	char	*gd_cmdline_path;
+};
+typedef struct globalData_t	GLOBALDATA_T;
+
+/* holds subcommands and their definitions */
+
+struct cmd_t {
+	char		*c_name;
+	char		*c_args;
+	int		(*c_func)(int argc, char **argv, GLOBALDATA_T *a_gdt);
+};
+typedef struct cmd_t	CMD_T;
+
+/* Command function prototypes */
+
+static int		cmd_can_add_driver(int argc, char **argv,
+				GLOBALDATA_T *a_gdt);
+static int		cmd_can_remove_driver(int argc, char **argv,
+				GLOBALDATA_T *a_gdt);
+static int		cmd_can_update_driver(int argc, char **argv,
+				GLOBALDATA_T *a_gdt);
+static int		cmd_is_alternative_root(int argc, char **argv,
+				GLOBALDATA_T *a_gdt);
+static int		cmd_is_boot_environment(int argc, char **argv,
+				GLOBALDATA_T *a_gdt);
+static int		cmd_is_diskless_client(int argc, char **argv,
+				GLOBALDATA_T *a_gdt);
+static int		cmd_is_global_zone(int argc, char **argv,
+				GLOBALDATA_T *a_gdt);
+static int		cmd_is_mounted_miniroot(int argc, char **argv,
+				GLOBALDATA_T *a_gdt);
+static int		cmd_is_netinstall_image(int argc, char **argv,
+				GLOBALDATA_T *a_gdt);
+static int		cmd_is_nonglobal_zone(int argc, char **argv,
+				GLOBALDATA_T *a_gdt);
+static int		cmd_is_path_writable(int argc, char **argv,
+				GLOBALDATA_T *a_gdt);
+static int		cmd_is_running_system(int argc, char **argv,
+				GLOBALDATA_T *a_gdt);
+static int		cmd_is_sparse_root_ng_zone(int argc, char **argv,
+				GLOBALDATA_T *a_gdt);
+static int		cmd_is_what(int argc, char **argv,
+				GLOBALDATA_T *a_gdt);
+static int		cmd_is_whole_root_ng_zone(int argc, char **argv,
+				GLOBALDATA_T *a_gdt);
+
+/* Utility function Prototypes */
+
+static boolean_t	getNegateResults(void);
+static boolean_t	recursionCheck(int *r_recursion, char *a_function);
+static boolean_t	checkForReadOnlyMount(GLOBALDATA_T *a_gdt,
+    char *a_mntPoint, char *a_fsType, char *a_mntOptions);
+static int		adjustResults(int a_result);
+static int		calculateFileSystemConfig(GLOBALDATA_T *a_gdt);
+static int		getRootPath(char **r_rootPath);
+static int		getZoneName(char **r_zoneName);
+static int		mountOptionPresent(char *a_mntOptions, char *a_opt);
+static int		parseGlobalData(char *a_envVar, GLOBALDATA_T **a_gdt);
+static int		resolvePath(char **r_path);
+static int		setRootPath(char *a_path, char *a_envVar,
+    boolean_t a_mustExist);
+static int		testPath(TEST_TYPES a_tt, char *format, ...);
+static int		usage(char *a_format, ...);
+static int		findToken(char *path, char *token);
+static char		*getMountOption(char **p);
+static void		dumpGlobalData(GLOBALDATA_T *a_gdt);
+static void		removeLeadingWhitespace(char **a_str);
+static void		setNegateResults(boolean_t setting);
+static void		setVerbose(boolean_t);
+static void		sortedInsert(FSI_T **r_list, long *a_listSize,
+    char *a_mntPoint, char *a_fsType, char *a_mntOptions);
+static void		setCmdLinePath(char **a_path, char **args,
+    int num_args);
+
+/* local static data */
+
+static boolean_t	_negateResults = B_FALSE;
+static char		*_rootPath = "/";
+
+/* define subcommand data structure */
+
+static CMD_T cmds[] = {
+	{ "can_add_driver",		" [path]",
+		cmd_can_add_driver },
+	{ "can_remove_driver",		" [path]",
+		cmd_can_remove_driver },
+	{ "can_update_driver",		" [path]",
+		cmd_can_update_driver },
+	{ "is_alternative_root",	" [path]",
+		cmd_is_alternative_root },
+	{ "is_boot_environment",	" [path]",
+		cmd_is_boot_environment },
+	{ "is_diskless_client",		" [path]",
+		cmd_is_diskless_client },
+	{ "is_global_zone",		" [path]",
+		cmd_is_global_zone },
+	{ "is_mounted_miniroot",	" [path]",
+		cmd_is_mounted_miniroot },
+	{ "is_netinstall_image",	" [path]",
+		cmd_is_netinstall_image },
+	{ "is_nonglobal_zone",		" [path]",
+		cmd_is_nonglobal_zone },
+	{ "is_path_writable",		" path",
+		cmd_is_path_writable },
+	{ "is_running_system",		" [path]",
+		cmd_is_running_system },
+	{ "is_sparse_root_nonglobal_zone", " [path]",
+		cmd_is_sparse_root_ng_zone },
+	{ "is_what", " [path]",
+		cmd_is_what },
+	{ "is_whole_root_nonglobal_zone", " [path]",
+		cmd_is_whole_root_ng_zone },
+	/* last one must be all NULLs */
+	{ NULL, NULL }
+};
+
+/*
+ * *****************************************************************************
+ * main
+ * *****************************************************************************
+ */
+
+/*
+ * Name:	main
+ * Description:	main processing loop for pkgcond *
+ * Return:	0 - condition is satisfied (true)
+ *		1 - condition is not satisfied (false)
+ *		2 - command line usage errors
+ *		3 - failure to determine condition
+ */
+
+int
+main(int argc, char **argv)
+{
+	GLOBALDATA_T	*gdt = NULL;
+	char		**newargv;
+	char		*p;
+	int		cur_cmd;
+	int		i;
+	int		newargc;
+
+	/* make standard output non-buffered */
+
+	setbuf(stdout, NULL);
+
+	/* set the default text domain for messaging */
+
+	(void) setlocale(LC_ALL, "");
+	(void) textdomain(TEXT_DOMAIN);
+
+	/* remember command name */
+
+	set_prog_name(argv[0]);
+
+	/* tell spmi zones interface how to access package output functions */
+
+	z_set_output_functions(echo, echoDebug, progerr);
+
+	/* set verbose mode if appropriate environment variable is set */
+
+	if (getenv(ENV_VAR_VERBOSE)) {
+		/* same as -v */
+		setVerbose(B_TRUE);
+	}
+
+	/* set debug mode if appropriate environment variable is set */
+
+	if (getenv(ENV_VAR_DEBUG)) {
+		/* same as -O debug */
+
+		/* set sml tracing (sml.c) */
+		smlSetVerbose(B_TRUE);
+
+		/* set log and echo (interactive) message tracing */
+		setVerbose(B_TRUE);
+
+		/* enable echoDebug debugging messages */
+		echoDebugSetFlag(B_TRUE);
+	}
+
+	/* generate usage if no options or arguments specified */
+
+	if (argc <= 1) {
+		(void) usage(MSG_NO_ARGUMENTS_SPECIFIED);
+		return (R_USAGE);
+	}
+
+	/*
+	 * process any arguments that can appear before the subcommand
+	 */
+
+	while ((i = getopt(argc, argv, ":O:vn?")) != EOF) {
+		switch (i) {
+		/*
+		 * Not a public interface: the -O option allows the behavior
+		 * of the package tools to be modified. Recognized options:
+		 * -> debug
+		 * ---> enable debugging output
+		 */
+
+		case 'O':
+			for (p = strtok(optarg, ","); p != NULL;
+				p = strtok(NULL, ",")) {
+
+				/* debug - enable all tracing */
+
+				if (strcmp(p, "debug") == 0) {
+					/* set sml tracing */
+					smlSetVerbose(B_TRUE);
+					/* set log/echo tracing */
+					setVerbose(B_TRUE);
+					/* enable debugging messages */
+					echoDebugSetFlag(B_TRUE);
+					continue;
+				}
+
+				progerr(ERR_INVALID_O_OPTION, p);
+				return (adjustResults(R_USAGE));
+			}
+			break;
+
+		/*
+		 * Public interface: enable verbose (debug) output.
+		 */
+
+		case 'v':	/* verbose mode enabled */
+			/* set command tracing only */
+			setVerbose(B_TRUE);
+			break;
+
+		/*
+		 * Public interface: negate output results.
+		 */
+
+		case 'n':
+			setNegateResults(B_TRUE);
+			break;
+
+		/*
+		 * unrecognized option
+		 */
+
+		case '?':
+		default:
+			(void) usage(MSG_INVALID_OPTION_SPECIFIED, optopt);
+			return (R_USAGE);
+		}
+	}
+
+	/*
+	 * done processing options that can preceed subcommand
+	 */
+
+	/* error if no subcommand specified */
+
+	if ((argc-optind) <= 0) {
+		(void) usage(MSG_NO_ARGUMENTS_SPECIFIED);
+		return (R_USAGE);
+	}
+
+	/* parse global data if environment variable set */
+
+	if (parseGlobalData(PKGCOND_GLOBAL_VARIABLE, &gdt) != R_SUCCESS) {
+		log_msg(LOG_MSG_ERR, ERR_CANNOT_USE_GLOBAL_DATA,
+			PKGCOND_GLOBAL_VARIABLE);
+		return (R_ERROR);
+	}
+
+	if (setRootPath(gdt->gd_installRoot,
+	    (strcmp(gdt->gd_installRoot, "/") == 0) ? NULL :
+	    ENV_VAR_SET, B_TRUE) != R_SUCCESS) {
+		log_msg(LOG_MSG_ERR, ERR_CANNOT_SET_ROOT_PATH,
+			ENV_VAR_PKGROOT);
+		return (R_ERROR);
+	}
+
+	/* set path provided on the command line */
+
+	setCmdLinePath(&(gdt->gd_cmdline_path), argv, argc);
+	echoDebug(DBG_CMDLINE_PATH,
+	    gdt->gd_cmdline_path == NULL ? "" : gdt->gd_cmdline_path);
+
+	/* determine how file systems are layered in this zone */
+
+	if (calculateFileSystemConfig(gdt) != R_SUCCESS) {
+		log_msg(LOG_MSG_ERR, ERR_CANNOT_CALC_FS_CONFIG);
+		return (R_ERROR);
+	}
+
+	/* dump global data read in (only if debugging) */
+
+	dumpGlobalData(gdt);
+
+	/* search for specified subcommand and execute if found */
+
+	for (cur_cmd = 0; cmds[cur_cmd].c_name != NULL; cur_cmd++) {
+		if (ci_streq(argv[optind], cmds[cur_cmd].c_name)) {
+			int	result;
+
+			/* make subcommand the first option */
+
+			newargc = argc - optind;
+			newargv = argv + optind;
+			opterr = optind = 1; optopt = 0;
+
+
+			/* call subcommand with its own argc/argv */
+
+			result = cmds[cur_cmd].c_func(newargc, newargv, gdt);
+
+			/* process result code and exit */
+
+			result = adjustResults(result);
+			log_msg(LOG_MSG_DEBUG, DBG_RESULTS, result);
+			return (result);
+		}
+	}
+
+	/* subcommand not found - output error message and exit with error */
+
+	log_msg(LOG_MSG_ERR, ERR_BAD_SUB, argv[optind]);
+	(void) usage(MSG_UNRECOGNIZED_CONDITION_SPECIFIED);
+	return (R_USAGE);
+}
+
+/*
+ * *****************************************************************************
+ * command implementation functions
+ * *****************************************************************************
+ */
+
+/*
+ * Name:	cmd_is_diskless_client
+ * Description:	determine if target is a diskless client
+ * Scope:	public
+ * Arguments:	argc,argv:
+ *		  - optional path to target to test
+ * Returns:	int
+ *			== 0 - success
+ *			!= 0 - failure
+ * IMPLEMENTATION:
+ *  - must not be initial installation to the install root
+ *  - must not be installation of a zone
+ *  - must not be a whole root non-global zone
+ *  - must not be a non-global zone
+ *  - must not be a mounted mini-root
+ *  - must not be a netinstall image
+ *  - must not be a boot environment
+ *  - The package "SUNWdclnt" must be installed at "/"
+ *  - The root path must not be "/"
+ *  - The path "/export/exec/Solaris_\*\/usr" must exist at "/"
+ *  - The directory "$ROOTDIR/../templates" must exist
+ */
+
+static int
+cmd_is_diskless_client(int argc, char **argv, GLOBALDATA_T *a_gdt)
+{
+	char	*rootPath = NULL;
+	char	cmd[MAXPATHLEN+1];
+	int	c;
+	int	r;
+	int	rc;
+static	char	*cmdName = "is_diskless_client";
+static	int	recursion = 0;
+
+	/* process any command line options */
+
+	while ((c = getopt(argc, argv, ":")) != EOF) {
+		switch (c) {
+		case '\0':	/* prevent end-of-loop not reached warning */
+			break;
+		case '?':
+		default:
+			(void) usage(MSG_IS_INVALID_OPTION, optopt, cmdName);
+			return (R_USAGE);
+		}
+	}
+
+	/* prevent recursion */
+
+	if (recursionCheck(&recursion, cmdName) == B_FALSE) {
+
+		/*
+		 * a diskless client cannot be any of the following
+		 */
+
+		/* cannot be whole root non-global zone */
+
+		r = cmd_is_whole_root_ng_zone(argc, argv, a_gdt);
+
+		/* cannot be nonglobal zone */
+
+		if (r != R_SUCCESS) {
+			r = cmd_is_nonglobal_zone(argc, argv, a_gdt);
+		}
+
+		/* cannot be mounted miniroot */
+
+		if (r != R_SUCCESS) {
+			r = cmd_is_mounted_miniroot(argc, argv, a_gdt);
+		}
+
+		/* cannot be a netinstall image */
+
+		if (r != R_SUCCESS) {
+			r = cmd_is_netinstall_image(argc, argv, a_gdt);
+		}
+
+		/* cannot be a boot environment */
+
+		if (r != R_SUCCESS) {
+			r = cmd_is_boot_environment(argc, argv, a_gdt);
+		}
+
+		/* no need to guard against recursion any more */
+
+		recursion--;
+
+		/* return failure if any of the preceeding are true */
+
+		switch (r) {
+			case R_SUCCESS:
+				return (R_FAILURE);
+			case R_FAILURE:
+				break;
+			case R_USAGE:
+			case R_ERROR:
+			default:
+				return (r);
+		}
+	}
+
+	/* normalize argc/argv */
+
+	argc -= optind;
+	argv += optind;
+
+	/* error if more than one argument */
+
+	if (argc > 1) {
+		log_msg(LOG_MSG_ERR, ERR_UNRECOGNIZED_OPTION, argv[1]);
+		(void) usage(MSG_IS_INVALID_OPTION, argv[1]);
+		return (R_USAGE);
+	}
+
+	/* process root path if first argument present */
+
+	if (argc == 1) {
+		if (setRootPath(argv[0], "argv[0]", B_TRUE) != R_SUCCESS) {
+			return (R_ERROR);
+		}
+	}
+
+	/* get current root path */
+
+	r = getRootPath(&rootPath);
+	if (r != R_SUCCESS) {
+		return (r);
+	}
+
+	/* start of command debugging information */
+
+	echoDebug(DBG_ROOTPATH_IS, rootPath);
+
+	/* SUNWdclnt must be installed */
+
+	if (pkgTestInstalled("SUNWdclnt", "/") != B_TRUE) {
+		log_msg(LOG_MSG_DEBUG, DBG_IDLC_PKG_NOT_INSTALLED,
+			rootPath, "SUNWdclnt", "/");
+		return (R_FAILURE);
+	}
+
+	/*   - $ROOTDIR must not be "/" */
+
+	if (strcmp(rootPath, "/") == 0) {
+		log_msg(LOG_MSG_DEBUG, DBG_IDLC_ROOTPATH_BAD, rootPath, "/");
+		return (R_FAILURE);
+	}
+
+	/*   - zone name must be global */
+
+	if (strcmp(a_gdt->gd_zoneName, GLOBAL_ZONENAME) != 0) {
+		log_msg(LOG_MSG_DEBUG, DBG_IDLC_ZONE_BAD, rootPath,
+			GLOBAL_ZONENAME);
+		return (R_FAILURE);
+	}
+
+	/*
+	 * /export/exec/Solaris_"*"/usr must exist;
+	 * create ls command to test:
+	 * /usr/bin/ls /export/exec/Solaris_"*"/usr
+	 */
+
+	(void) snprintf(cmd, sizeof (cmd), "%s %s >/dev/null 2>&1",
+		LS_CMD, "/export/exec/Solaris_*/usr");
+
+	/* execute command */
+
+	rc = system(cmd);
+
+	/* return error if ls returns something other than "0" */
+
+	if (rc != 0) {
+		log_msg(LOG_MSG_DEBUG, DBG_IDLC_PATH_MISSING,
+			rootPath, "/export/exec/Solaris_*/usr");
+		return (R_FAILURE);
+	}
+
+	/*
+	 * /usr must be empty on a diskless client:
+	 * create ls command to test:
+	 * /usr/bin/ls -d1 $ROOTDIR/usr/\*
+	 */
+	(void) snprintf(cmd, sizeof (cmd), "%s %s %s/%s >/dev/null 2>&1",
+		LS_CMD, "-1d", rootPath, "usr/*");
+
+	/* execute command */
+
+	rc = system(cmd);
+
+	/* return error if ls returns "0" */
+
+	if (rc == 0) {
+		log_msg(LOG_MSG_DEBUG, DBG_IDLC_USR_IS_NOT_EMPTY,
+			rootPath);
+		return (R_FAILURE);
+	}
+
+	/* there must be a templates directory at ${ROOTPATH}/../templates */
+
+	r = testPath(TEST_EXISTS|TEST_IS_DIRECTORY,
+		"%s/%s", rootPath, "../templates");
+	if (r != R_SUCCESS) {
+		log_msg(LOG_MSG_DEBUG, DBG_IDLC_NO_TEMPLATES_PATH,
+			rootPath, rootPath, "../templates");
+		return (R_FAILURE);
+	}
+
+	/* must not be initial installation to the install root */
+
+	if ((a_gdt->gd_initialInstall == B_TRUE) &&
+	    (strcmp(a_gdt->gd_installRoot, rootPath) == 0)) {
+		/* initial install: install root cannot be diskless client */
+		log_msg(LOG_MSG_DEBUG, DBG_IDLC_INITIAL_INSTALL, rootPath);
+		return (R_FAILURE);
+	}
+
+	/* must not be installation of a zone */
+
+	if ((a_gdt->gd_globalZoneInstall == B_TRUE) ||
+	    (a_gdt->gd_nonglobalZoneInstall == B_TRUE)) {
+		/* initial zone install: no path can be diskless client */
+		log_msg(LOG_MSG_DEBUG, DBG_IDLC_ZONE_INSTALL, rootPath);
+		return (R_FAILURE);
+	}
+
+	/* the path is a diskless client */
+
+	log_msg(LOG_MSG_DEBUG, DBG_IDLC_PATH_IS_DISKLESS_CLIENT, rootPath);
+
+	return (R_SUCCESS);
+}
+
+/*
+ * Name:	cmd_is_global_zone
+ * Description:	determine if target is a global zone
+ * Scope:	public
+ * Arguments:	argc,argv:
+ *		  - optional path to target to test
+ * Returns:	int
+ *			== 0 - success
+ *			!= 0 - failure
+ * IMPLEMENTATION:
+ *  - must not be initial installation to the install root
+ *  - must not be installation of a non-global zone
+ *  - must not be a non-global zone
+ *  - must not be a mounted mini-root
+ *  - must not be a netinstall image
+ *  - must not be a diskless client
+ *  - if $ROOTDIR is "/":
+ *  -- if zone name is "GLOBAL", then is a global zone;
+ *  -- else not a global zone.
+ *  - $ROOTDIR/etc/zones must exist and be a directory
+ *  - $ROOTDIR/.tmp_proto must not exist
+ *  - $ROOTDIR/var must exist and must not be a symbolic link
+ */
+
+static int
+cmd_is_global_zone(int argc, char **argv, GLOBALDATA_T *a_gdt)
+{
+	char	*rootPath = NULL;
+	int	c;
+	int	r;
+static	char	*cmdName = "is_global_zone";
+static	int	recursion = 0;
+
+	/* process any command line options */
+
+	while ((c = getopt(argc, argv, ":")) != EOF) {
+		switch (c) {
+		case '\0':	/* prevent end-of-loop not reached warning */
+			break;
+		case '?':
+		default:
+			(void) usage(MSG_IS_INVALID_OPTION, optopt, cmdName);
+			return (R_USAGE);
+		}
+	}
+
+	/* prevent recursion */
+
+	if (recursionCheck(&recursion, cmdName) == B_FALSE) {
+
+		/*
+		 * a global zone cannot be any of the following
+		 */
+
+		/* cannot be a non-global zone */
+
+		r = cmd_is_nonglobal_zone(argc, argv, a_gdt);
+
+		/* cannot be a mounted miniroot */
+
+		if (r != R_SUCCESS) {
+			r = cmd_is_mounted_miniroot(argc, argv, a_gdt);
+		}
+
+		/* cannot be a netinstall image */
+
+		if (r != R_SUCCESS) {
+			r = cmd_is_netinstall_image(argc, argv, a_gdt);
+		}
+
+		/* cannot be a diskless client */
+
+		if (r != R_SUCCESS) {
+			r = cmd_is_diskless_client(argc, argv, a_gdt);
+		}
+
+		/* no need to guard against recursion any more */
+
+		recursion--;
+
+		/* return failure if any of the preceeding are true */
+
+		switch (r) {
+			case R_SUCCESS:
+				return (R_FAILURE);
+			case R_FAILURE:
+				break;
+			case R_USAGE:
+			case R_ERROR:
+			default:
+				return (r);
+		}
+	}
+
+	/* normalize argc/argv */
+
+	argc -= optind;
+	argv += optind;
+
+	/* error if more than one argument */
+
+	if (argc > 1) {
+		log_msg(LOG_MSG_ERR, ERR_UNRECOGNIZED_OPTION, argv[1]);
+		(void) usage(MSG_IS_INVALID_OPTION, argv[1]);
+		return (R_USAGE);
+	}
+
+	/* process root path if first argument present */
+
+	if (argc == 1) {
+		if (setRootPath(argv[0], "argv[0]", B_TRUE) != R_SUCCESS) {
+			return (R_ERROR);
+		}
+	}
+
+	/* get current root path */
+
+	r = getRootPath(&rootPath);
+	if (r != R_SUCCESS) {
+		return (r);
+	}
+
+	/* start of command debugging information */
+
+	echoDebug(DBG_ROOTPATH_IS, rootPath);
+
+	/* must not be initial installation to the install root */
+
+	if ((a_gdt->gd_initialInstall == B_TRUE) &&
+	    (strcmp(a_gdt->gd_installRoot, rootPath) == 0)) {
+		/* initial install: install root cannot be global zone */
+		log_msg(LOG_MSG_DEBUG, DBG_ISGZ_INITIAL_INSTALL, rootPath);
+		return (R_FAILURE);
+	}
+
+	/* must not be installation of a non-global zone */
+
+	if (a_gdt->gd_nonglobalZoneInstall == B_TRUE) {
+		/* initial nonglobal zone install: no path can be global zone */
+		log_msg(LOG_MSG_DEBUG, DBG_ISGZ_NGZ_ZONE_INSTALL, rootPath);
+		return (R_FAILURE);
+	}
+
+	/* handle if global zone installation to the install root */
+
+	if ((a_gdt->gd_globalZoneInstall == B_TRUE) &&
+	    (strcmp(a_gdt->gd_installRoot, rootPath) == 0)) {
+			/* the path is a global zone */
+
+			log_msg(LOG_MSG_DEBUG, DBG_ISGZ_PATH_IS_GLOBAL_ZONE,
+				rootPath);
+
+			return (R_SUCCESS);
+	}
+
+	/* true if current root is "/" and zone name is GLOBAL_ZONENAME */
+
+	if (strcmp(rootPath, "/") == 0) {
+		if (strcmp(a_gdt->gd_zoneName, GLOBAL_ZONENAME) == 0) {
+			/* the path is a global zone */
+
+			log_msg(LOG_MSG_DEBUG, DBG_ISGZ_PATH_IS_GLOBAL_ZONE,
+				rootPath);
+
+			return (R_SUCCESS);
+		}
+
+		/* inside a non-global zone */
+
+		log_msg(LOG_MSG_DEBUG, DBG_ISGZ_ZONENAME_ISNT_GLOBAL,
+			rootPath, a_gdt->gd_zoneName);
+
+		return (R_FAILURE);
+	}
+
+	/*
+	 * current root is not "/" - see if target looks like a global zone
+	 *
+	 * - rootpath is not "/"
+	 * - and $ROOTDIR/etc/zones exists
+	 * - and $ROOTDIR/.tmp_proto does not exist
+	 * - and $ROOTDIR/var is not a symbolic link
+	 */
+
+	/* not global zone if /etc/zones does not exist */
+
+	r = testPath(TEST_EXISTS|TEST_IS_DIRECTORY,
+		"%s/%s", rootPath, "/etc/zones");
+	if (r != R_SUCCESS) {
+		log_msg(LOG_MSG_DEBUG, DBG_ISGZ_PATH_ISNT_DIRECTORY,
+			rootPath, "/etc/zones");
+		return (R_FAILURE);
+	}
+
+	/* .tmp_proto must not exist */
+
+	r = testPath(TEST_NOT_EXISTS,
+		"%s/%s", rootPath, ".tmp_proto");
+	if (r != R_SUCCESS) {
+		log_msg(LOG_MSG_DEBUG, DBG_ISGZ_PATH_EXISTS,
+			rootPath, "/.tmp_proto");
+		return (R_FAILURE);
+	}
+
+	/* /var must not be a symbolic link */
+
+	r = testPath(TEST_EXISTS|TEST_NOT_SYMBOLIC_LINK,
+		"%s/%s", rootPath, "/var");
+	if (r != R_SUCCESS) {
+		log_msg(LOG_MSG_DEBUG, DBG_ISGZ_PATH_IS_SYMLINK,
+			rootPath, "/var");
+		return (R_FAILURE);
+	}
+
+	/* the path is a global zone */
+
+	log_msg(LOG_MSG_DEBUG, DBG_ISGZ_PATH_IS_GLOBAL_ZONE, rootPath);
+
+	return (R_SUCCESS);
+}
+
+/*
+ * Name:	cmd_is_netinstall_image
+ * Description:	determine if target is a net install image
+ * Scope:	public
+ * Arguments:	argc,argv:
+ *		  - optional path to target to test
+ * Returns:	int
+ *			== 0 - success
+ *			!= 0 - failure
+ * IMPLEMENTATION:
+ *  - must not be initial installation to the install root
+ *  - must not be installation of a zone
+ *  - must not be a global zone
+ *  - must not be a mounted mini-root
+ *  - zone name must be "global"
+ *  - $ROOTDIR/.tmp_proto must exist and must be a directory
+ *  - $ROOTDIR/var must exist and must be a symbolic link
+ *  - $ROOTDIR/tmp/kernel must exist and must be a directory
+ *  - $ROOTDIR/.tmp_proto/kernel must exist and must be a symbolic link
+ */
+
+static int
+cmd_is_netinstall_image(int argc, char **argv, GLOBALDATA_T *a_gdt)
+{
+	char	*rootPath = NULL;
+	int	c;
+	int	r;
+static	char	*cmdName = "is_netinstall_image";
+static	int	recursion = 0;
+
+	/* process any command line options */
+
+	while ((c = getopt(argc, argv, ":")) != EOF) {
+		switch (c) {
+		case '\0':	/* prevent end-of-loop not reached warning */
+			break;
+		case '?':
+		default:
+			(void) usage(MSG_IS_INVALID_OPTION, optopt, cmdName);
+			return (R_USAGE);
+		}
+	}
+
+	/* prevent recursion */
+
+	if (recursionCheck(&recursion, cmdName) == B_FALSE) {
+
+		/* a netinstall image cannot be a global zone */
+
+		r = cmd_is_global_zone(argc, argv, a_gdt);
+
+		/* no need to guard against recursion any more */
+
+		recursion--;
+
+		switch (r) {
+			case R_SUCCESS:
+				return (R_FAILURE);
+			case R_FAILURE:
+				break;
+			case R_USAGE:
+			case R_ERROR:
+			default:
+				return (r);
+		}
+	}
+
+	/* normalize argc/argv */
+
+	argc -= optind;
+	argv += optind;
+
+	/* error if more than one argument */
+
+	if (argc > 1) {
+		log_msg(LOG_MSG_ERR, ERR_UNRECOGNIZED_OPTION, argv[1]);
+		(void) usage(MSG_IS_INVALID_OPTION, argv[1]);
+		return (R_USAGE);
+	}
+
+	/* process root path if first argument present */
+
+	if (argc == 1) {
+		if (setRootPath(argv[0], "argv[0]", B_TRUE) != R_SUCCESS) {
+			return (R_ERROR);
+		}
+	}
+
+	/* get current root path */
+
+	r = getRootPath(&rootPath);
+	if (r != R_SUCCESS) {
+		return (r);
+	}
+
+	/* start of command debugging information */
+
+	echoDebug(DBG_ROOTPATH_IS, rootPath);
+
+	/* current zone name must be "global" */
+
+	if (strcmp(a_gdt->gd_zoneName, GLOBAL_ZONENAME) != 0) {
+		log_msg(LOG_MSG_DEBUG, DBG_INIM_BAD_CURRENT_ZONE,
+			rootPath, GLOBAL_ZONENAME);
+		return (R_FAILURE);
+	}
+
+	/* cannot be a mounted_miniroot */
+
+	if (cmd_is_mounted_miniroot(argc, argv, a_gdt) == R_SUCCESS) {
+		log_msg(LOG_MSG_DEBUG, DBG_IMRT_PATH_IS_MOUNTED_MINIROOT,
+			rootPath);
+		return (R_FAILURE);
+	}
+
+	/* $ROOTDIR/.tmp_proto exists */
+
+	r = testPath(TEST_EXISTS|TEST_IS_DIRECTORY,
+		"%s/%s", rootPath, ".tmp_proto");
+	if (r != R_SUCCESS) {
+		log_msg(LOG_MSG_DEBUG, DBG_INIM_PATH_ISNT_DIRECTORY,
+			rootPath, "/.tmp_proto");
+		return (R_FAILURE);
+	}
+
+	/* $ROOTDIR/var is a symbolic link */
+
+	r = testPath(TEST_IS_SYMBOLIC_LINK,
+		"%s/%s", rootPath, "/var");
+	if (r != R_SUCCESS) {
+		log_msg(LOG_MSG_DEBUG, DBG_INIM_PATH_ISNT_SYMLINK,
+			rootPath, "/var");
+		return (R_FAILURE);
+	}
+
+	/* $ROOTDIR/tmp/kernel does exist */
+
+	r = testPath(TEST_EXISTS|TEST_IS_DIRECTORY,
+		"%s/%s", rootPath, "/tmp/kernel");
+	if (r != R_SUCCESS) {
+		log_msg(LOG_MSG_DEBUG, DBG_INIM_PATH_ISNT_DIRECTORY,
+			rootPath, "/tmp/kernel");
+		return (R_FAILURE);
+	}
+
+	/* $ROOTDIR/.tmp_proto/kernel is a symbolic link */
+
+	r = testPath(TEST_IS_SYMBOLIC_LINK,
+		"%s/%s", rootPath, "/.tmp_proto/kernel");
+	if (r != R_SUCCESS) {
+		log_msg(LOG_MSG_DEBUG, DBG_INIM_PATH_ISNT_SYMLINK,
+			rootPath, "/.tmp_proto/kernel");
+		return (R_FAILURE);
+	}
+
+	/* must not be initial installation to the install root */
+
+	if ((a_gdt->gd_initialInstall == B_TRUE) &&
+	    (strcmp(a_gdt->gd_installRoot, rootPath) == 0)) {
+		/* initial install: install root cannot be netinstall image */
+		log_msg(LOG_MSG_DEBUG, DBG_INIM_INITIAL_INSTALL, rootPath);
+		return (R_FAILURE);
+	}
+
+	/* must not be installation of a zone */
+
+	if ((a_gdt->gd_globalZoneInstall == B_TRUE) ||
+	    (a_gdt->gd_nonglobalZoneInstall == B_TRUE)) {
+		/* initial zone install: no path can be netinstall image */
+		log_msg(LOG_MSG_DEBUG, DBG_INIM_ZONE_INSTALL, rootPath);
+		return (R_FAILURE);
+	}
+
+	/* target is a netinstall image */
+
+	log_msg(LOG_MSG_DEBUG, DBG_INIM_PATH_IS_NETINSTALL_IMAGE, rootPath);
+
+	return (R_SUCCESS);
+}
+
+/*
+ * Name:	cmd_is_mounted_miniroot
+ * Description:	determine if target is a mounted miniroot image
+ * Scope:	public
+ * Arguments:	argc,argv:
+ *		  - optional path to target to test
+ * Returns:	int
+ *			== 0 - success
+ *			!= 0 - failure
+ * IMPLEMENTATION:
+ *  - must not be initial installation to the install root
+ *  - must not be installation of a zone
+ *  - zone name must be "global"
+ *  - $ROOTDIR/tmp/kernel must exist and must be a symbolic link
+ *  - $ROOTDIR/tmp/root/kernel must exist and must be a directory
+ */
+
+static int
+cmd_is_mounted_miniroot(int argc, char **argv, GLOBALDATA_T *a_gdt)
+{
+	char	*rootPath = NULL;
+	int	c;
+	int	r;
+static	char	*cmdName = "is_mounted_miniroot";
+static	int	recursion = 0;
+
+	/* process any command line options */
+
+	while ((c = getopt(argc, argv, ":")) != EOF) {
+		switch (c) {
+		case '\0':	/* prevent end-of-loop not reached warning */
+			break;
+		case '?':
+		default:
+			(void) usage(MSG_IS_INVALID_OPTION, optopt, cmdName);
+			return (R_USAGE);
+		}
+	}
+
+	/* prevent recursion */
+
+	if (recursionCheck(&recursion, cmdName) == B_FALSE) {
+		recursion--;
+	}
+
+	/* normalize argc/argv */
+
+	argc -= optind;
+	argv += optind;
+
+	/* error if more than one argument */
+
+	if (argc > 1) {
+		log_msg(LOG_MSG_ERR, ERR_UNRECOGNIZED_OPTION, argv[1]);
+		(void) usage(MSG_IS_INVALID_OPTION, argv[1]);
+		return (R_USAGE);
+	}
+
+	/* process root path if first argument present */
+
+	if (argc == 1) {
+		if (setRootPath(argv[0], "argv[0]", B_TRUE) != R_SUCCESS) {
+			return (R_ERROR);
+		}
+	}
+
+	/* get current root path */
+
+	r = getRootPath(&rootPath);
+	if (r != R_SUCCESS) {
+		return (r);
+	}
+
+	/* start of command debugging information */
+
+	echoDebug(DBG_ROOTPATH_IS, rootPath);
+
+	/* current zone name must be "global" */
+
+	if (strcmp(a_gdt->gd_zoneName, GLOBAL_ZONENAME) != 0) {
+		log_msg(LOG_MSG_DEBUG, DBG_IMRT_BAD_CURRENT_ZONE,
+			rootPath, GLOBAL_ZONENAME);
+		return (R_FAILURE);
+	}
+
+	/* $ROOTDIR/tmp/kernel is a symbolic link */
+
+	r = testPath(TEST_IS_SYMBOLIC_LINK,
+		"%s/%s", rootPath, "/tmp/kernel");
+	if (r != R_SUCCESS) {
+		log_msg(LOG_MSG_DEBUG, DBG_IMRT_PATH_ISNT_SYMLINK,
+			rootPath, "/tmp/kernel");
+		return (R_FAILURE);
+	}
+
+	/* $ROOTDIR/tmp/root/kernel is a directory */
+
+	r = testPath(TEST_EXISTS|TEST_IS_DIRECTORY,
+		"%s/%s", rootPath, "/tmp/root/kernel");
+	if (r != R_SUCCESS) {
+		log_msg(LOG_MSG_DEBUG, DBG_IMRT_PATH_ISNT_DIRECTORY,
+			rootPath, "/tmp/root/kernel");
+		return (R_FAILURE);
+	}
+
+	/* must not be initial installation to the install root */
+
+	if ((a_gdt->gd_initialInstall == B_TRUE) &&
+	    (strcmp(a_gdt->gd_installRoot, rootPath) == 0)) {
+		/* initial install: install root cannot be mounted miniroot */
+		log_msg(LOG_MSG_DEBUG, DBG_IMRT_INITIAL_INSTALL, rootPath);
+		return (R_FAILURE);
+	}
+
+	/* must not be installation of a zone */
+
+	if ((a_gdt->gd_globalZoneInstall == B_TRUE) ||
+	    (a_gdt->gd_nonglobalZoneInstall == B_TRUE)) {
+		/* initial zone install: no path can be mounted miniroot */
+		log_msg(LOG_MSG_DEBUG, DBG_IMRT_ZONE_INSTALL, rootPath);
+		return (R_FAILURE);
+	}
+
+	/* target is a mounted miniroot */
+
+	log_msg(LOG_MSG_DEBUG, DBG_IMRT_PATH_IS_MOUNTED_MINIROOT, rootPath);
+
+	return (R_SUCCESS);
+}
+
+/*
+ * Name:	cmd_is_nonglobal_zone
+ * Description:	determine if target is a global zone
+ * Scope:	public
+ * Arguments:	argc,argv:
+ *		  - optional path to target to test
+ * Returns:	int
+ *			== 0 - success
+ *			!= 0 - failure
+ *  - must not be initial installation to the install root
+ *  - must not be installation of a global zone
+ *  - success if installation of a non-global zone
+ */
+
+static int
+cmd_is_nonglobal_zone(int argc, char **argv, GLOBALDATA_T *a_gdt)
+{
+	char	*rootPath = NULL;
+	int	c;
+	int	r;
+static	char	*cmdName = "is_nonglobal_zone";
+static	int	recursion = 0;
+
+	/* process any command line options */
+
+	while ((c = getopt(argc, argv, ":")) != EOF) {
+		switch (c) {
+		case '\0':	/* prevent end-of-loop not reached warning */
+			break;
+		case '?':
+		default:
+			(void) usage(MSG_IS_INVALID_OPTION, optopt, cmdName);
+			return (R_USAGE);
+		}
+	}
+
+	/* prevent recursion */
+
+	if (recursionCheck(&recursion, cmdName) == B_FALSE) {
+		recursion--;
+	}
+
+	/* normalize argc/argv */
+
+	argc -= optind;
+	argv += optind;
+
+	/* error if more than one argument */
+
+	if (argc > 1) {
+		log_msg(LOG_MSG_ERR, ERR_UNRECOGNIZED_OPTION, argv[1]);
+		(void) usage(MSG_IS_INVALID_OPTION, argv[1]);
+		return (R_USAGE);
+	}
+
+	/* process root path if first argument present */
+
+	if (argc == 1) {
+		if (setRootPath(argv[0], "argv[0]", B_TRUE) != R_SUCCESS) {
+			return (R_ERROR);
+		}
+	}
+
+	/* get current root path */
+
+	r = getRootPath(&rootPath);
+	if (r != R_SUCCESS) {
+		return (r);
+	}
+
+	/* start of command debugging information */
+
+	echoDebug(DBG_ROOTPATH_IS, rootPath);
+
+	/* handle if non-global zone installation to the install root */
+
+	if ((a_gdt->gd_nonglobalZoneInstall == B_TRUE) &&
+	    (strcmp(a_gdt->gd_installRoot, rootPath) == 0)) {
+		log_msg(LOG_MSG_DEBUG, DBG_NGZN_INSTALL_ZONENAME_IS_NGZ,
+			rootPath, a_gdt->gd_zoneName);
+		return (R_SUCCESS);
+	}
+
+	/* must not be initial installation to the install root */
+
+	if ((a_gdt->gd_initialInstall == B_TRUE) &&
+	    (strcmp(a_gdt->gd_installRoot, rootPath) == 0)) {
+		/* initial install: install root cannot be non-global zone */
+		log_msg(LOG_MSG_DEBUG, DBG_NGZN_INITIAL_INSTALL, rootPath);
+		return (R_FAILURE);
+	}
+
+	/* must not be installation of a global zone */
+
+	if ((a_gdt->gd_globalZoneInstall == B_TRUE) ||
+	    (a_gdt->gd_nonglobalZoneInstall == B_TRUE)) {
+		/* initial global zone install: no path can be nonglobal zone */
+		log_msg(LOG_MSG_DEBUG, DBG_NGZN_GLOBAL_ZONE_INSTALL, rootPath);
+		return (R_FAILURE);
+	}
+
+	/*
+	 * *********************************************************************
+	 * if root directory is "/" then the only thing that needs to be done is
+	 * to test the zone name directly - if the zone name is "global" then
+	 * the target is not a non-global zone; otherwise if the zone name is
+	 * not "global" then the target IS a non-global zone.
+	 * *********************************************************************
+	 */
+
+	if (strcmp(rootPath, "/") == 0) {
+		/* target is current running root */
+		if (strcmp(a_gdt->gd_zoneName, GLOBAL_ZONENAME) == 0) {
+			/* in the global zone */
+			log_msg(LOG_MSG_DEBUG, DBG_NGZN_ZONENAME_ISNT_NGZ,
+				rootPath, a_gdt->gd_zoneName);
+			return (R_FAILURE);
+		}
+		/* in a non-global zone */
+		log_msg(LOG_MSG_DEBUG, DBG_NGZN_ZONENAME_IS_NGZ,
+			rootPath, a_gdt->gd_zoneName);
+		return (R_SUCCESS);
+	}
+
+	/*
+	 * $ROOTDIR/etc/zones/index must exist in a global zone. It also
+	 * exists in a non-global zone after s10u4 but we can't check that
+	 * since it is undeterministic for all releases so we only check
+	 * for the global zone here.
+	 */
+
+	r = testPath(TEST_EXISTS, "%s/%s", rootPath, "/etc/zones/index");
+	if (r == R_SUCCESS) {
+
+		/* See if "global" exists in .../etc/zones/index */
+
+		if (testPath(TEST_GLOBAL_TOKEN_IN_FILE, "%s/%s", rootPath,
+		    "/etc/zones/index") != R_SUCCESS) {
+			log_msg(LOG_MSG_DEBUG, DBG_NGZN_ZONENAME_ISNT_NGZ,
+			    rootPath, GLOBAL_ZONENAME);
+			return (R_FAILURE);
+		}
+	}
+
+	/*
+	 * *********************************************************************
+	 * If the root directory is "/" then you can use only the zone
+	 * name to determine if the zone is non-global or not since the
+	 * package is being installed or removed to the current "zone".
+	 *
+	 * Since the root directory being tested is not "/" then you have to
+	 * look into the target to try and infer zone type using means other
+	 * than the zone name only.
+	 * *********************************************************************
+	 */
+
+	/* reject if any items found that cannot be in a non-global zone */
+
+	/* .tmp_proto must not exist */
+
+	r = testPath(TEST_NOT_EXISTS, "%s/%s", rootPath, ".tmp_proto");
+	if (r != R_SUCCESS) {
+		/* $R/.tmp_proto cannot exist in a non-global zone */
+		log_msg(LOG_MSG_DEBUG, DBG_NGZN_PATH_EXISTS,
+			rootPath, "/.tmp_proto");
+		return (R_FAILURE);
+	}
+
+	/* /var must not be a symbolic link */
+
+	r = testPath(TEST_EXISTS|TEST_NOT_SYMBOLIC_LINK,
+		"%s/%s", rootPath, "/var");
+	if (r != R_SUCCESS) {
+		/* $R/var cannot be a symbolic link in a non-global zone */
+		log_msg(LOG_MSG_DEBUG, DBG_NGZN_PATH_DOES_NOT_EXIST,
+			rootPath, "/var");
+		return (R_FAILURE);
+	}
+
+	/* $ROOTDIR/tmp/root/kernel must not exist */
+
+	r = testPath(TEST_NOT_EXISTS,
+		"%s/%s", rootPath, "/tmp/root/kernel");
+	if (r != R_SUCCESS) {
+		/* $R/tmp/root/kernel cannot exist in a non-global zone */
+		log_msg(LOG_MSG_DEBUG, DBG_NGZN_PATH_EXISTS,
+			rootPath, "/tmp/root/kernel");
+		return (R_FAILURE);
+	}
+
+	/*
+	 * *********************************************************************
+	 * no items exist in $ROOTDIR that identify something other than
+	 * a non-global zone.
+	 *
+	 * if in global zone no more tests possible: is a non-global zone
+	 * *********************************************************************
+	 */
+
+	if (strcmp(a_gdt->gd_zoneName, GLOBAL_ZONENAME) == 0) {
+		/* in the global zone */
+		log_msg(LOG_MSG_DEBUG, DBG_NGZN_IN_GZ_IS_NONGLOBAL_ZONE,
+			rootPath);
+		return (R_SUCCESS);
+	}
+
+	/*
+	 * *********************************************************************
+	 * In non-global zone: interrogate zone name and type.
+	 *
+	 * The parent zone is the zone that the "pkgadd" or "pkgrm" command was
+	 * run in. The child zone is the zone that the "pkginstall" or
+	 * "pkgremove" command was run in.
+	 * *********************************************************************
+	 */
+
+	/*
+	 * If parent zone name and current zone name defined, and
+	 * both zone names are the same, since pkgcond is running
+	 * inside of a non-global zone, this is how the scratch
+	 * zone is implemented, so target is a non-global zone
+	 */
+
+	if ((a_gdt->gd_parentZoneName != NULL) &&
+		(a_gdt->gd_currentZoneName != NULL) &&
+		(strcmp(a_gdt->gd_parentZoneName,
+					a_gdt->gd_currentZoneName) == 0)) {
+			/* parent and current zone name identical: non-gz */
+			log_msg(LOG_MSG_DEBUG, DBG_NGZN_PARENT_CHILD_SAMEZONE,
+				rootPath, a_gdt->gd_parentZoneName);
+			return (R_SUCCESS);
+	}
+
+	/*
+	 * In non-global zone if inherited FS's exits
+	 * or zone specific read only FS's exist
+	 * or it is in a mounted state.
+	 */
+
+	if (a_gdt->gd_inheritedFileSystems != NULL ||
+		a_gdt->gd_srFsMountedRO || a_gdt->inMountedState) {
+		log_msg(LOG_MSG_DEBUG, DBG_NGZN_IS_NONGLOBAL_ZONE, rootPath);
+		return (R_SUCCESS);
+	}
+
+	/*
+	 * the parent and current zone name are not the same;
+	 * interrogate the zone types: the parent must be global
+	 * and the current must be non-global, which would be set
+	 * when a package command is run in the global zone that in
+	 * turn runs a package command within the non-global zone.
+	 */
+
+	/* if defined, parent zone type must be "global" */
+
+	if ((a_gdt->gd_parentZoneType != NULL) &&
+		(strcmp(a_gdt->gd_parentZoneType, "nonglobal") == 0)) {
+		log_msg(LOG_MSG_DEBUG, DBG_NGZN_BAD_PARENT_ZONETYPE,
+			rootPath, "nonglobal");
+		return (R_FAILURE);
+	}
+
+	/* if defined, current zone type must be "nonglobal" */
+
+	if ((a_gdt->gd_currentZoneType != NULL) &&
+		(strcmp(a_gdt->gd_currentZoneType, GLOBAL_ZONENAME) == 0)) {
+		log_msg(LOG_MSG_DEBUG, DBG_NGZN_BAD_CURRENT_ZONETYPE,
+			rootPath, GLOBAL_ZONENAME);
+		return (R_FAILURE);
+	}
+
+	/*
+	 * *********************************************************************
+	 * no other tests possible: target is a non-global zone
+	 * *********************************************************************
+	 */
+
+	log_msg(LOG_MSG_DEBUG, DBG_NGZN_IS_NONGLOBAL_ZONE, rootPath);
+
+	return (R_SUCCESS);
+}
+
+/*
+ * Name:	cmd_is_running_system
+ * Description:	determine if target is a global zone
+ * Scope:	public
+ * Arguments:	argc,argv:
+ *		  - optional path to target to test
+ * Returns:	int
+ *			== 0 - success
+ *			!= 0 - failure
+ * IMPLEMENTATION:
+ *  - must not be initial installation to the install root
+ *  - must not be installation of a zone
+ *  - must not be a diskless client
+ *  - $ROOTDIR must be "/"
+ *  - zone name must be "global"
+ */
+
+static int
+cmd_is_running_system(int argc, char **argv, GLOBALDATA_T *a_gdt)
+{
+	char	*rootPath = NULL;
+	int	c;
+	int	r;
+static	char	*cmdName = "is_running_system";
+static	int	recursion = 0;
+
+	/* process any command line options */
+
+	while ((c = getopt(argc, argv, ":")) != EOF) {
+		switch (c) {
+		case '\0':	/* prevent end-of-loop not reached warning */
+			break;
+		case '?':
+		default:
+			(void) usage(MSG_IS_INVALID_OPTION, optopt, cmdName);
+			return (R_USAGE);
+		}
+	}
+
+	/* prevent recursion */
+
+	if (recursionCheck(&recursion, cmdName) == B_FALSE) {
+
+		/* a running system cannot be a diskless client */
+
+		r = cmd_is_diskless_client(argc, argv, a_gdt);
+
+		/* no need to guard against recursion any more */
+
+		recursion--;
+
+		switch (r) {
+			case R_SUCCESS:
+				return (R_FAILURE);
+			case R_FAILURE:
+				break;
+			case R_USAGE:
+			case R_ERROR:
+			default:
+				return (r);
+		}
+	}
+
+	/* normalize argc/argv */
+
+	argc -= optind;
+	argv += optind;
+
+	/* error if more than one argument */
+
+	if (argc > 1) {
+		log_msg(LOG_MSG_ERR, ERR_UNRECOGNIZED_OPTION, argv[1]);
+		(void) usage(MSG_IS_INVALID_OPTION, argv[1]);
+		return (R_USAGE);
+	}
+
+	/* process root path if first argument present */
+
+	if (argc == 1) {
+		if (setRootPath(argv[0], "argv[0]", B_TRUE) != R_SUCCESS) {
+			return (R_ERROR);
+		}
+	}
+
+	/* get current root path */
+
+	r = getRootPath(&rootPath);
+	if (r != R_SUCCESS) {
+		return (r);
+	}
+
+	/* start of command debugging information */
+
+	echoDebug(DBG_ROOTPATH_IS, rootPath);
+
+	/* if root path is "/" then check zone name */
+
+	if (strcmp(rootPath, "/") != 0) {
+		log_msg(LOG_MSG_DEBUG, DBG_IRST_ROOTPATH_BAD, rootPath, "/");
+		return (R_FAILURE);
+	}
+
+	/* zone name must be global */
+
+	if (strcmp(a_gdt->gd_zoneName, GLOBAL_ZONENAME) != 0) {
+		log_msg(LOG_MSG_DEBUG, DBG_IRST_ZONE_BAD, rootPath,
+			GLOBAL_ZONENAME);
+		return (R_FAILURE);
+	}
+
+	/* must not be initial installation to the install root */
+
+	if ((a_gdt->gd_initialInstall == B_TRUE) &&
+	    (strcmp(a_gdt->gd_installRoot, rootPath) == 0)) {
+		/* initial install: install root cannot be the running system */
+		log_msg(LOG_MSG_DEBUG, DBG_IRST_INITIAL_INSTALL, rootPath);
+		return (R_FAILURE);
+	}
+
+	/* must not be installation of a zone */
+
+	if ((a_gdt->gd_globalZoneInstall == B_TRUE) ||
+	    (a_gdt->gd_nonglobalZoneInstall == B_TRUE)) {
+		/* initial zone install: no path can be running system */
+		log_msg(LOG_MSG_DEBUG, DBG_IRST_ZONE_INSTALL, rootPath);
+		return (R_FAILURE);
+	}
+
+	/* target is a running system */
+
+	log_msg(LOG_MSG_DEBUG, DBG_IRST_PATH_IS_RUNNING_SYSTEM, rootPath);
+
+	return (R_SUCCESS);
+}
+
+/*
+ * Name:	cmd_can_add_driver
+ * Description:	determine if target is a global zone
+ * Scope:	public
+ * Arguments:	argc,argv:
+ *		  - optional path to target to test
+ * Returns:	int
+ *			== 0 - success
+ *			!= 0 - failure
+ * Implementation:
+ * A driver can be added to the system if the components of a Solaris
+ * instance capable of loading drivers is present and it is not the
+ * currently running system.
+ */
+
+static int
+cmd_can_add_driver(int argc, char **argv, GLOBALDATA_T *a_gdt)
+{
+	char	*rootPath = NULL;
+	int	c;
+	int	r;
+static	char	*cmdName = "can_add_driver";
+static	int	recursion = 0;
+
+	/* process any command line options */
+
+	while ((c = getopt(argc, argv, ":")) != EOF) {
+		switch (c) {
+		case '\0':	/* prevent end-of-loop not reached warning */
+			break;
+		case '?':
+		default:
+			(void) usage(MSG_IS_INVALID_OPTION, optopt, cmdName);
+			return (R_USAGE);
+		}
+	}
+
+	/* prevent recursion */
+
+	if (recursionCheck(&recursion, cmdName) == B_FALSE) {
+
+		/* see if this is the current running system */
+
+		r = cmd_is_running_system(argc, argv, a_gdt);
+
+		/* cannot be a diskless client */
+
+		if (r != R_SUCCESS) {
+			r = cmd_is_diskless_client(argc, argv, a_gdt);
+		}
+
+		/* no need to guard against recursion any more */
+
+		recursion--;
+
+		switch (r) {
+			case R_SUCCESS:
+				/* is a running system */
+				return (R_FAILURE);
+			case R_FAILURE:
+				/* not a running syste */
+				break;
+			case R_USAGE:
+			case R_ERROR:
+			default:
+				/* cannot determine if is a running system */
+				return (r);
+		}
+	}
+
+	/* normalize argc/argv */
+
+	argc -= optind;
+	argv += optind;
+
+	/* error if more than one argument */
+
+	if (argc > 1) {
+		log_msg(LOG_MSG_ERR, ERR_UNRECOGNIZED_OPTION, argv[1]);
+		(void) usage(MSG_IS_INVALID_OPTION, argv[1]);
+		return (R_USAGE);
+	}
+
+	/* process root path if first argument present */
+
+	if (argc == 1) {
+		if (setRootPath(argv[0], "argv[0]", B_TRUE) != R_SUCCESS) {
+			return (R_ERROR);
+		}
+	}
+
+	/* get current root path */
+
+	r = getRootPath(&rootPath);
+	if (r != R_SUCCESS) {
+		return (r);
+	}
+
+	/* start of command debugging information */
+
+	echoDebug(DBG_ROOTPATH_IS, rootPath);
+
+	/* /etc must exist and must not be a symbolic link */
+
+	r = testPath(TEST_EXISTS|TEST_NOT_SYMBOLIC_LINK,
+		"%s/%s", rootPath, "/etc");
+	if (r != R_SUCCESS) {
+		log_msg(LOG_MSG_DEBUG, DBG_ADDV_PATH_IS_SYMLINK,
+			rootPath, "/etc");
+		return (R_FAILURE);
+	}
+
+	/* /platform must exist and must not be a symbolic link */
+
+	r = testPath(TEST_EXISTS|TEST_NOT_SYMBOLIC_LINK,
+		"%s/%s", rootPath, "/platform");
+	if (r != R_SUCCESS) {
+		log_msg(LOG_MSG_DEBUG, DBG_ADDV_PATH_IS_SYMLINK,
+			rootPath, "/platform");
+		return (R_FAILURE);
+	}
+
+	/* /kernel must exist and must not be a symbolic link */
+
+	r = testPath(TEST_EXISTS|TEST_NOT_SYMBOLIC_LINK,
+		"%s/%s", rootPath, "/kernel");
+	if (r != R_SUCCESS) {
+		log_msg(LOG_MSG_DEBUG, DBG_ADDV_PATH_IS_SYMLINK,
+			rootPath, "/kernel");
+		return (R_FAILURE);
+	}
+
+	/* can add a driver */
+
+	log_msg(LOG_MSG_DEBUG, DBG_ADDV_YES, rootPath);
+
+	return (R_SUCCESS);
+}
+
+/*
+ * Name:	cmd_can_update_driver
+ * Description:	determine if target is a global zone
+ * Scope:	public
+ * Arguments:	argc,argv:
+ *		  - optional path to target to test
+ * Returns:	int
+ *			== 0 - success
+ *			!= 0 - failure
+ * Implementation:
+ * A driver can be added to the system if the components of a Solaris
+ * instance capable of loading drivers is present and it is not the
+ * currently running system.
+ */
+
+static int
+cmd_can_update_driver(int argc, char **argv, GLOBALDATA_T *a_gdt)
+{
+	char	*rootPath = NULL;
+	int	c;
+	int	r;
+static	char	*cmdName = "can_update_driver";
+static	int	recursion = 0;
+
+	/* process any command line options */
+
+	while ((c = getopt(argc, argv, ":")) != EOF) {
+		switch (c) {
+		case '\0':	/* prevent end-of-loop not reached warning */
+			break;
+		case '?':
+		default:
+			(void) usage(MSG_IS_INVALID_OPTION, optopt, cmdName);
+			return (R_USAGE);
+		}
+	}
+
+	/* prevent recursion */
+
+	if (recursionCheck(&recursion, cmdName) == B_FALSE) {
+
+		/* see if this is the current running system */
+
+		r = cmd_is_running_system(argc, argv, a_gdt);
+
+		/* cannot be a diskless client */
+
+		if (r != R_SUCCESS) {
+			r = cmd_is_diskless_client(argc, argv, a_gdt);
+		}
+
+		/* no need to guard against recursion any more */
+
+		recursion--;
+
+		switch (r) {
+			case R_SUCCESS:
+				/* is a running system */
+				return (R_FAILURE);
+			case R_FAILURE:
+				/* not a running syste */
+				break;
+			case R_USAGE:
+			case R_ERROR:
+			default:
+				/* cannot determine if is a running system */
+				return (r);
+		}
+	}
+
+	/* normalize argc/argv */
+
+	argc -= optind;
+	argv += optind;
+
+	/* error if more than one argument */
+
+	if (argc > 1) {
+		log_msg(LOG_MSG_ERR, ERR_UNRECOGNIZED_OPTION, argv[1]);
+		(void) usage(MSG_IS_INVALID_OPTION, argv[1]);
+		return (R_USAGE);
+	}
+
+	/* process root path if first argument present */
+
+	if (argc == 1) {
+		if (setRootPath(argv[0], "argv[0]", B_TRUE) != R_SUCCESS) {
+			return (R_ERROR);
+		}
+	}
+
+	/* get current root path */
+
+	r = getRootPath(&rootPath);
+	if (r != R_SUCCESS) {
+		return (r);
+	}
+
+	/* start of command debugging information */
+
+	echoDebug(DBG_ROOTPATH_IS, rootPath);
+
+	/* /etc must exist and must not be a symbolic link */
+
+	r = testPath(TEST_EXISTS|TEST_NOT_SYMBOLIC_LINK,
+		"%s/%s", rootPath, "/etc");
+	if (r != R_SUCCESS) {
+		log_msg(LOG_MSG_DEBUG, DBG_UPDV_PATH_IS_SYMLINK,
+			rootPath, "/etc");
+		return (R_FAILURE);
+	}
+
+	/* /platform must exist and must not be a symbolic link */
+
+	r = testPath(TEST_EXISTS|TEST_NOT_SYMBOLIC_LINK,
+		"%s/%s", rootPath, "/platform");
+	if (r != R_SUCCESS) {
+		log_msg(LOG_MSG_DEBUG, DBG_UPDV_PATH_IS_SYMLINK,
+			rootPath, "/platform");
+		return (R_FAILURE);
+	}
+
+	/* /kernel must exist and must not be a symbolic link */
+
+	r = testPath(TEST_EXISTS|TEST_NOT_SYMBOLIC_LINK,
+		"%s/%s", rootPath, "/kernel");
+	if (r != R_SUCCESS) {
+		log_msg(LOG_MSG_DEBUG, DBG_UPDV_PATH_IS_SYMLINK,
+			rootPath, "/kernel");
+		return (R_FAILURE);
+	}
+
+	/* can update driver */
+
+	log_msg(LOG_MSG_DEBUG, DBG_UPDV_YES, rootPath);
+
+	return (R_SUCCESS);
+}
+
+/*
+ * Name:	cmd_can_remove_driver
+ * Description:	determine if target is a global zone
+ * Scope:	public
+ * Arguments:	argc,argv:
+ *		  - optional path to target to test
+ * Returns:	int
+ *			== 0 - success
+ *			!= 0 - failure
+ * Implementation:
+ * A driver can be added to the system if the components of a Solaris
+ * instance capable of loading drivers is present and it is not the
+ * currently running system.
+ */
+
+static int
+cmd_can_remove_driver(int argc, char **argv, GLOBALDATA_T *a_gdt)
+{
+	char	*rootPath = NULL;
+	int	c;
+	int	r;
+static	char	*cmdName = "can_remove_driver";
+static	int	recursion = 0;
+
+	/* process any command line options */
+
+	while ((c = getopt(argc, argv, ":")) != EOF) {
+		switch (c) {
+		case '\0':	/* prevent end-of-loop not reached warning */
+			break;
+		case '?':
+		default:
+			(void) usage(MSG_IS_INVALID_OPTION, optopt, cmdName);
+			return (R_USAGE);
+		}
+	}
+
+	/* prevent recursion */
+
+	if (recursionCheck(&recursion, cmdName) == B_FALSE) {
+
+		/* see if this is the current running system */
+
+		r = cmd_is_running_system(argc, argv, a_gdt);
+
+		/* cannot be a diskless client */
+
+		if (r != R_SUCCESS) {
+			r = cmd_is_diskless_client(argc, argv, a_gdt);
+		}
+
+		/* no need to guard against recursion any more */
+
+		recursion--;
+
+		switch (r) {
+			case R_SUCCESS:
+				/* is a running system */
+				return (R_FAILURE);
+			case R_FAILURE:
+				/* not a running syste */
+				break;
+			case R_USAGE:
+			case R_ERROR:
+			default:
+				/* cannot determine if is a running system */
+				return (r);
+		}
+	}
+
+	/* normalize argc/argv */
+
+	argc -= optind;
+	argv += optind;
+
+	/* error if more than one argument */
+
+	if (argc > 1) {
+		log_msg(LOG_MSG_ERR, ERR_UNRECOGNIZED_OPTION, argv[1]);
+		(void) usage(MSG_IS_INVALID_OPTION, argv[1]);
+		return (R_USAGE);
+	}
+
+	/* process root path if first argument present */
+
+	if (argc == 1) {
+		if (setRootPath(argv[0], "argv[0]", B_TRUE) != R_SUCCESS) {
+			return (R_ERROR);
+		}
+	}
+
+	/* get current root path */
+
+	r = getRootPath(&rootPath);
+	if (r != R_SUCCESS) {
+		return (r);
+	}
+
+	/* start of command debugging information */
+
+	echoDebug(DBG_ROOTPATH_IS, rootPath);
+
+	/* /etc must exist and must not be a symbolic link */
+
+	r = testPath(TEST_EXISTS|TEST_NOT_SYMBOLIC_LINK,
+		"%s/%s", rootPath, "/etc");
+	if (r != R_SUCCESS) {
+		log_msg(LOG_MSG_DEBUG, DBG_RMDV_PATH_IS_SYMLINK,
+			rootPath, "/etc");
+		return (R_FAILURE);
+	}
+
+	/* /platform must exist and must not be a symbolic link */
+
+	r = testPath(TEST_EXISTS|TEST_NOT_SYMBOLIC_LINK,
+		"%s/%s", rootPath, "/platform");
+	if (r != R_SUCCESS) {
+		log_msg(LOG_MSG_DEBUG, DBG_RMDV_PATH_IS_SYMLINK,
+			rootPath, "/platform");
+		return (R_FAILURE);
+	}
+
+	/* /kernel must exist and must not be a symbolic link */
+
+	r = testPath(TEST_EXISTS|TEST_NOT_SYMBOLIC_LINK,
+		"%s/%s", rootPath, "/kernel");
+	if (r != R_SUCCESS) {
+		log_msg(LOG_MSG_DEBUG, DBG_RMDV_PATH_IS_SYMLINK,
+			rootPath, "/kernel");
+		return (R_FAILURE);
+	}
+
+	/* can remove driver */
+
+	log_msg(LOG_MSG_DEBUG, DBG_RMDV_YES, rootPath);
+
+	return (R_SUCCESS);
+}
+
+/*
+ * Name:	cmd_is_path_writable
+ * Description:	determine if target path is writable (not inherited)
+ * Scope:	public
+ * Arguments:	argc,argv:
+ *		  - optional path to target to test
+ * Returns:	int
+ *			== 0 - success
+ *			!= 0 - failure
+ * IMPLEMENTATION:
+ * - path must be found in the file systems configured
+ * - file system type must not be "inherited"
+ * - mount options must not include "read only"
+ */
+
+static int
+cmd_is_path_writable(int argc, char **argv, GLOBALDATA_T *a_gdt)
+{
+	FSI_T	*list;
+	char	*rootPath = NULL;
+	int	c;
+	int	n;
+	int	nn;
+	int	r;
+	long	listSize;
+	long	rootPathLen;
+static	char	*cmdName = "is_path_writable";
+static	int	recursion = 0;
+
+	/* process any command line options */
+
+	while ((c = getopt(argc, argv, ":")) != EOF) {
+		switch (c) {
+		case '\0':	/* prevent end-of-loop not reached warning */
+			break;
+		case '?':
+		default:
+			(void) usage(MSG_IS_INVALID_OPTION, optopt, cmdName);
+			return (R_USAGE);
+		}
+	}
+
+	/* prevent recursion */
+
+	if (recursionCheck(&recursion, cmdName) == B_FALSE) {
+		recursion--;
+	}
+
+	/* normalize argc/argv */
+
+	argc -= optind;
+	argv += optind;
+
+	/* error if more than one argument */
+
+	if (argc > 1) {
+		log_msg(LOG_MSG_ERR, ERR_UNRECOGNIZED_OPTION, argv[1]);
+		(void) usage(MSG_IS_INVALID_OPTION, argv[1]);
+		return (R_USAGE);
+	}
+
+	/* process root path if first argument present */
+
+	if (argc != 1) {
+		(void) usage(ERR_REQUIRED_ROOTPATH_MISSING, cmdName);
+		return (R_USAGE);
+	}
+
+	if (setRootPath(argv[0], "argv[0]", B_TRUE) != R_SUCCESS) {
+		return (R_ERROR);
+	}
+
+	/* get current root path */
+
+	r = getRootPath(&rootPath);
+	if (r != R_SUCCESS) {
+		return (r);
+	}
+
+	/* start of command debugging information */
+
+	echoDebug(DBG_ROOTPATH_IS, rootPath);
+
+	/* search file system conf for this path */
+
+	rootPathLen = strlen(rootPath);
+	list = a_gdt->gd_fileSystemConfig;
+	listSize = a_gdt->gd_fileSystemConfigLen;
+	for (nn = 0, n = 0; n < listSize; n++) {
+		long	mplen = strlen(list[n].fsi_mntPoint);
+		if (rootPathLen < mplen) {
+			/* root path is longer than target, ignore */
+			continue;
+		}
+		if (strncmp(rootPath, list[n].fsi_mntPoint, mplen) == 0) {
+			/* remember last partial match */
+			nn = n;
+		}
+	}
+
+	log_msg(LOG_MSG_DEBUG, DBG_PWRT_INFO,
+		rootPath, list[nn].fsi_mntPoint, list[nn].fsi_fsType,
+		list[nn].fsi_mntOptions);
+
+	/*
+	 * need to determine if the mount point is writeable:
+	 * If parent mount point is "inherited" then it is not writeable
+	 */
+
+	if (strcmp(list[nn].fsi_fsType, FSTYPE_INHERITED) == 0) {
+		log_msg(LOG_MSG_DEBUG, DBG_PWRT_INHERITED, rootPath,
+			list[nn].fsi_mntOptions);
+		return (R_FAILURE);
+	}
+
+	/* see if the file system is mounted with the "read only" option */
+
+	r = mountOptionPresent(list[nn].fsi_mntOptions, MNTOPT_RO);
+	if (r == R_SUCCESS) {
+		log_msg(LOG_MSG_DEBUG, DBG_PWRT_READONLY,
+			rootPath, list[nn].fsi_mntOptions);
+		return (R_FAILURE);
+	}
+
+	/* target path is writable */
+
+	log_msg(LOG_MSG_DEBUG, DBG_PWRT_IS, rootPath);
+
+	return (R_SUCCESS);
+}
+
+/*
+ * Name:	cmd_is_alternative_root
+ * Description:	determine if target is an alternative root
+ * Scope:	public
+ * Arguments:	argc,argv:
+ *		  - optional path to target to test
+ * Returns:	int
+ *			== 0 - success
+ *			!= 0 - failure
+ * Implementation:
+ *  - success if an initial installation to the install root
+ *	(an initial install to $PKG_INSTALL_ROOT means that $PKG_INSTALL_ROOT
+ *	points to an alternative root that is under construction)
+ *  - must not be installation of a zone
+ *  - must not be a boot environment
+ *  - must not be a diskless client
+ *  - must not be a mounted miniroot
+ *  - must not be a netinstall image
+ *  - must not be a nonglobal zone
+ *  - must not be a running system
+ *  - $ROOTDIR must not be "/"
+ *  - $ROOTDIR/var must exist
+ */
+
+static int
+cmd_is_alternative_root(int argc, char **argv, GLOBALDATA_T *a_gdt)
+{
+	char	*rootPath = NULL;
+	int	c;
+	int	r;
+static	char	*cmdName = "is_alternative_root";
+static	int	recursion = 0;
+
+	/* process any command line options */
+
+	while ((c = getopt(argc, argv, ":")) != EOF) {
+		switch (c) {
+		case '\0':	/* prevent end-of-loop not reached warning */
+			break;
+		case '?':
+		default:
+			(void) usage(MSG_IS_INVALID_OPTION, optopt, cmdName);
+			return (R_USAGE);
+		}
+	}
+
+	/* prevent recursion */
+
+	if (recursionCheck(&recursion, cmdName) == B_FALSE) {
+
+		/*
+		 * an alternative root cannot be any of the following
+		 */
+
+		/* cannot be a boot_environment */
+
+		r = cmd_is_boot_environment(argc, argv, a_gdt);
+
+		/* cannot be a diskless_client */
+
+		if (r != R_SUCCESS) {
+			r = cmd_is_diskless_client(argc, argv, a_gdt);
+		}
+
+		/* cannot be a mounted_miniroot */
+
+		if (r != R_SUCCESS) {
+			r = cmd_is_mounted_miniroot(argc, argv, a_gdt);
+		}
+
+		/* cannot be a netinstall_image */
+
+		if (r != R_SUCCESS) {
+			r = cmd_is_netinstall_image(argc, argv, a_gdt);
+		}
+
+		/* cannot be a nonglobal_zone */
+
+		if (r != R_SUCCESS) {
+			r = cmd_is_nonglobal_zone(argc, argv, a_gdt);
+		}
+
+		/* cannot be a running_system */
+
+		if (r != R_SUCCESS) {
+			r = cmd_is_running_system(argc, argv, a_gdt);
+		}
+
+		/* no need to guard against recursion any more */
+
+		recursion--;
+
+		/* return failure if any of the preceeding are true */
+
+		switch (r) {
+			case R_SUCCESS:
+				return (R_FAILURE);
+			case R_FAILURE:
+				break;
+			case R_USAGE:
+			case R_ERROR:
+			default:
+				return (r);
+		}
+	}
+
+	/* normalize argc/argv */
+
+	argc -= optind;
+	argv += optind;
+
+	/* error if more than one argument */
+
+	if (argc > 1) {
+		log_msg(LOG_MSG_ERR, ERR_UNRECOGNIZED_OPTION, argv[1]);
+		(void) usage(MSG_IS_INVALID_OPTION, argv[1]);
+		return (R_USAGE);
+	}
+
+	/* process root path if first argument present */
+
+	if (argc == 1) {
+		if (setRootPath(argv[0], "argv[0]", B_TRUE) != R_SUCCESS) {
+			return (R_ERROR);
+		}
+	}
+
+	/* get current root path */
+
+	r = getRootPath(&rootPath);
+	if (r != R_SUCCESS) {
+		return (r);
+	}
+
+	/* start of command debugging information */
+
+	echoDebug(DBG_ROOTPATH_IS, rootPath);
+
+	/* return success if initial installation */
+
+	if ((a_gdt->gd_initialInstall == B_TRUE) &&
+	    (strcmp(a_gdt->gd_installRoot, rootPath) == 0)) {
+		log_msg(LOG_MSG_DEBUG, DBG_IALR_INITIAL_INSTALL, rootPath);
+		return (R_SUCCESS);
+	}
+
+	/* root path must not be "/" */
+
+	if (strcmp(rootPath, "/") == 0) {
+		log_msg(LOG_MSG_DEBUG, DBG_IALR_BAD_ROOTPATH, rootPath, "/");
+		return (R_FAILURE);
+	}
+
+	/* /var must exist */
+
+	r = testPath(TEST_EXISTS,
+		"%s/%s", rootPath, "/var");
+	if (r != R_SUCCESS) {
+		log_msg(LOG_MSG_DEBUG, DBG_IALR_PATH_DOES_NOT_EXIST,
+			rootPath, "/var");
+		return (R_FAILURE);
+	}
+
+	/* must not be installation of a zone */
+
+	if ((a_gdt->gd_globalZoneInstall == B_TRUE) ||
+	    (a_gdt->gd_nonglobalZoneInstall == B_TRUE)) {
+		/* initial zone install: no path can be alternative root */
+		log_msg(LOG_MSG_DEBUG, DBG_IALR_ZONE_INSTALL, rootPath);
+		return (R_FAILURE);
+	}
+
+	/* target is an alternative root */
+
+	log_msg(LOG_MSG_DEBUG, DBG_IALR_IS, rootPath);
+
+	return (R_SUCCESS);
+}
+
+/*
+ * Name:	cmd_is_boot_environment
+ * Description:	determine if target is an alternative, inactive boot environment
+ * Scope:	public
+ * Arguments:	argc,argv:
+ *		  - optional path to target to test
+ * Returns:	int
+ *			== 0 - success
+ *			!= 0 - failure
+ * IMPLEMENTATION:
+ *  - must not be initial installation to the install root
+ *  - must not be installation of a zone
+ *  - must not be a diskless client
+ *  - must not be a netinstall image
+ *  - must not be a mounted miniroot
+ *  - $ROOTDIR must not be "/"
+ *  - $ROOTDIR/etc/lutab must exist
+ *  - $ROOTDIR/etc/lu must exist and must be a directory
+ */
+
+static int
+cmd_is_boot_environment(int argc, char **argv, GLOBALDATA_T *a_gdt)
+{
+	char	*rootPath = NULL;
+	int	c;
+	int	r;
+static	char	*cmdName = "is_boot_environment";
+static	int	recursion = 0;
+
+	/* process any command line options */
+
+	while ((c = getopt(argc, argv, ":")) != EOF) {
+		switch (c) {
+		case '\0':	/* prevent end-of-loop not reached warning */
+			break;
+		case '?':
+		default:
+			(void) usage(MSG_IS_INVALID_OPTION, optopt, cmdName);
+			return (R_USAGE);
+		}
+	}
+
+	/* prevent recursion */
+
+	if (recursionCheck(&recursion, cmdName) == B_FALSE) {
+		/*
+		 * a boot environment cannot be any of the following
+		 */
+
+		/* cannot be a diskless client */
+
+		r = cmd_is_diskless_client(argc, argv, a_gdt);
+
+		/* cannot be a netinstall_image */
+
+		if (r != R_SUCCESS) {
+			r = cmd_is_netinstall_image(argc, argv, a_gdt);
+		}
+
+		/* cannot be a mounted_miniroot */
+
+		if (r != R_SUCCESS) {
+			r = cmd_is_mounted_miniroot(argc, argv, a_gdt);
+		}
+
+		/* no need to guard against recursion any more */
+
+		recursion--;
+
+		/* return failure if any of the preceeding are true */
+
+		switch (r) {
+			case R_SUCCESS:
+				return (R_FAILURE);
+			case R_FAILURE:
+				break;
+			case R_USAGE:
+			case R_ERROR:
+			default:
+				return (r);
+		}
+	}
+
+	/* normalize argc/argv */
+
+	argc -= optind;
+	argv += optind;
+
+	/* error if more than one argument */
+
+	if (argc > 1) {
+		log_msg(LOG_MSG_ERR, ERR_UNRECOGNIZED_OPTION, argv[1]);
+		(void) usage(MSG_IS_INVALID_OPTION, argv[1]);
+		return (R_USAGE);
+	}
+
+	/* process root path if first argument present */
+
+	if (argc == 1) {
+		if (setRootPath(argv[0], "argv[0]", B_TRUE) != R_SUCCESS) {
+			return (R_ERROR);
+		}
+	}
+
+	/* get current root path */
+
+	r = getRootPath(&rootPath);
+	if (r != R_SUCCESS) {
+		return (r);
+	}
+
+	/* start of command debugging information */
+
+	echoDebug(DBG_ROOTPATH_IS, rootPath);
+
+	/* root path must not be "/" */
+
+	if (strcmp(rootPath, "/") == 0) {
+		log_msg(LOG_MSG_DEBUG, DBG_BENV_BAD_ROOTPATH, rootPath, "/");
+		return (R_FAILURE);
+	}
+
+	/* zone name must be global */
+
+	if (strcmp(a_gdt->gd_zoneName, GLOBAL_ZONENAME) != 0) {
+		log_msg(LOG_MSG_DEBUG, DBG_BENV_BAD_ZONE, rootPath,
+			GLOBAL_ZONENAME);
+		return (R_FAILURE);
+	}
+
+	/* $ROOTDIR/etc/lutab must exist */
+
+	r = testPath(TEST_EXISTS, "%s/%s", rootPath, "/etc/lutab");
+	if (r != R_SUCCESS) {
+		log_msg(LOG_MSG_DEBUG, DBG_BENV_NO_ETCLUTAB, rootPath,
+			"/etc/lutab");
+		return (R_FAILURE);
+	}
+
+	/* $ROOTDIR/etc/lu must exist */
+
+	r = testPath(TEST_EXISTS|TEST_IS_DIRECTORY,
+		"%s/%s", rootPath, "/etc/lu");
+	if (r != R_SUCCESS) {
+		log_msg(LOG_MSG_DEBUG, DBG_BENV_NO_ETCLU, rootPath, "/etc/lu");
+		return (R_FAILURE);
+	}
+
+	/* must not be initial installation */
+
+	if ((a_gdt->gd_initialInstall == B_TRUE) &&
+	    (strcmp(a_gdt->gd_installRoot, rootPath) == 0)) {
+		log_msg(LOG_MSG_DEBUG, DBG_BENV_INITIAL_INSTALL, rootPath);
+		return (R_FAILURE);
+	}
+
+	/* must not be installation of a zone */
+
+	if ((a_gdt->gd_globalZoneInstall == B_TRUE) ||
+	    (a_gdt->gd_nonglobalZoneInstall == B_TRUE)) {
+		/* initial zone install: no path can be boot environment */
+		log_msg(LOG_MSG_DEBUG, DBG_BENV_ZONE_INSTALL, rootPath);
+		return (R_FAILURE);
+	}
+
+	/* target is a boot environment */
+
+	log_msg(LOG_MSG_DEBUG, DBG_BENV_IS, rootPath);
+
+	return (R_SUCCESS);
+}
+
+/*
+ * Name:	cmd_is_sparse_root_ng_zone
+ * Description:	determine if target is a sparse non-global zone
+ * Scope:	public
+ * Arguments:	argc,argv:
+ *		  - optional path to target to test
+ * Returns:	int
+ *			== 0 - success
+ *			!= 0 - failure
+ * IMPLEMENATION:
+ *  - must be a non-global zone
+ *  - inherited file systems must be present, and/or
+ *  - read-only lofs file systems must be present
+ */
+
+static int
+cmd_is_sparse_root_ng_zone(int argc, char **argv, GLOBALDATA_T *a_gdt)
+{
+	char	*rootPath = NULL;
+	int	c;
+	int	r;
+static	char	*cmdName = "is_sparse_root_nonglobal_zone";
+static	int	recursion = 0;
+
+	/* process any command line options */
+
+	while ((c = getopt(argc, argv, ":")) != EOF) {
+		switch (c) {
+		case '\0':	/* prevent end-of-loop not reached warning */
+			break;
+		case '?':
+		default:
+			(void) usage(MSG_IS_INVALID_OPTION, optopt, cmdName);
+			return (R_USAGE);
+		}
+	}
+
+	/* prevent recursion */
+
+	if (recursionCheck(&recursion, cmdName) == B_FALSE) {
+
+		/* see if this is a non-global zone */
+
+		r = cmd_is_nonglobal_zone(argc, argv, a_gdt);
+
+		/* no need to guard against recursion any more */
+
+		recursion--;
+
+		switch (r) {
+			case R_SUCCESS:
+				/* is a non-global zone */
+				break;
+			case R_FAILURE:
+				/* not a non-global zone */
+				return (R_FAILURE);
+			case R_USAGE:
+			case R_ERROR:
+			default:
+				/* cannot determine if non-global zone */
+				return (r);
+		}
+	}
+
+	/* normalize argc/argv */
+
+	argc -= optind;
+	argv += optind;
+
+	/* error if more than one argument */
+
+	if (argc > 1) {
+		log_msg(LOG_MSG_ERR, ERR_UNRECOGNIZED_OPTION, argv[1]);
+		(void) usage(MSG_IS_INVALID_OPTION, argv[1]);
+		return (R_USAGE);
+	}
+
+	/* process root path if first argument present */
+
+	if (argc == 1) {
+		if (setRootPath(argv[0], "argv[0]", B_TRUE) != R_SUCCESS) {
+			return (R_ERROR);
+		}
+	}
+
+	/* get current root path */
+
+	r = getRootPath(&rootPath);
+	if (r != R_SUCCESS) {
+		return (r);
+	}
+
+	/* start of command debugging information */
+
+	echoDebug(DBG_ROOTPATH_IS, rootPath);
+
+	/*
+	 * in a non-global zone:
+	 * if any file systems are inherited, or if /usr is read only,
+	 * then the target is a sparse root non-global zone.
+	 */
+
+	if ((a_gdt->gd_inheritedFileSystems != (char **)NULL) ||
+		(a_gdt->gd_srFsMountedRO == B_TRUE)) {
+		/* no inherited file systems */
+		log_msg(LOG_MSG_DEBUG, DBG_SRNG_IS, rootPath);
+		return (R_SUCCESS);
+	}
+
+	/* target is not a sparse root non-global zone */
+
+	log_msg(LOG_MSG_DEBUG, DBG_SRNG_IS_NOT, rootPath);
+
+	return (R_FAILURE);
+
+}
+
+/*
+ * Name:	cmd_is_what
+ * Description:	determine what the target is
+ * Scope:	public
+ * Arguments:	argc,argv:
+ *		  - optional path to target to test
+ * Returns:	int
+ *			== 0 - success
+ *			!= 0 - failure
+ */
+
+static int
+cmd_is_what(int argc, char **argv, GLOBALDATA_T *a_gdt)
+{
+	char	*rootPath = NULL;
+	int	c;
+	int	cur_cmd;
+	int	r;
+static	char	*cmdName = "is_what";
+
+	/* process any command line options */
+
+	while ((c = getopt(argc, argv, ":")) != EOF) {
+		switch (c) {
+		case '\0':	/* prevent end-of-loop not reached warning */
+			break;
+		case '?':
+		default:
+			(void) usage(MSG_IS_INVALID_OPTION, optopt, cmdName);
+			return (R_USAGE);
+		}
+	}
+
+	/* normalize argc/argv */
+
+	argc -= optind;
+	argv += optind;
+
+	/* error if more than one argument */
+
+	if (argc > 1) {
+		log_msg(LOG_MSG_ERR, ERR_UNRECOGNIZED_OPTION, argv[1]);
+		(void) usage(MSG_IS_INVALID_OPTION, argv[1]);
+		return (R_USAGE);
+	}
+
+	/* process root path if first argument present */
+
+	if (argc == 1) {
+		if (setRootPath(argv[0], "argv[0]", B_TRUE) != R_SUCCESS) {
+			return (R_ERROR);
+		}
+	}
+
+	/* get current root path */
+
+	r = getRootPath(&rootPath);
+	if (r != R_SUCCESS) {
+		return (r);
+	}
+
+	/*
+	 * construct the command line for all of the packages
+	 */
+
+	argc = 0;
+	argv[argc++] = strdup(get_prog_name());
+	argv[argc++] = strdup(rootPath);
+
+	/* start of command debugging information */
+
+	echoDebug(DBG_ROOTPATH_IS, rootPath);
+
+	/* search for specified subcommand and execute if found */
+
+	for (cur_cmd = 0; cmds[cur_cmd].c_name != NULL; cur_cmd++) {
+		int	result;
+
+		/* do not recursively call this function */
+
+		if (cmds[cur_cmd].c_func == cmd_is_what) {
+			continue;
+		}
+
+		/* call subcommand with its own argc/argv */
+
+		result = cmds[cur_cmd].c_func(argc, argv, a_gdt);
+
+		/* process result code and exit */
+
+		result = adjustResults(result);
+		log_msg(LOG_MSG_INFO, MSG_IS_WHAT_RESULT,
+			cmds[cur_cmd].c_name, result);
+	}
+	return (R_SUCCESS);
+}
+
+/*
+ * Name:	cmd_is_whole_root_ng_zone
+ * Description:	determine if target is a global zone
+ * Scope:	public
+ * Arguments:	argc,argv:
+ *		  - optional path to target to test
+ * Returns:	int
+ *			== 0 - success
+ *			!= 0 - failure
+ * IMPLEMENTATION:
+ *  - must be a non-global zone
+ *  - no inherited file systems may be present
+ *  - no read-only lofs file systems may be present
+ */
+
+static int
+cmd_is_whole_root_ng_zone(int argc, char **argv, GLOBALDATA_T *a_gdt)
+{
+	char	*rootPath = NULL;
+	int	c;
+	int	r;
+static	char	*cmdName = "is_whole_root_nonglobal_zone";
+static	int	recursion = 0;
+
+	/* process any command line options */
+
+	while ((c = getopt(argc, argv, ":")) != EOF) {
+		switch (c) {
+		case '\0':	/* prevent end-of-loop not reached warning */
+			break;
+		case '?':
+		default:
+			(void) usage(MSG_IS_INVALID_OPTION, optopt, cmdName);
+			return (R_USAGE);
+		}
+	}
+
+	/* prevent recursion */
+
+	if (recursionCheck(&recursion, cmdName) == B_FALSE) {
+
+		/* see if this is a non-global zone */
+
+		r = cmd_is_nonglobal_zone(argc, argv, a_gdt);
+
+		/* no need to guard against recursion any more */
+
+		recursion--;
+
+		switch (r) {
+			case R_SUCCESS:
+				/* is a non-global zone */
+				break;
+			case R_FAILURE:
+				/* not a non-global zone */
+				return (R_FAILURE);
+			case R_USAGE:
+			case R_ERROR:
+			default:
+				/* cannot determine if non-global zone */
+				return (r);
+		}
+	}
+
+	/* normalize argc/argv */
+
+	argc -= optind;
+	argv += optind;
+
+	/* error if more than one argument */
+
+	if (argc > 1) {
+		log_msg(LOG_MSG_ERR, ERR_UNRECOGNIZED_OPTION, argv[1]);
+		(void) usage(MSG_IS_INVALID_OPTION, argv[1]);
+		return (R_USAGE);
+	}
+
+	/* process root path if first argument present */
+
+	if (argc == 1) {
+		if (setRootPath(argv[0], "argv[0]", B_TRUE) != R_SUCCESS) {
+			return (R_ERROR);
+		}
+	}
+
+	/* get current root path */
+
+	r = getRootPath(&rootPath);
+	if (r != R_SUCCESS) {
+		return (r);
+	}
+
+	/* start of command debugging information */
+
+	echoDebug(DBG_ROOTPATH_IS, rootPath);
+
+	/*
+	 * in a non-global zone:
+	 * if no file systems are inherited, and if /usr is not
+	 * read only, then the target is a whole root non-global zone.
+	 */
+
+	if ((a_gdt->gd_inheritedFileSystems == (char **)NULL) &&
+		(a_gdt->gd_srFsMountedRO == B_FALSE)) {
+		/* no inherited file systems */
+		log_msg(LOG_MSG_DEBUG, DBG_WRNG_IS, rootPath);
+		return (R_SUCCESS);
+	}
+
+	/* target is not a whole-root non-global zone */
+
+	log_msg(LOG_MSG_DEBUG, DBG_WRNG_IS_NOT, rootPath);
+
+	return (R_FAILURE);
+}
+
+/*
+ * *****************************************************************************
+ * utility support functions
+ * *****************************************************************************
+ */
+
+/*
+ * Name:	getMountOption
+ * Description:	return next mount option in a string
+ * Arguments:	p - pointer to string containing mount options
+ * Output:	none
+ * Returns:	char * - pointer to next option in string "p"
+ * Side Effects: advances input "p" and inserts \0 in place of the
+ *		option separator found.
+ */
+
+static char *
+getMountOption(char **p)
+{
+	char *cp = *p;
+	char *retstr;
+
+	/* advance past all white space */
+
+	while (*cp && isspace(*cp))
+		cp++;
+
+	/* remember start of next option */
+
+	retstr = cp;
+
+	/* advance to end of string or option separator */
+
+	while (*cp && *cp != ',')
+		cp++;
+
+	/* replace separator with '\0' if not at end of string */
+	if (*cp) {
+		*cp = '\0';
+		cp++;
+	}
+
+	/* reset caller's pointer and return pointer to option */
+
+	*p = cp;
+	return (retstr);
+}
+
+/*
+ * Name:	mountOptionPresent
+ * Description:	determine if specified mount option is present in list
+ *		of mount point options
+ * Arguments:	a_mntOptions - pointer to string containing list of mount
+ *			point options to search
+ *		a_opt - pointer to string containing option to search for
+ * Output:	none
+ * Returns:	R_SUCCESS - option is present in list of mount point options
+ *		R_FAILURE - options is not present
+ *		R_ERROR - unable to determine if option is present or not
+ */
+
+static int
+mountOptionPresent(char *a_mntOptions, char *a_opt)
+{
+	char tmpopts[MNT_LINE_MAX];
+	char *f, *opts = tmpopts;
+
+	/* return false if no mount options present */
+
+	if ((a_opt == NULL) || (*a_opt == '\0')) {
+		return (R_FAILURE);
+	}
+
+	/* return not present if no list of options to search */
+
+	if (a_mntOptions == NULL) {
+		return (R_FAILURE);
+	}
+
+	/* return not present if list of options to search is empty */
+
+	if (*a_mntOptions == '\0') {
+		return (R_FAILURE);
+	}
+
+	/* make local copy of option list to search */
+
+	(void) strcpy(opts, a_mntOptions);
+
+	/* scan each option looking for the specified option */
+
+	f = getMountOption(&opts);
+	for (; *f; f = getMountOption(&opts)) {
+		/* return success if option matches target */
+		if (strncmp(a_opt, f, strlen(a_opt)) == 0) {
+			return (R_SUCCESS);
+		}
+	}
+
+	/* option not found */
+
+	return (R_FAILURE);
+}
+
+/*
+ * Name:	sortedInsert
+ * Description:	perform an alphabetical sorted insert into a list
+ * Arguments:	r_list - pointer to list to insert next entry into
+ *		a_listSize - pointer to current list size
+ *		a_mntPoint - mount point to insert (is sort key)
+ *		a_fsType - file system type for mount point
+ *		a_mntOptions - file syste mount options for mount point
+ * Output:	None
+ * Returns:	None
+ */
+
+static void
+sortedInsert(FSI_T **r_list, long *a_listSize, char *a_mntPoint,
+	char *a_fsType, char *a_mntOptions)
+{
+	int	listSize;
+	FSI_T	*list;
+	int	n;
+
+	/* entry assertions */
+
+	assert(a_listSize != (long *)NULL);
+	assert(a_mntPoint != NULL);
+	assert(a_fsType != NULL);
+	assert(a_mntOptions != NULL);
+
+	/* entry debugging info */
+
+	echoDebug(DBG_SINS_ENTRY, a_mntPoint, a_fsType, a_mntOptions);
+
+	/* localize references to the list and list size */
+
+	listSize = *a_listSize;
+	list = *r_list;
+
+	/*
+	 * if list empty insert this entry as the first one in the list
+	 */
+
+	if (listSize == 0) {
+		/* allocate new entry for list */
+		listSize++;
+		list = (FSI_T *)realloc(list, sizeof (FSI_T)*(listSize+1));
+
+		/* first entry is data passed to this function */
+		list[0].fsi_mntPoint = strdup(a_mntPoint);
+		list[0].fsi_fsType = strdup(a_fsType);
+		list[0].fsi_mntOptions = strdup(a_mntOptions);
+
+		/* second entry is all NULL - end of entry marker */
+		list[1].fsi_mntPoint = NULL;
+		list[1].fsi_fsType = NULL;
+		list[1].fsi_mntOptions = NULL;
+
+		/* restore list and list size references to caller */
+		*a_listSize = listSize;
+		*r_list = list;
+
+		return;
+	}
+
+	/*
+	 * list not empty - scan looking for largest match
+	 */
+
+	for (n = 0; n < listSize; n++) {
+		int	c;
+
+		/* compare target with current list entry */
+
+		c = strcmp(list[n].fsi_mntPoint, a_mntPoint);
+
+		if (c == 0) {
+			char	*me;
+			long	len;
+
+			/* entry already in list -- merge entries */
+
+			len = strlen(list[n].fsi_mntOptions) +
+				strlen(a_mntOptions) + 2;
+			me = (char *)calloc(1, len);
+
+			/* merge two mount options lists into one */
+
+			(void) strlcat(me, list[n].fsi_mntOptions, len);
+			(void) strlcat(me, ",", len);
+			(void) strlcat(me, a_mntOptions, len);
+
+			/* free old list, replace with merged one */
+
+			free(list[n].fsi_mntOptions);
+			list[n].fsi_mntOptions = me;
+
+			echoDebug(DBG_SORTEDINS_SKIPPED,
+				n, list[n].fsi_mntPoint, a_fsType,
+				list[n].fsi_fsType, a_mntOptions,
+				list[n].fsi_mntOptions);
+
+			continue;
+		} else if (c < 0) {
+			/* entry before this one - skip */
+			continue;
+		}
+
+		/*
+		 * entry after this one - insert new entry
+		 */
+
+		/* allocate one more entry and make space for new entry */
+		listSize++;
+		list = (FSI_T *)realloc(list,
+			sizeof (FSI_T)*(listSize+1));
+		(void) memmove(&(list[n+1]), &(list[n]),
+			sizeof (FSI_T)*(listSize-n));
+
+		/* insert this entry into list */
+		list[n].fsi_mntPoint = strdup(a_mntPoint);
+		list[n].fsi_fsType = strdup(a_fsType);
+		list[n].fsi_mntOptions = strdup(a_mntOptions);
+
+		/* restore list and list size references to caller */
+		*a_listSize = listSize;
+		*r_list = list;
+
+		return;
+	}
+
+	/*
+	 * all entries are before this one - append to end of list
+	 */
+
+	/* allocate new entry at end of list */
+	listSize++;
+	list = (FSI_T *)realloc(list, sizeof (FSI_T)*(listSize+1));
+
+	/* append this entry to the end of the list */
+	list[listSize-1].fsi_mntPoint = strdup(a_mntPoint);
+	list[listSize-1].fsi_fsType = strdup(a_fsType);
+	list[listSize-1].fsi_mntOptions = strdup(a_mntOptions);
+
+	/* restore list and list size references to caller */
+	*a_listSize = listSize;
+	*r_list = list;
+}
+
+/*
+ * Name:	calculateFileSystemConfig
+ * Description:	generate sorted list of all mounted file systems
+ * Arguments:	a_gdt - global data structure to place sorted entries into
+ * Output:	None
+ * Returns:	R_SUCCESS - successfully generated mounted file systems list
+ *		R_FAILURE - options is not present
+ *		R_ERROR - unable to determine if option is present or not
+ */
+
+static int
+calculateFileSystemConfig(GLOBALDATA_T *a_gdt)
+{
+	FILE		*fp;
+	struct mnttab	mntbuf;
+	FSI_T		*list;
+	long		listSize;
+	boolean_t	readOnlyMountFound = B_FALSE;
+
+	/* entry assetions */
+
+	assert(a_gdt != (GLOBALDATA_T *)NULL);
+
+	/* entry debugging info */
+
+	echoDebug(DBG_CALCSCFG_ENTRY);
+
+	/* allocate a list that has one termination entry */
+
+	list = (FSI_T *)calloc(1, sizeof (FSI_T));
+	list[0].fsi_mntPoint = NULL;
+	list[0].fsi_fsType = NULL;
+	list[0].fsi_mntOptions = NULL;
+	listSize = 0;
+
+	/* insert entries for all inherited file systems */
+
+	if (a_gdt->gd_inheritedFileSystems) {
+		int n;
+		char **ifs = a_gdt->gd_inheritedFileSystems;
+
+		/* debugging info */
+
+		echoDebug(DBG_CALCSCFG_INHERITED);
+
+		/* insert all inherited file systems */
+
+		for (n = 0; ifs[n]; n++) {
+			sortedInsert(&list, &listSize, ifs[n],
+				FSTYPE_INHERITED, MNTOPT_RO);
+		}
+	}
+
+	/* open the mount table for reading */
+
+	fp = fopen(MNTTAB, "r");
+	if (fp == (FILE *)NULL) {
+		return (R_ERROR);
+	}
+
+	/* debugging info */
+
+	echoDebug(DBG_CALCSCFG_MOUNTED);
+
+	/* go through all the specials looking for the device */
+
+	while (getmntent(fp, &mntbuf) == 0) {
+		if (mntbuf.mnt_mountp[0] == '/') {
+			sortedInsert(&list, &listSize,
+			strdup(mntbuf.mnt_mountp),
+			strdup(mntbuf.mnt_fstype),
+			strdup(mntbuf.mnt_mntopts ? mntbuf.mnt_mntopts : ""));
+		}
+
+		/*
+		 * Set flag if we are in a non-global zone and it is in
+		 * the mounted state.
+		 */
+
+		if (strcmp(mntbuf.mnt_mountp, "/a") == 0 &&
+			strcmp(mntbuf.mnt_special, "/a") == 0 &&
+			strcmp(mntbuf.mnt_fstype, "lofs") == 0) {
+			a_gdt->inMountedState = B_TRUE;
+		}
+
+		if (!readOnlyMountFound) {
+			readOnlyMountFound = checkForReadOnlyMount(a_gdt,
+			    mntbuf.mnt_mountp, mntbuf.mnt_fstype,
+			    mntbuf.mnt_mntopts);
+		}
+	}
+
+	/* close mount table file */
+
+	(void) fclose(fp);
+
+	/* store list pointers in global data structure */
+
+	a_gdt->gd_fileSystemConfig = list;
+	a_gdt->gd_fileSystemConfigLen = listSize;
+
+	return (R_SUCCESS);
+}
+
+/*
+ * Name:	checkForReadOnlyMount
+ * Description:	given a mount point, type and options, determine if the
+ *              mounted file system is part of a "sparse root" configuration
+ *              by checking if the known Zone directories a ro LOFS mounted.
+ * Arguments:	a_gdt - global data structure to place sorted entries into
+ *		a_mntPoint - pointer to string representing mount point
+ *		a_fsType - pointer to string representing file system type
+ *		a_mntOptions - pointer to string representing the options
+ *			used to mount the file system
+ * Returns:	B_TRUE - if sparse root mount is found
+ * 		B_FLASE - if no sparse root mount's are found
+ * Side Effects: set:
+ *			a_gdt->gd_srFsMountedRO = B_TRUE
+ *		if the mounted file system is part of a 'sparse root' config
+ */
+
+static boolean_t
+checkForReadOnlyMount(GLOBALDATA_T *a_gdt, char *a_mntPoint,
+	char *a_fsType, char *a_mntOptions)
+{
+	/* entry assertions */
+	int	i;
+	char mntPoint[MAXPATHLEN];
+	char *zDirs[] = { "/usr", "/lib", "/platform", "/sbin", NULL };
+	char *aZDirs[] = { "/a/usr", "/a/lib", "/a/platform", "/a/sbin", NULL };
+
+	assert(a_gdt != (GLOBALDATA_T *)NULL);
+	assert(a_mntPoint != NULL);
+	assert(a_fsType != NULL);
+
+	/* return if no read-only mount option */
+
+	if (mountOptionPresent(a_mntOptions, MNTOPT_RO) != R_SUCCESS) {
+		return (B_FALSE);
+	}
+
+	/* return if file system is not read-only mounted */
+
+	if (strcmp(a_fsType, MNTTYPE_LOFS) != 0) {
+		return (B_FALSE);
+	}
+
+	/* file system is a read-only lofs mounted.  */
+
+	/* Check read-only lofs mount's appended to the command line path */
+
+	if (a_gdt->gd_cmdline_path != NULL) {
+		if (strncmp(a_mntPoint, a_gdt->gd_cmdline_path,
+			strlen(a_gdt->gd_cmdline_path)) == 0) {
+			for (i = 0; zDirs[i] != NULL; i++) {
+				(void) snprintf(mntPoint, sizeof (mntPoint),
+				    "%s%s", a_gdt->gd_cmdline_path,
+				    zDirs[i]);
+				if (strcmp(a_mntPoint, mntPoint) == 0) {
+					echoDebug(DBG_CKSR_FSREADONLY,
+					    a_mntPoint, a_fsType);
+					a_gdt->gd_srFsMountedRO = B_TRUE;
+					return (B_TRUE);
+				}
+			}
+		}
+
+	/* Check read-only lofs mount's in the mounted state */
+
+	} else if (a_gdt->inMountedState) {
+		for (i = 0; aZDirs[i] != NULL; i++) {
+			if (strncmp(a_mntPoint, aZDirs[i],
+			    sizeof (aZDirs[i])) == 0) {
+				echoDebug(DBG_CKSR_FSREADONLY, a_mntPoint,
+				    a_fsType);
+				a_gdt->gd_srFsMountedRO = B_TRUE;
+				return (B_TRUE);
+			}
+		}
+
+	/* Check read-only lofs mount's for live system */
+
+	} else {
+		for (i = 0; zDirs[i] != NULL; i++) {
+			if (strncmp(a_mntPoint, zDirs[i],
+			    sizeof (zDirs[i])) == 0) {
+				echoDebug(DBG_CKSR_FSREADONLY, a_mntPoint,
+				    a_fsType);
+				a_gdt->gd_srFsMountedRO = B_TRUE;
+				return (B_TRUE);
+			}
+		}
+	}
+
+	return (B_FALSE);
+}
+
+/*
+ * Name: 	adjustResults
+ * Description:	adjust output result code before existing
+ * Arguments:	a_result - result code to adjust
+ * Returns:	int - adjusted result code
+ */
+
+static int
+adjustResults(int a_result)
+{
+	boolean_t	negate = getNegateResults();
+	int		realResult;
+
+	/* adjust code as appropriate */
+
+	switch (a_result) {
+	case R_SUCCESS:		/* condition satisfied */
+		realResult = ((negate == B_TRUE) ? 1 : 0);
+		break;
+	case R_FAILURE:		/* condition not satisfied */
+		realResult = ((negate == B_TRUE) ? 0 : 1);
+		break;
+	case R_USAGE:		/* usage errors */
+		realResult = 2;
+		break;
+	case R_ERROR:		/* condition could not be determined */
+	default:
+		realResult = 3;
+		break;
+	}
+
+	/* debugging output */
+
+	log_msg(LOG_MSG_DEBUG, DBG_ADJUST_RESULTS, a_result, negate,
+		realResult);
+
+	/* return results */
+
+	return (realResult);
+}
+
+/*
+ * Name:        setCmdLinePath
+ * Description:	set global command line path
+ * Arguments:   path - path to set from the command line
+ *              args - command line args
+ *              num_args - number of command line args
+ * Returns:     R_SUCCESS - root path successfully set
+ *              R_FAILURE - root path could not be set
+ *              R_ERROR - fatal error attempting to set root path
+ */
+
+static void
+setCmdLinePath(char **path, char **args, int num_args)
+{
+	char   rp[PATH_MAX] = { '\0' };
+	struct stat statbuf;
+
+	if (*path != NULL) {
+		return;
+	}
+
+	/*
+	 * If a path "pkgcond is_global_zone [path]" is provided on the
+	 * command line it must be the last argument.
+	 */
+
+	if (realpath(args[num_args - 1], rp) != NULL) {
+		if (stat(rp, &statbuf) == 0) {
+			/* make sure the target is a directory */
+			if ((statbuf.st_mode & S_IFDIR)) {
+				*path = strdup(rp);
+			} else {
+				*path = NULL;
+			}
+		}
+	}
+}
+
+/*
+ * Name:	setRootPath
+ * Description:	set global root path returned by getRootPath
+ * Arguments:	a_path - root path to set
+ *		a_mustExist - B_TRUE if path must exist (else error)
+ *			- B_FALSE if path may not exist
+ * Returns:	R_SUCCESS - root path successfully set
+ *		R_FAILURE - root path could not be set
+ *		R_ERROR - fatal error attempting to set root path
+ */
+
+static int
+setRootPath(char *a_path, char *a_envVar, boolean_t a_mustExist)
+{
+	char		rp[PATH_MAX] = { '\0' };
+	struct stat	statbuf;
+
+	/* if no data then issue warning and return success */
+
+	if ((a_path == NULL) || (*a_path == '\0')) {
+		echoDebug(DBG_NO_DEFAULT_ROOT_PATH_SET);
+		return (R_SUCCESS);
+	}
+
+	/* path present - resolve to absolute path */
+
+	if (realpath(a_path, rp) == NULL) {
+		if (a_mustExist == B_TRUE) {
+			/* must exist ... error */
+			log_msg(LOG_MSG_ERR, ERR_DEFAULT_ROOT_INVALID,
+				a_path, strerror(errno));
+			return (R_ERROR);
+		} else {
+			/* may not exist - use path as specified */
+			(void) strcpy(rp, a_path);
+		}
+	}
+
+	/* debugging output */
+
+	echoDebug(DBG_DEFAULT_ROOT_PATH_SET, rp, a_envVar ? a_envVar : "");
+
+	/* validate path existence if it must exist */
+
+	if (a_mustExist == B_TRUE) {
+
+		/* get node status */
+
+		if (stat(rp, &statbuf) != 0) {
+			log_msg(LOG_MSG_ERR, ERR_DEFAULT_ROOT_INVALID,
+				rp, strerror(errno));
+			return (R_ERROR);
+		}
+
+		/* make sure the target is a directory */
+
+		if (!(statbuf.st_mode & S_IFDIR)) {
+			log_msg(LOG_MSG_ERR, ERR_DEFAULT_ROOT_NOT_DIR, rp);
+			return (R_ERROR);
+		}
+	}
+
+	/* target exists and is a directory - set */
+
+	echoDebug(DBG_SET_ROOT_PATH_TO, rp);
+
+	/* store copy of resolved root path */
+
+	_rootPath = strdup(rp);
+
+	/* success! */
+
+	return (R_SUCCESS);
+}
+
+/*
+ * Name:	testPath
+ * Description:	determine if a path meets the specified conditions
+ * Arguments:	a_tt - conditions to test path against
+ * 		a_format - format to use to generate path
+ *		arguments following a_format - as needed for a_format
+ * Returns:	R_SUCCESS - the path meets all of the specified conditions
+ *		R_FAILURE - the path does not meet all of the conditions
+ *		R_ERROR - error attempting to test path
+ */
+
+/*PRINTFLIKE2*/
+static int
+testPath(TEST_TYPES a_tt, char *a_format, ...)
+{
+	char		*mbPath;	/* copy for the path to be returned */
+	char		bfr[1];
+	int		r;
+	size_t		vres = 0;
+	struct stat	statbuf;
+	va_list		ap;
+	int		fd;
+
+	/* entry assertions */
+
+	assert(a_format != NULL);
+	assert(*a_format != '\0');
+
+	/* determine size of the message in bytes */
+
+	va_start(ap, a_format);
+	vres = vsnprintf(bfr, 1, a_format, ap);
+	va_end(ap);
+
+	assert(vres > 0);
+
+	/* allocate storage to hold the message */
+
+	mbPath = (char *)calloc(1, vres+2);
+	assert(mbPath != NULL);
+
+	/* generate the results of the printf conversion */
+
+	va_start(ap, a_format);
+	vres = vsnprintf(mbPath, vres+1, a_format, ap);
+	va_end(ap);
+
+	assert(vres > 0);
+
+	echoDebug(DBG_TEST_PATH, mbPath, (unsigned long)a_tt);
+
+	/*
+	 * When a path given to open(2) contains symbolic links, the
+	 * open system call first resolves all symbolic links and then
+	 * opens that final "resolved" path. As a result, it is not
+	 * possible to check the result of an fstat(2) against the
+	 * file descriptor returned by open(2) for S_IFLNK (a symbolic
+	 * link) since all symbolic links are resolved before the
+	 * target is opened.
+	 *
+	 * When testing the target as being (or not being) a symbolic
+	 * link, first use lstat(2) against the target to determine
+	 * whether or not the specified target itself is (or is not) a
+	 * symbolic link.
+	 */
+
+	if (a_tt & (TEST_IS_SYMBOLIC_LINK|TEST_NOT_SYMBOLIC_LINK)) {
+		/*
+		 * testing target is/is not a symbolic link; use lstat
+		 * to determine the status of the target itself rather
+		 * than what the target might finally address.
+		 */
+
+		if (lstat(mbPath, &statbuf) != 0) {
+			echoDebug(DBG_CANNOT_LSTAT_PATH, mbPath,
+				strerror(errno));
+			free(mbPath);
+			return (R_FAILURE);
+		}
+
+		/* Is the target required to be a symbolic link? */
+
+		if (a_tt & TEST_IS_SYMBOLIC_LINK) {
+			/* target must be a symbolic link */
+			if (!(statbuf.st_mode & S_IFLNK)) {
+				/* failure: target is not a symbolic link */
+				echoDebug(DBG_IS_NOT_A_SYMLINK, mbPath);
+				free(mbPath);
+				return (R_FAILURE);
+			}
+			/* success: target is a symbolic link */
+			echoDebug(DBG_SYMLINK_IS, mbPath);
+		}
+
+		/* Is the target required to not be a symbolic link? */
+
+		if (a_tt & TEST_NOT_SYMBOLIC_LINK) {
+			/* target must not be a symbolic link */
+			if (statbuf.st_mode & S_IFLNK) {
+				/* failure: target is a symbolic link */
+				echoDebug(DBG_IS_A_SYMLINK, mbPath);
+				free(mbPath);
+				return (R_FAILURE);
+			}
+			/* success: target is not a symbolic link */
+			echoDebug(DBG_SYMLINK_NOT, mbPath);
+		}
+
+		/*
+		 * if only testing is/is not a symbolic link, then
+		 * no need to open the target: return success.
+		 */
+
+		if (!(a_tt &
+		    (~(TEST_IS_SYMBOLIC_LINK|TEST_NOT_SYMBOLIC_LINK)))) {
+			free(mbPath);
+			return (R_SUCCESS);
+		}
+	}
+
+	/* resolve path and remove any whitespace */
+
+	r = resolvePath(&mbPath);
+	if (r != R_SUCCESS) {
+		echoDebug(DBG_TEST_PATH_NO_RESOLVE, mbPath);
+		free(mbPath);
+		if (a_tt & TEST_NOT_EXISTS) {
+			return (R_SUCCESS);
+		}
+		return (r);
+	}
+
+	echoDebug(DBG_TEST_PATH_RESOLVE, mbPath);
+
+	/* open the file - this is the basic existence test */
+
+	fd = open(mbPath, O_RDONLY|O_LARGEFILE, 0);
+
+	/* existence test failed if file cannot be opened */
+
+	if (fd < 0) {
+		/*
+		 * target could not be opened - if testing for non-existence,
+		 * return success, otherwise return failure
+		 */
+		if (a_tt & TEST_NOT_EXISTS) {
+			echoDebug(DBG_CANNOT_ACCESS_PATH_OK, mbPath);
+			free(mbPath);
+			return (R_SUCCESS);
+		}
+
+		echoDebug(DBG_CANNOT_ACCESS_PATH_BUT_SHOULD,
+		    mbPath, strerror(errno));
+		free(mbPath);
+
+		return (R_FAILURE);
+	}
+
+	/*
+	 * target successfully opened - if testing for non-existence,
+	 * return failure, otherwise continue with specified tests
+	 */
+
+	if (a_tt & TEST_NOT_EXISTS) {
+		/* testing for non-existence: return failure */
+		echoDebug(DBG_TEST_EXISTS_SHOULD_NOT, mbPath);
+		free(mbPath);
+		(void) close(fd);
+		return (R_FAILURE);
+	}
+
+	/* get the file status */
+
+	r = fstat(fd, &statbuf);
+	if (r != 0) {
+		echoDebug(DBG_PATH_DOES_NOT_EXIST, mbPath, strerror(errno));
+		(void) close(fd);
+		free(mbPath);
+		return (R_FAILURE);
+	}
+
+	/* required to be a directory? */
+
+	if (a_tt & TEST_IS_DIRECTORY) {
+		if (!(statbuf.st_mode & S_IFDIR)) {
+			/* is not a directory */
+			echoDebug(DBG_IS_NOT_A_DIRECTORY, mbPath);
+			free(mbPath);
+			return (R_FAILURE);
+		}
+		/* a directory */
+		echoDebug(DBG_DIRECTORY_IS, mbPath);
+	}
+
+	/* required to not be a directory? */
+
+	if (a_tt & TEST_NOT_DIRECTORY) {
+		if (statbuf.st_mode & S_IFDIR) {
+			/* is a directory */
+			echoDebug(DBG_IS_A_DIRECTORY, mbPath);
+			free(mbPath);
+			return (R_FAILURE);
+		}
+		/* not a directory */
+		echoDebug(DBG_DIRECTORY_NOT, mbPath);
+	}
+
+	/* required to be a file? */
+
+	if (a_tt & TEST_IS_FILE) {
+		if (!(statbuf.st_mode & S_IFREG)) {
+			/* is not a regular file */
+			echoDebug(DBG_IS_NOT_A_FILE, mbPath);
+			free(mbPath);
+			return (R_FAILURE);
+		}
+		/* a regular file */
+		echoDebug(DBG_FILE_IS, mbPath);
+	}
+
+	/* required to not be a file? */
+
+	if (a_tt & TEST_NOT_FILE) {
+		if (statbuf.st_mode & S_IFREG) {
+			/* is a regular file */
+			echoDebug(DBG_IS_A_FILE, mbPath);
+			free(mbPath);
+			return (R_FAILURE);
+		}
+		/* not a regular file */
+		echoDebug(DBG_FILE_NOT, mbPath);
+	}
+
+	/*
+	 * Find token (global) in file pointed to by mbPath.
+	 * token is only compared to first word in mbPath.
+	 */
+
+	if (a_tt & TEST_GLOBAL_TOKEN_IN_FILE) {
+		if (!(statbuf.st_mode & S_IFREG)) {
+			/* is not a regular file */
+			echoDebug(DBG_IS_NOT_A_FILE, mbPath);
+			free(mbPath);
+			return (R_FAILURE);
+		}
+		/* If global exists then we're not in a non-global zone */
+		if (findToken(mbPath, GLOBAL_ZONENAME) == R_SUCCESS) {
+			echoDebug(DBG_TOKEN__EXISTS, GLOBAL_ZONENAME, mbPath);
+			free(mbPath);
+			return (R_FAILURE);
+		}
+	}
+
+	(void) close(fd);
+
+	/* success! */
+
+	echoDebug(DBG_TESTPATH_OK, mbPath);
+
+	/* free up temp storage used to hold path to test */
+
+	free(mbPath);
+
+	return (R_SUCCESS);
+}
+
+/*
+ * Name:        findToken
+ * Description:	Find first token in file.
+ * Arguments:
+ *              path - file to search for token
+ *              token - string to search for
+ * Returns:
+ *              R_SUCCESS - the token exists
+ *              R_FAILURE - the token does not exist
+ *              R_ERROR - fatal error attempting to find token
+ */
+
+static int
+findToken(char *path, char *token)
+{
+	FILE	*fp;
+	char	*cp;
+	char	line[MAXPATHLEN];
+
+	if (path == NULL || token == NULL) {
+		return (R_ERROR);
+	}
+	if ((fp = fopen(path, "r")) == NULL) {
+		return (R_ERROR);
+	}
+
+	while (fgets(line, sizeof (line), fp) != NULL) {
+		for (cp = line; *cp && isspace(*cp); cp++);
+		/* skip comments */
+		if (*cp == '#') {
+			continue;
+		}
+		if (pkgstrContainsToken(cp, token, ":")) {
+			(void) fclose(fp);
+			return (R_SUCCESS);
+		}
+	}
+	(void) fclose(fp);
+	return (R_FAILURE);
+}
+
+
+/*
+ * Name:	resolvePath
+ * Description:	fully resolve a path to an absolute real path
+ * Arguments:	r_path - pointer to pointer to malloc()ed storage containing
+ *			the path to resolve - this path may be reallocated
+ *			as necessary to hold the fully resolved path
+ * Output:	r_path - is realloc()ed as necessary
+ * Returns:	R_SUCCESS - the path is fully resolved
+ *		R_FAILURE - the path could not be resolved
+ *		R_ERROR - fatal error attempting to resolve path
+ */
+
+static int
+resolvePath(char **r_path)
+{
+	int		i;
+	char		resolvedPath[MAXPATHLEN+1] = {'\0'};
+	size_t		mbPathlen;	/* length of multi-byte path */
+	size_t		wcPathlen;	/* length of wide-character path */
+	wchar_t		*wcPath;	/* wide-character version of the path */
+	wchar_t		*wptr;		/* scratch pointer */
+
+	/* entry assertions */
+
+	assert(r_path != (char **)NULL);
+
+	/* return error if the path is completely empty */
+
+	if (*r_path == '\0') {
+		return (R_FAILURE);
+	}
+
+	/* remove all leading whitespace */
+
+	removeLeadingWhitespace(r_path);
+
+	/*
+	 * convert to real path: an absolute pathname that names the same file,
+	 * whose resolution does not involve ".", "..",  or  symbolic links.
+	 */
+
+	if (realpath(*r_path, resolvedPath) != NULL) {
+		free(*r_path);
+		*r_path = strdup(resolvedPath);
+	}
+
+	/*
+	 *  convert the multi-byte version of the path to a
+	 *  wide-character rendering, for doing our figuring.
+	 */
+
+	mbPathlen = strlen(*r_path);
+
+	if ((wcPath = (wchar_t *)
+		calloc(1, sizeof (wchar_t)*(mbPathlen+1))) == NULL) {
+		return (R_FAILURE);
+	}
+
+	/*LINTED*/
+	if ((wcPathlen = mbstowcs(wcPath, *r_path, mbPathlen)) == -1) {
+		free(wcPath);
+		return (R_FAILURE);
+	}
+
+	/*
+	 *  remove duplicate slashes first ("//../" -> "/")
+	 */
+
+	for (wptr = wcPath, i = 0; i < wcPathlen; i++) {
+		*wptr++ = wcPath[i];
+
+		if (wcPath[i] == '/') {
+			i++;
+
+			while (wcPath[i] == '/') {
+				i++;
+			}
+
+			i--;
+		}
+	}
+
+	*wptr = '\0';
+
+	/*
+	 *  now convert back to the multi-byte format.
+	 */
+
+	/*LINTED*/
+	if (wcstombs(*r_path, wcPath, mbPathlen) == -1) {
+		free(wcPath);
+		return (R_FAILURE);
+	}
+
+	/* at this point have a path */
+
+	/* free up temporary storage */
+
+	free(wcPath);
+
+	return (R_SUCCESS);
+}
+
+/*
+ * Name:	removeLeadingWhitespace
+ * Synopsis:	Remove leading whitespace from string
+ * Description:	Remove all leading whitespace characters from a string
+ * Arguments:	a_str - [RO, *RW] - (char **)
+ *			Pointer to handle to string (in allocated storage) to
+ *			remove all leading whitespace from
+ * Returns:	void
+ *			The input string is modified as follows:
+ *			== NULL:
+ *				- input string was NULL
+ *				- input string is all whitespace
+ *			!= NULL:
+ *				- copy of input string with leading
+ *				  whitespace removed
+ * CAUTION:	The input string must be allocated space (via malloc() or
+ *		strdup()) - it must not be a static or inline character string
+ * NOTE:	The input string a_str will be freed with 'free'
+ *		if it is all whitespace, or if it contains any leading
+ *		whitespace characters
+ * NOTE:    	Any string returned is placed in new storage for the
+ *		calling method. The caller must use 'free' to dispose
+ *		of the storage once the string is no longer needed.
+ * Errors:	If the string cannot be created, the process exits
+ */
+
+static void
+removeLeadingWhitespace(char **a_str)
+{
+	char	*o_str;
+
+	/* entry assertions */
+
+	assert(a_str != (char **)NULL);
+
+	/* if string is null, just return */
+
+	if (*a_str == NULL) {
+		return;
+	}
+	o_str = *a_str;
+
+	/* if string is empty, deallocate and return NULL */
+
+	if (*o_str == '\0') {
+		/* free string */
+		free(*a_str);
+		*a_str = NULL;
+		return;
+	}
+
+	/* if first character is not a space, just return */
+
+	if (!isspace(*o_str)) {
+		return;
+	}
+
+	/* advance past all space characters */
+
+	while ((*o_str != '\0') && (isspace(*o_str))) {
+		o_str++;
+	}
+
+	/* if string was all space characters, deallocate and return NULL */
+
+	if (*o_str == '\0') {
+		/* free string */
+		free(*a_str);
+		*a_str = NULL;
+		return;
+	}
+
+	/* have non-space/null byte, return dup, deallocate original */
+
+	o_str = strdup(o_str);
+	free(*a_str);
+	*a_str = o_str;
+}
+
+/*
+ * Name:	getZoneName
+ * Description:	get the name of the zone this process is running in
+ * Arguments:	r_zoneName - pointer to pointer to receive zone name
+ * Output:	r_zoneName - a pointer to malloc()ed storage containing
+ *			the zone name this process is running in is stored
+ *			in the location pointed to by r_zoneName
+ * Returns:	R_SUCCESS - the zone name is successfully returned
+ *		R_FAILURE - the zone name is not successfully returned
+ *		R_ERROR - error attempting to get the zone name
+ */
+
+static int
+getZoneName(char **r_zoneName)
+{
+static char zoneName[ZONENAME_MAX] = { '\0' };
+
+	/* if zone name not already present, retrieve and cache name */
+
+	if (zoneName[0] == '\0') {
+		if (getzonenamebyid(getzoneid(), zoneName,
+			sizeof (zoneName)) < 0) {
+			log_msg(LOG_MSG_ERR, ERR_CANNOT_GET_ZONENAME);
+			return (R_ERROR);
+		}
+	}
+
+	/* return cached zone name */
+
+	*r_zoneName = zoneName;
+	return (R_SUCCESS);
+}
+
+/*
+ * Name:	getRootPath
+ * Description:	get the root path being tested by this process
+ * Arguments:	r_rootPath - pointer to pointer to receive root path
+ * Output:	r_rootPath - a pointer to malloc()ed storage containing
+ *			the root path name this process is testing
+ * Returns:	R_SUCCESS - the root path is successfully returned
+ *		R_FAILURE - the root path is not successfully returned
+ *		R_ERROR - error attempting to get the root path
+ */
+
+static int
+getRootPath(char **r_rootPath)
+{
+	*r_rootPath = _rootPath;
+	return (R_SUCCESS);
+}
+
+/*
+ * Name:	setVerbose
+ * Description:	Turns on verbose output
+ * Scope:	public
+ * Arguments:	verbose = B_TRUE indicates verbose mode
+ * Returns:	none
+ */
+
+static void
+setVerbose(boolean_t setting)
+{
+	/* set log verbose messages */
+
+	log_set_verbose(setting);
+
+	/* set interactive messages */
+
+	echoSetFlag(setting);
+}
+
+/*
+ * Name:	negate_results
+ * Description:	control negation of results
+ * Scope:	public
+ * Arguments:	setting
+ *		== B_TRUE indicates negated results mode
+ *		== B_FALSE indicates non-negated results mode
+ * Returns:	none
+ */
+
+static void
+setNegateResults(boolean_t setting)
+{
+	log_msg(LOG_MSG_DEBUG, DBG_SET_NEGATE_RESULTS,
+		_negateResults, setting);
+
+	_negateResults = setting;
+}
+
+/*
+ * Name:	getNegateResults
+ * Description:	Returns whether or not to results are negated
+ * Scope:	public
+ * Arguments:	none
+ * Returns:	B_TRUE - results are negated
+ *		B_FALSE - results are not negated
+ */
+
+static boolean_t
+getNegateResults(void)
+{
+	return (_negateResults);
+}
+
+/*
+ * Name:	usage
+ * Description:	output usage string
+ * Arguments:	a_format - format to use to generate message
+ *		arguments following a_format - as needed for a_format
+ * Output:	Outputs the usage string to stderr.
+ * Returns:	R_ERROR
+ */
+
+static int
+usage(char *a_format, ...)
+{
+	int		cur_cmd;
+	char		cmdlst[LINE_MAX+1] = { '\0' };
+	char		*message;
+	char		bfr[1];
+	char		*p = get_prog_name();
+	size_t		vres = 0;
+	va_list		ap;
+
+	/* entry assertions */
+
+	assert(a_format != NULL);
+	assert(*a_format != '\0');
+
+	/* determine size of the message in bytes */
+
+	va_start(ap, a_format);
+	/* LINTED warning: variable format specifier to vsnprintf(); */
+	vres = vsnprintf(bfr, 1, a_format, ap);
+	va_end(ap);
+
+	assert(vres > 0);
+
+	/* allocate storage to hold the message */
+
+	message = (char *)calloc(1, vres+2);
+	assert(message != NULL);
+
+	/* generate the results of the printf conversion */
+
+	va_start(ap, a_format);
+	/* LINTED warning: variable format specifier to vsnprintf(); */
+	vres = vsnprintf(message, vres+1, a_format, ap);
+	va_end(ap);
+
+	assert(vres > 0);
+
+	/* generate list of all defined conditions */
+
+	for (cur_cmd = 0; cmds[cur_cmd].c_name != NULL; cur_cmd++) {
+		(void) strlcat(cmdlst, "\t", sizeof (cmdlst));
+		(void) strlcat(cmdlst, cmds[cur_cmd].c_name, sizeof (cmdlst));
+		if (cmds[cur_cmd].c_args != NULL) {
+			(void) strlcat(cmdlst, cmds[cur_cmd].c_args,
+						sizeof (cmdlst));
+		}
+		(void) strlcat(cmdlst, "\n", sizeof (cmdlst));
+	}
+
+	/* output usage with conditions */
+
+	log_msg(LOG_MSG_INFO, MSG_USAGE, message, p ? p : "pkgcond", cmdlst);
+
+	return (R_ERROR);
+}
+
+/*
+ * Name:	parseGlobalData
+ * Description:	parse environment global data and store in global data structure
+ * Arguments:	a_envVar - pointer to string representing the name of the
+ *			environment variable to get and parse
+ *		r_gdt - pointer to pointer to global data structure to fill in
+ *			using the parsed data from a_envVar
+ * Output:	none
+ * Returns:	R_SUCCESS - the global data is successfully parsed
+ *		R_FAILURE - problem parsing global data
+ *		R_ERROR - fatal error attempting to parse global data
+ */
+
+static int
+parseGlobalData(char *a_envVar, GLOBALDATA_T **r_gdt)
+{
+	int		r;
+	int		n;
+	char		*a;
+	SML_TAG		*tag;
+	SML_TAG		*ntag;
+
+	assert(r_gdt != (GLOBALDATA_T **)NULL);
+
+	/*
+	 * allocate space for global data structure if needed
+	 */
+
+	if (*r_gdt == (GLOBALDATA_T *)NULL) {
+		*r_gdt = (GLOBALDATA_T *)calloc(1, sizeof (GLOBALDATA_T));
+	}
+
+	/*
+	 * get initial installation indication:
+	 * If the initial install variable is set to "true", then an initial
+	 * installation of Solaris is underway. When this condition is true:
+	 * - if the path being checked is the package install root, then
+	 *   the path is considered to be an 'alternative root' which is
+	 *   currently being installed.
+	 * - if the path being checked is not the package install root, then
+	 *   the path needs to be further analyzed to determine what it may
+	 *   be referring to.
+	 */
+
+	a = getenv(ENV_VAR_INITIAL_INSTALL);
+	if ((a != NULL) && (strcasecmp(a, "true") == 0)) {
+		(*r_gdt)->gd_initialInstall = B_TRUE;
+	}
+
+	/* get current zone name */
+
+	r = getZoneName(&(*r_gdt)->gd_zoneName);
+	if (r != R_SUCCESS) {
+		(*r_gdt)->gd_zoneName = "";
+	}
+
+	/*
+	 * get zone installation status:
+	 * - If the package install zone name is not set, then an installation
+	 *   of a global zone, or of a non-global zone, is not underway.
+	 * - If the package install zone name is set to "global", then an
+	 *   installation of a global zone is underway. In this case, no path
+	 *   can be a netinstall image, diskless client, mounted miniroot,
+	 *   non-global zone, the current running system, alternative root,
+	 *   or alternative boot environment.
+	 * - If the package install zone name is set to a value other than
+	 *   "global", then an installation of a non-global zone with that name
+	 *   is underway.  In this case, no path can be a netinstall image,
+	 *   diskless client, mounted miniroot, global zone, the current
+	 *   running system, alternative root, or alternative boot environment.
+	 */
+
+	a = getenv(ENV_VAR_PKGZONENAME);
+	if ((a == NULL) || (*a == '\0')) {
+		/* not installing a zone */
+		(*r_gdt)->gd_globalZoneInstall = B_FALSE;
+		(*r_gdt)->gd_nonglobalZoneInstall = B_FALSE;
+	} else if (strcmp(a, GLOBAL_ZONENAME) == 0) {
+		/* installing a global zone */
+		(*r_gdt)->gd_globalZoneInstall = B_TRUE;
+		(*r_gdt)->gd_nonglobalZoneInstall = B_FALSE;
+		(*r_gdt)->gd_zoneName = a;
+	} else {
+		/* installing a non-global zone by that name */
+		(*r_gdt)->gd_globalZoneInstall = B_FALSE;
+		(*r_gdt)->gd_nonglobalZoneInstall = B_TRUE;
+		(*r_gdt)->gd_zoneName = a;
+	}
+
+	/*
+	 * get package install root. If it doesn't exist check for
+	 * patch install root (ROOTDIR)
+	 */
+
+	a = getenv(ENV_VAR_PKGROOT);
+	if ((a != NULL) && (*a != '\0')) {
+		(*r_gdt)->gd_installRoot = a;
+	} else {
+		a = getenv(ENV_VAR_PATCHROOT);
+		if ((a != NULL) && (*a != '\0')) {
+			(*r_gdt)->gd_installRoot = a;
+		} else {
+			(*r_gdt)->gd_installRoot = "/";
+		}
+	}
+
+	/*
+	 * get patch client version: always set if $ROOTDIR != / and
+	 * the $ROOTDIR/var/sadm/softinfo/INST_RELEASE file exists.
+	 */
+
+	a = getenv(ENV_VAR_PATCH_CLIENTVER);
+	(*r_gdt)->gd_patchClientVersion = (a ? a : "");
+
+	/* get the global data environment variable */
+
+	a = getenv(a_envVar);
+
+	/* if no data then issue warning and return success */
+
+	if ((a == NULL) || (*a_envVar == '\0')) {
+		log_msg(LOG_MSG_DEBUG, DBG_NO_GLOBAL_DATA_AVAILABLE, a_envVar);
+		return (R_SUCCESS);
+	}
+
+	/* data present - parse into SML structure */
+
+	log_msg(LOG_MSG_DEBUG, DBG_PARSE_GLOBAL, a);
+
+	r = smlConvertStringToTag(&tag, a);
+	if (r != R_SUCCESS) {
+		log_msg(LOG_MSG_ERR, ERR_CANNOT_PARSE_GLOBAL_DATA, a);
+		return (R_FAILURE);
+	}
+
+	smlDbgPrintTag(tag, DBG_PARSED_ENVIRONMENT, a_envVar);
+
+	/* fill in global data structure */
+
+	/* find the environment condition information structure */
+
+	ntag = smlGetTagByName(tag, 0, TAG_COND_TOPLEVEL);
+	if (ntag == SML_TAG__NULL) {
+		log_msg(LOG_MSG_WRN, WRN_PARSED_DATA_MISSING,
+			TAG_COND_TOPLEVEL);
+		return (R_FAILURE);
+	}
+
+	/*
+	 * data found - extract what we know about
+	 */
+
+	/* parent zone name */
+
+	a = smlGetParamByTag(ntag, 0, TAG_COND_PARENT_ZONE, TAG_COND_ZONE_NAME);
+	(*r_gdt)->gd_parentZoneName = a;
+
+	/* parent zone type */
+
+	a = smlGetParamByTag(ntag, 0, TAG_COND_PARENT_ZONE, TAG_COND_ZONE_TYPE);
+	(*r_gdt)->gd_parentZoneType = a;
+
+	/* current zone name */
+
+	a = smlGetParamByTag(ntag, 0, TAG_COND_CURRENT_ZONE,
+		TAG_COND_ZONE_NAME);
+	(*r_gdt)->gd_currentZoneName = a;
+
+	/* current zone type */
+
+	a = smlGetParamByTag(ntag, 0, TAG_COND_CURRENT_ZONE,
+		TAG_COND_ZONE_TYPE);
+	(*r_gdt)->gd_currentZoneType = a;
+
+	/* extract any inherited file systems */
+
+	for (n = 0; ; n++) {
+		char	**ifs;
+
+		a = smlGetParamByTag(ntag, n, TAG_COND_INHERITED_FS,
+			TAG_COND_FS_NAME);
+
+		if (a == NULL) {
+			if (n > 0) {
+				/* LINTED warning: variable may be used */
+				(*r_gdt)->gd_inheritedFileSystems = ifs;
+			}
+			break;
+		}
+
+		if (n == 0) {
+			ifs = (char **)calloc(1, sizeof (char **)*(n+2));
+			ifs[n] = a;
+			ifs[n+1] = NULL;
+		} else {
+			ifs = (char **)realloc(ifs, sizeof (char **)*(n+2));
+			ifs[n] = a;
+			ifs[n+1] = NULL;
+		}
+	}
+
+	return (R_SUCCESS);
+}
+
+/*
+ * Name:	dumpGlobalData
+ * Description:	dump global data structure using echoDebug
+ * Arguments:	a_gdt - pointer to global data structure to dump
+ * Outputs:	echoDebug is called to output global data strucutre information
+ * Returns:	void
+ */
+
+static void
+dumpGlobalData(GLOBALDATA_T *a_gdt)
+{
+	/* entry assertions */
+
+	assert(a_gdt != (GLOBALDATA_T *)NULL);
+
+	/* debugging enabled, dump the global data structure */
+
+	echoDebug(DBG_DUMP_GLOBAL_ENTRY);
+	echoDebug(DBG_DUMP_GLOBAL_PARENT_ZONE,
+		a_gdt->gd_parentZoneName ? a_gdt->gd_parentZoneName : "",
+		a_gdt->gd_parentZoneType ? a_gdt->gd_parentZoneType : "");
+	echoDebug(DBG_DUMP_GLOBAL_CURRENT_ZONE,
+		a_gdt->gd_currentZoneName ? a_gdt->gd_currentZoneName : "",
+		a_gdt->gd_currentZoneType ? a_gdt->gd_currentZoneType : "");
+
+	if (a_gdt->gd_inheritedFileSystems) {
+		int n;
+		char **ifs = a_gdt->gd_inheritedFileSystems;
+		for (n = 0; ifs[n]; n++) {
+			echoDebug(DBG_DUMP_GLOBAL_LINE, n, ifs[n]);
+		}
+	}
+}
+
+/*
+ * Name:	recursionCheck
+ * Description:	prevent recursive calling of functions
+ * Arguments:	r_recursion - pointer to int recursion counter
+ *		a_function - pointer to name of function
+ * Returns:	B_TRUE - function is recursively called
+ *		B_FALSE - function not recursively called
+ */
+
+static boolean_t
+recursionCheck(int *r_recursion, char *a_function)
+{
+	/* prevent recursion */
+
+	(*r_recursion)++;
+	if (*r_recursion > 1) {
+		echoDebug(DBG_RECURSION, a_function, *r_recursion);
+		(*r_recursion)--;
+		return (B_TRUE);
+	}
+
+	echoDebug(DBG_NO_RECURSION, a_function);
+	return (B_FALSE);
+}
+
+/*
+ * Name:	quit
+ * Description:	cleanup and exit
+ * Arguments:	a_retcode - the code to use to determine final exit status;
+ *			if this is NOT "99" and if a "ckreturnFunc" is
+ *			set, then that function is called with a_retcode
+ *			to set the final exit status.
+ *		Valid values are:
+ *		0 - success
+ *		1 - package operation failed (fatal error)
+ *		2 - non-fatal error (warning)
+ *		3 - user selected quit (operation interrupted)
+ *		4 - admin settings prevented operation
+ *		5 - interaction required and -n (non-interactive) specified
+ *		"10" is added to indicate "immediate reboot required"
+ *		"20" is be added to indicate "reboot after install required"
+ *		99 - do not interpret the code - just exit "99"
+ * Returns:	<<this function does not return - calls exit()>>
+ * NOTE:	This is needed because libinst functions can call "quit(99)"
+ *		to force an error exit.
+ */
+
+void
+quit(int a_retcode)
+{
+	/* process return code if not quit(99) */
+
+	if (a_retcode == 99) {
+		exit(0x7f);	/* processing error (127) */
+	}
+
+	exit(R_FAILURE);
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/usr/src/cmd/svr4pkg/pkgcond/pkgcond.h	Fri Jun 05 10:28:40 2009 -0400
@@ -0,0 +1,65 @@
+/*
+ * 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
+ * or http://www.opensolaris.org/os/licensing.
+ * 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 2006 Sun Microsystems, Inc.  All rights reserved.
+ * Use is subject to license terms.
+ */
+
+#ifndef _PKGCOND_H
+#define	_PKGCOND_H
+
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/*
+ * global definitions
+ */
+
+/* environment variable */
+#define	ENV_VAR_DEBUG		"PKGCOND_DEBUG"
+#define	ENV_VAR_PKGROOT		"PKG_INSTALL_ROOT"
+#define	ENV_VAR_PATCHROOT	"ROOTDIR"
+#define	ENV_VAR_SET		"SET_FROM_ENVIRONMENT"
+#define	ENV_VAR_VERBOSE		"PKGCOND_VERBOSE"
+#define	ENV_VAR_PKGZONENAME	"SUNW_PKG_INSTALL_ZONENAME"
+#define	ENV_VAR_INITIAL_INSTALL	"PKG_INIT_INSTALL"
+#define	ENV_VAR_PATCH_CLIENTVER	"PATCH_CLIENT_VERSION"
+
+/* file system types */
+#define	FSTYPE_INHERITED	"inherited"
+
+/* return codes used with pkgcond itself */
+#define	R_SUCCESS	0x0	/* condition match / success */
+#define	R_FAILURE	0x1	/* condition no match / failure */
+#define	R_USAGE		0x2	/* command usage issue */
+#define	R_ERROR		0x3	/* could not determine condition / error */
+
+/* main.c */
+int	main(int argc, char **argv);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* _PKGCOND_H */
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/usr/src/cmd/svr4pkg/pkgcond/pkgcond_msgs.h	Fri Jun 05 10:28:40 2009 -0400
@@ -0,0 +1,498 @@
+/*
+ * 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
+ * or http://www.opensolaris.org/os/licensing.
+ * 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 2007 Sun Microsystems, Inc.  All rights reserved.
+ * Use is subject to license terms.
+ */
+
+#ifndef	_PKGCOND_MSGS_H
+#define	_PKGCOND_MSGS_H
+
+
+#include <libintl.h>
+
+#ifdef	__cplusplus
+extern "C" {
+#endif
+
+#ifdef	lint
+#define	gettext(x)	x
+#endif
+
+/* generic messages */
+
+#define	MSG_USAGE						gettext(\
+"%s; usage is:\n" \
+"\t%s [-nv] <condition> [ <option(s)> ]\n" \
+"\n" \
+"command options:\n" \
+"\t-n - negate results of condition test\n" \
+"\t-v - verbose output of condition testing\n" \
+"\n" \
+"<condition> may be any one of:\n" \
+"%s\n" \
+"<option(s)> are specific to the condition used\n" \
+"\n" \
+"pkgcond -?\n" \
+"\t- Shows this help message\n")
+
+#define	MSG_NO_PKG_ENV_DATA_PRESENT				gettext(\
+"no data available from package tools: zone information may be incomplete")
+
+#define	MSG_NO_ARGUMENTS_SPECIFIED				gettext(\
+"no condition to check specified")
+
+#define	MSG_INVALID_OPTION_SPECIFIED				gettext(\
+"option <%c> not recognized")
+
+#define	MSG_IS_INVALID_OPTION					gettext(\
+"option <%c> not recognized by condition <%s>")
+
+#define	MSG_UNRECOGNIZED_CONDITION_SPECIFIED			gettext(\
+"condition not recognized")
+
+#define	MSG_IS_WHAT_RESULT					gettext(\
+"%s=%d")
+
+/* debugging messages */
+
+#define	DBG_NO_RECURSION					gettext(\
+"nonrecursive call to <%s>")
+
+#define	DBG_RECURSION						gettext(\
+"recursive call to <%s> count <%d> ignored")
+
+#define	DBG_TESTPATH_OK						gettext(\
+"path <%s> matches all criteria")
+
+#define	DBG_ADDV_PATH_IS_SYMLINK				gettext(\
+"cannot add driver to path <%s>: <%s> does not exist or exists but " \
+"is a symbolic link")
+
+#define	DBG_ADDV_YES						gettext(\
+"root path <%s> can have a driver added")
+
+#define	DBG_UPDV_PATH_IS_SYMLINK				gettext(\
+"cannot update driver to path <%s>: <%s> does not exist or exists but " \
+"is a symbolic link")
+
+#define	DBG_UPDV_YES						gettext(\
+"root path <%s> can have a driver updated")
+
+#define	DBG_RMDV_PATH_IS_SYMLINK				gettext(\
+"cannot remove driver to path <%s>: <%s> does not exist or exists but " \
+"is a symbolic link")
+
+#define	DBG_RMDV_YES						gettext(\
+"root path <%s> can have a driver removed")
+
+#define	DBG_ROOTPATH_IS						gettext(\
+"root path is <%s>")
+
+#define	DBG_CANNOT_ACCESS_PATH_BUT_SHOULD			gettext(\
+"test_path: path <%s> must exist and does not: %s")
+
+#define	DBG_CANNOT_ACCESS_PATH_OK				gettext(\
+"test_path: path <%s> must not (and does not) exist")
+
+#define	DBG_PATH_DOES_NOT_EXIST					gettext(\
+"test_path: path <%s> does not exist: %s")
+
+#define	DBG_CANNOT_LSTAT_PATH					gettext(\
+"test_path: cannot lstat path <%s>: %s")
+
+#define	DBG_IS_A_DIRECTORY					gettext(\
+"test_path: path <%s> is a directory but is not supposed to be")
+
+#define	DBG_IS_NOT_A_DIRECTORY					gettext(\
+"test_path: path <%s> is not a directory but is supposed to be")
+
+#define	DBG_DIRECTORY_NOT					gettext(\
+"test_path: path <%s> is not a directory")
+
+#define	DBG_DIRECTORY_IS					gettext(\
+"test_path: path <%s> is a directory")
+
+#define	DBG_IS_A_FILE						gettext(\
+"test_path: path <%s> is a file but is not supposed to be")
+
+#define	DBG_IS_NOT_A_FILE					gettext(\
+"test_path: path <%s> is not a file but is supposed to be")
+
+#define	DBG_TOKEN__EXISTS					gettext(\
+"test_path: token <%s> exists in path <%s>")
+
+#define	DBG_FILE_NOT						gettext(\
+"test_path: path <%s> is not a file")
+
+#define	DBG_FILE_IS						gettext(\
+"test_path: path <%s> is a file")
+
+#define	DBG_IS_A_SYMLINK					gettext(\
+"test_path: path <%s> is a symlink but is not supposed to be")
+
+#define	DBG_IS_NOT_A_SYMLINK					gettext(\
+"test_path: path <%s> is not a symlink but is supposed to be")
+
+#define	DBG_SORTEDINS_SKIPPED					gettext(\
+"duplicate entry <%d> : <%s> (<%s> vs <%s>, <%s> vs <%s>): merged options")
+
+#define	DBG_SYMLINK_NOT						gettext(\
+"test_path: path <%s> is not a symlink")
+
+#define	DBG_SYMLINK_IS						gettext(\
+"test_path: path <%s> is a symlink")
+
+#define	DBG_SET_NEGATE_RESULTS					gettext(\
+"set_negate_results: current setting <%d> new setting <%d>")
+
+#define	DBG_ADJUST_RESULTS					gettext(\
+"adjust_results: result <%d> negate <%d> returned result <%d>")
+
+#define	DBG_PARSE_GLOBAL					gettext(\
+"parsing global data <%s>")
+
+#define	DBG_NO_DEFAULT_ROOT_PATH_SET				gettext(\
+"no default root path set")
+
+#define	DBG_DEFAULT_ROOT_PATH_SET				gettext(\
+"default root path <%s> set from environment variable <%s>")
+
+#define	DBG_RESULTS						gettext(\
+"returning results <%d>")
+
+#define	DBG_SET_ROOT_PATH_TO					gettext(\
+"setting root path to <%s>")
+
+#define	DBG_TEST_PATH						gettext(\
+"test path <%s> flags <0x%08lx>")
+
+#define	DBG_TEST_PATH_NO_RESOLVE				gettext(\
+"cannot resolve path <%s>")
+
+#define	DBG_TEST_PATH_RESOLVE					gettext(\
+"test resolved path <%s>")
+
+#define	DBG_TEST_EXISTS_SHOULD_NOT				gettext(\
+"path <%s> exists but should not")
+
+#define	DBG_PARSED_ENVIRONMENT					gettext(\
+"global data parsed from environment variable <%s>")
+
+#define	DBG_DUMP_GLOBAL_LINE					gettext(\
+"inherited file system <%d> is <%s>")
+
+#define	DBG_DUMP_GLOBAL_ENTRY					gettext(\
+"global data settings")
+
+#define	DBG_DUMP_GLOBAL_PARENT_ZONE				gettext(\
+"parentzone zoneName <%s> zoneType <%s>")
+
+#define	DBG_DUMP_GLOBAL_CURRENT_ZONE				gettext(\
+"currentzone zoneName <%s> zoneType <%s>")
+
+#define	DBG_IDLC_INITIAL_INSTALL				gettext(\
+"path <%s> is not a diskless client: initial installation in progress")
+
+#define	DBG_IDLC_ZONE_INSTALL					gettext(\
+"path <%s> is not a diskless client: initial zone installation in progress")
+
+#define	DBG_IDLC_PKG_NOT_INSTALLED				gettext(\
+"path <%s> is not a diskless client: package <%s> is not installed in <%s>")
+
+#define	DBG_IDLC_ROOTPATH_BAD					gettext(\
+"path <%s> is not a diskless client: root path cannot be <%s>")
+
+#define	DBG_IDLC_ZONE_BAD					gettext(\
+"path <%s> is not a diskless client: current zone must be <%s>")
+
+#define	DBG_IDLC_PATH_MISSING					gettext(\
+"path <%s> is not a diskless client: <%s> does not exist")
+
+#define	DBG_IDLC_USR_IS_NOT_EMPTY				gettext(\
+"path <%s> is not a diskless client: </usr> is not empty")
+
+#define	DBG_IDLC_NO_TEMPLATES_PATH				gettext(\
+"path <%s> is not a diskless client: <%s/%s> does not exist")
+
+#define	DBG_IDLC_PATH_IS_DISKLESS_CLIENT			gettext(\
+"path <%s> is a diskless client")
+
+#define	DBG_ISGZ_INITIAL_INSTALL				gettext(\
+"path <%s> is not a global zone: initial installation in progress")
+
+#define	DBG_ISGZ_NGZ_ZONE_INSTALL				gettext(\
+"path <%s> is not a global zone: initial non-global zone " \
+"installation in progress")
+
+#define	DBG_ISGZ_PATH_IS_GLOBAL_ZONE				gettext(\
+"path <%s> is a global zone")
+
+#define	DBG_ISGZ_PATH_ISNT_DIRECTORY				gettext(\
+"path <%s> is not a global zone: directory <%s> does not exist")
+
+#define	DBG_ISGZ_PATH_EXISTS					gettext(\
+"path <%s> is not a global zone: <%s> exists")
+
+#define	DBG_ISGZ_ZONENAME_ISNT_GLOBAL				gettext(\
+"path <%s> is not a global zone: zone name <%s> is not <global>")
+
+#define	DBG_ISGZ_PATH_IS_SYMLINK				gettext(\
+"path <%s> is not a global zone: <%s> does not exist or exists but " \
+"is a symbolic link")
+
+#define	DBG_INIM_INITIAL_INSTALL				gettext(\
+"path <%s> is not a netinstall image: initial installation in progress")
+
+#define	DBG_INIM_ZONE_INSTALL					gettext(\
+"path <%s> is not a netinstall image: initial zone installation in progress")
+
+#define	DBG_INIM_PATH_IS_NETINSTALL_IMAGE			gettext(\
+"path <%s> is a netinstall image")
+
+#define	DBG_INIM_BAD_CURRENT_ZONE				gettext(\
+"path <%s> is not a netinstall image: current zone is not <%s>")
+
+#define	DBG_INIM_PATH_ISNT_SYMLINK				gettext(\
+"path <%s> is not a netinstall image: <%s> does not exist or exists " \
+"but is not a symbolic link")
+
+#define	DBG_INIM_PATH_ISNT_DIRECTORY				gettext(\
+"path <%s> is not a netinstall image: <%s> does not exist or " \
+"is not a directory")
+
+#define	DBG_IMRT_INITIAL_INSTALL				gettext(\
+"path <%s> is not a mounted miniroot image: initial installation in progress")
+
+#define	DBG_IMRT_ZONE_INSTALL					gettext(\
+"path <%s> is not a mounted miniroot image: initial zone " \
+"installation in progress")
+
+#define	DBG_IMRT_PATH_IS_MOUNTED_MINIROOT			gettext(\
+"path <%s> is a mounted miniroot")
+
+#define	DBG_IMRT_BAD_CURRENT_ZONE				gettext(\
+"path <%s> is not a mounted miniroot image: current zone is not <%s>")
+
+#define	DBG_IMRT_ROOTDIR_BAD					gettext(\
+"path <%s> is not a mounted miniroot image: root directory is not <%s>")
+
+#define	DBG_IMRT_PATH_ISNT_SYMLINK				gettext(\
+"path <%s> is not a mounted miniroot image: <%s> does not exist or is " \
+" not a symbolic link")
+
+#define	DBG_IMRT_PATH_ISNT_DIRECTORY				gettext(\
+"path <%s> is not a netinstall image: <%s> does not exist or is not " \
+" a directory")
+
+#define	DBG_NGZN_INITIAL_INSTALL				gettext(\
+"path <%s> is not a non-global zone: initial installation in progress")
+
+#define	DBG_NGZN_GLOBAL_ZONE_INSTALL				gettext(\
+"path <%s> is not a non-global zone: initial global zone " \
+"installation in progress")
+
+#define	DBG_NGZN_IN_GZ_IS_NONGLOBAL_ZONE			gettext(\
+"path <%s> is a non-global zone: running in global zone")
+
+#define	DBG_NGZN_PARENT_CHILD_SAMEZONE				gettext(\
+"path <%s> is a non-global zone: parent/child are same zone name <%s>")
+
+#define	DBG_NGZN_IS_NONGLOBAL_ZONE				gettext(\
+"path <%s> is a non-global zone")
+
+#define	DBG_NGZN_ZONENAME_ISNT_NGZ				gettext(\
+"path <%s> is not a non-global zone: zone name is <%s>")
+
+#define	DBG_NGZN_INSTALL_ZONENAME_IS_NGZ			gettext(\
+"path <%s> is a non-global zone: installation of non-global zone name is <%s>")
+
+#define	DBG_NGZN_ZONENAME_IS_NGZ				gettext(\
+"path <%s> is a non-global zone: zone name is <%s>")
+
+#define	DBG_NGZN_PATH_EXISTS					gettext(\
+"path <%s> is not a non-global zone: <%s> exists")
+
+#define	DBG_NGZN_BAD_PARENT_ZONETYPE				gettext(\
+"path <%s> is not a non-global zone: parent zone type is <%s>")
+
+#define	DBG_NGZN_BAD_CURRENT_ZONETYPE				gettext(\
+"path <%s> is not a non-global zone: current zone type is <%s>")
+
+#define	DBG_NGZN_PATH_DOES_NOT_EXIST				gettext(\
+"path <%s> is not a non-global zone: <%s> does not exist or exists but " \
+"is a symbolic link")
+
+#define	DBG_IRST_INITIAL_INSTALL				gettext(\
+"path <%s> is not the current running system: initial installation in progress")
+
+#define	DBG_IRST_ZONE_INSTALL					gettext(\
+"path <%s> is not the current running system: initial zone installation " \
+"in progress")
+
+#define	DBG_IRST_PATH_IS_RUNNING_SYSTEM				gettext(\
+"path <%s> is a running system")
+
+#define	DBG_IRST_ZONE_BAD					gettext(\
+"path <%s> is not the current running system: the current zone name " \
+" is not <%s>")
+
+#define	DBG_IRST_ROOTPATH_BAD					gettext(\
+"path <%s> is not the current running system: root path is not <%s>")
+
+#define	DBG_IALR_INITIAL_INSTALL				gettext(\
+"path <%s> is an alternative root: initial installation in progress")
+
+#define	DBG_IALR_ZONE_INSTALL					gettext(\
+"path <%s> is not an alternative root: initial zone installation in progress")
+
+#define	DBG_IALR_PATH_DOES_NOT_EXIST				gettext(\
+"path <%s> is not an alternative root: <%s> does not exist or exists but " \
+"is a symbolic link")
+
+#define	DBG_IALR_BAD_ROOTPATH					gettext(\
+"path <%s> is not an alternative root: root directory is <%s>")
+
+#define	DBG_IALR_IS						gettext(\
+"root path <%s> is an alternative root")
+
+#define	DBG_WRNG_IS						gettext(\
+"root path <%s> is a whole root non-global zone")
+
+#define	DBG_WRNG_IS_NOT						gettext(\
+"root path <%s> is not a whole root non-global zones: " \
+"file systems are inherited")
+
+#define	DBG_SRNG_IS_NOT						gettext(\
+"root path <%s> is not a sparse root non-global zones: " \
+"file systems are not inherited")
+
+#define	DBG_SRNG_IS						gettext(\
+"root path <%s> is a sparse root non-global zone")
+
+#define	DBG_BENV_INITIAL_INSTALL				gettext(\
+"path <%s> is not an alternative boot environment: initial " \
+"installation in progress")
+
+#define	DBG_BENV_ZONE_INSTALL					gettext(\
+"path <%s> is not an alternative boot environment: initial zone " \
+"installation in progress")
+
+#define	DBG_BENV_IS						gettext(\
+"path <%s> is an alternative boot environment")
+
+#define	DBG_BENV_NO_ETCLU					gettext(\
+"path <%s> is not an alternative boot environment: <%s> does " \
+"not exist or is not a directory")
+
+#define	DBG_BENV_NO_ETCLUTAB					gettext(\
+"path <%s> is not an alternative boot environment: <%s> does not exist")
+
+#define	DBG_BENV_BAD_ZONE					gettext(\
+"path <%s> is not an alternative boot environment: " \
+"the current zone name is not <%s>")
+
+#define	DBG_BENV_BAD_ROOTPATH					gettext(\
+"path <%s> is not an alternative boot environment: root directory is <%s>")
+
+#define	DBG_PWRT_INHERITED					gettext(\
+"root path <%s> is not writeable: is inherited with <%s>")
+
+#define	DBG_PWRT_READONLY					gettext(\
+"root path <%s> is not writeable: is read only <%s>")
+
+#define	DBG_PWRT_IS						gettext(\
+"root path <%s> is writeable")
+
+#define	DBG_PWRT_INFO						gettext(\
+"root path <%s> is mount point <%s> fstype <%s> options <%s>")
+
+#define	DBG_NO_GLOBAL_DATA_AVAILABLE				gettext(\
+"no global data available in environment variable <%s>")
+
+#define	DBG_CKSR_FSREADONLY					gettext(\
+"file system <%s> type <%s> is read-only")
+
+#define	DBG_CALCSCFG_ENTRY					gettext(\
+"analyzing inherited and mounted file systems")
+
+#define	DBG_CALCSCFG_INHERITED					gettext(\
+"analyzing inherited file systems")
+
+#define	DBG_CALCSCFG_MOUNTED					gettext(\
+"analyzing mounted file systems")
+
+#define	DBG_SINS_ENTRY						gettext(\
+"inserting mount point <%s> type <%s> options <%s>")
+
+#define	DBG_NGZN_PATH_EXISTS					gettext(\
+"path <%s> is not a non-global zone: <%s> exists")
+
+#define	DBG_CMDLINE_PATH					gettext(\
+"command line path to check set to: <%s>")
+
+/* warnings */
+
+#define	WRN_PARSED_DATA_MISSING					gettext(\
+"available global data missing <%s>")
+
+/* errors */
+
+#define	MSG_FATAL						gettext(\
+	"Fatal Error")
+
+#define	ERR_REQUIRED_ROOTPATH_MISSING				gettext(\
+"the <%s> condition requires a root path to be specified")
+
+#define	ERR_CANNOT_GET_ZONENAME					gettext(\
+"could not determine zone name")
+
+#define	ERR_CANNOT_CALC_FS_CONFIG				gettext(\
+"cannot calculate file system config")
+
+#define	ERR_CANNOT_PARSE_GLOBAL_DATA				gettext(\
+"cannot parse global data SML: <%s>")
+
+#define	ERR_UNRECOGNIZED_OPTION					gettext(\
+"unrecognized option <%s>")
+
+#define	ERR_DEFAULT_ROOT_INVALID				gettext(\
+"cannot set root path to <%s>: %s")
+
+#define	ERR_DEFAULT_ROOT_NOT_DIR				gettext(\
+"cannot set root path to <%s>: not a directory")
+
+#define	ERR_CANNOT_SET_ROOT_PATH				gettext(\
+"cannot set root path from environment variable <%s>")
+
+#define	ERR_CANNOT_USE_GLOBAL_DATA				gettext(\
+"global data from environment variable <%s> cannot be used to determine " \
+"conditions and capabilities")
+
+#define	ERR_BAD_SUB						gettext(\
+	"\"%s\" is not a valid condition")
+
+#ifdef	__cplusplus
+}
+#endif
+
+#endif /* _PKGCOND_MSGS_H */
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/usr/src/cmd/svr4pkg/pkginfo/Makefile	Fri Jun 05 10:28:40 2009 -0400
@@ -0,0 +1,42 @@
+#
+# 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
+# or http://www.opensolaris.org/os/licensing.
+# 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 2009 Sun Microsystems, Inc.  All rights reserved.
+# Use is subject to license terms.
+#
+
+PROG=		pkginfo
+
+OBJS=		pkginfo.o
+SRCS=		$(OBJS:.o=.c)
+
+include $(SRC)/cmd/svr4pkg/Makefile.svr4pkg
+
+LDLIBS +=	-lpkg -linstzones -ladm
+
+
+.KEEP_STATE:
+all:            $(PROG)
+
+install:	all $(ROOTPROG)
+
+include $(SRC)/cmd/svr4pkg/Makefile.svr4pkg.targ
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/usr/src/cmd/svr4pkg/pkginfo/pkginfo.c	Fri Jun 05 10:28:40 2009 -0400
@@ -0,0 +1,821 @@
+/*
+ * 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
+ * or http://www.opensolaris.org/os/licensing.
+ * 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 2009 Sun Microsystems, Inc.  All rights reserved.
+ * Use is subject to license terms.
+ */
+
+/* Copyright (c) 1984, 1986, 1987, 1988, 1989 AT&T */
+/* All Rights Reserved */
+
+
+#define	__EXTENTIONS__
+
+#include <stdio.h>
+#include <limits.h>
+#include <unistd.h>
+#include <stdlib.h>
+#include <locale.h>
+#include <libintl.h>
+#include <strings.h>
+#include <string.h>
+#include <dirent.h>
+#include <sys/param.h>
+#include <sys/stat.h>
+#include <pkginfo.h>
+#include <fcntl.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <sys/param.h>
+#include <sys/mman.h>
+#include <pkgstrct.h>
+#include <pkglocs.h>
+#include <errno.h>
+#include <ctype.h>
+
+#include <pkglib.h>
+#include <instzones_api.h>
+#include <libadm.h>
+#include <libinst.h>
+
+extern char	*pkgdir;
+extern int	pkginfofind(char *path, char *pkg_dir, char *pkginst);
+
+#define	ERR_USAGE	"usage:\n" \
+			"%s [-q] [-pi] [-x|l] [options] [pkg ...]\n" \
+			"%s -d device [-q] [-x|l] [options] [pkg ...]\n" \
+			"where\n" \
+			"  -q #quiet mode\n" \
+			"  -p #select partially installed packages\n" \
+			"  -i #select completely installed packages\n" \
+			"  -x #extracted listing\n" \
+			"  -l #long listing\n" \
+			"  -r #relocation base \n" \
+			"and options may include:\n" \
+			"  -c category, [category...]\n" \
+			"  -a architecture\n" \
+			"  -v version\n"
+
+#define	ERR_INCOMP0	"-L and -l/-x/-r flags are incompatible"
+#define	ERR_INCOMP1	"-l and -x/-r flags are not compatible"
+#define	ERR_INCOMP2	"-x and -l/-r flags are not compatible"
+#define	ERR_INCOMP3	"-r and -x/-x flags are not compatible"
+#define	ERR_NOINFO	"ERROR: information for \"%s\" was not found"
+#define	ERR_NOPINFO	"ERROR: No partial information for \"%s\" was found"
+#define	ERR_BADINFO	"pkginfo file is corrupt or missing"
+#define	ERR_ROOT_SET	"Could not set install root from the environment."
+#define	ERR_ROOT_CMD	"Command line install root contends with environment."
+
+/* Format for dumping package attributes in dumpinfo() */
+#define	FMT	"%10s:  %s\n"
+#define	SFMT	"%-11.11s %-*.*s %s\n"
+#define	CFMT	"%*.*s  "
+#define	XFMT	"%-*.*s  %s\n"
+
+#define	nblock(size)	((size + (DEV_BSIZE - 1)) / DEV_BSIZE)
+#define	MAXCATG	64
+
+static char	*device = NULL;
+static char	*parmlst[] = {
+	"DESC", "PSTAMP", "INSTDATE", "VSTOCK", "SERIALNUM", "HOTLINE",
+	"EMAIL", NULL
+};
+
+static char	contents[PATH_MAX];
+static int	errflg = 0;
+static int	qflag = 0;
+static int	iflag = -1;
+static int	pflag = -1;
+static int	lflag = 0;
+static int	Lflag = 0;
+static int	Nflag = 0;
+static int	xflag = 0;
+static int	rflag = 0; 		/* bug # 1081606 */
+static struct cfent	entry;
+static char	**pkg = NULL;
+static int	pkgcnt = 0;
+static char	*ckcatg[MAXCATG] = {NULL};
+static int	ncatg = 0;
+static char	*ckvers = NULL;
+static char	*ckarch = NULL;
+
+static struct cfstat {
+	char	pkginst[32];
+	short	exec;
+	short	dirs;
+	short	link;
+	short	partial;
+	long	spooled;
+	long	installed;
+	short	info;
+	short	shared;
+	short	setuid;
+	long	tblks;
+	struct cfstat *next;
+} *data;
+static struct pkginfo info;
+
+static struct	cfstat *fpkg(char *pkginst);
+static int	iscatg(char *list);
+static int	selectp(char *p);
+static void	usage(void), look_for_installed(void),
+		report(void), rdcontents(void);
+static void	pkgusage(struct cfstat *dp, struct cfent *pentry);
+static void	getinfo(struct cfstat *dp);
+static void	dumpinfo(struct cfstat *dp, int pkgLngth);
+
+int
+main(int argc, char **argv)
+{
+	int	c;
+
+	pkgdir = NULL;
+	setErrstr(NULL);
+
+	/* initialize locale mechanism */
+
+	(void) setlocale(LC_ALL, "");
+
+#if !defined(TEXT_DOMAIN)	/* Should be defined by cc -D */
+#define	TEXT_DOMAIN "SYS_TEST"
+#endif
+	(void) textdomain(TEXT_DOMAIN);
+
+	/* determine program name */
+
+	(void) set_prog_name(argv[0]);
+
+	/* tell spmi zones interface how to access package output functions */
+
+	z_set_output_functions(echo, echoDebug, progerr);
+
+	/* establish installation root directory */
+
+	if (!set_inst_root(getenv("PKG_INSTALL_ROOT"))) {
+		progerr(gettext(ERR_ROOT_SET));
+		exit(1);
+	}
+
+	while ((c = getopt(argc, argv, "LNR:xv:a:d:qrpilc:?")) != EOF) {
+		switch (c) {
+		    case 'v':
+			ckvers = optarg;
+			break;
+
+		    case 'a':
+			ckarch = optarg;
+			break;
+
+		    case 'd':
+			/* -d could specify stream or mountable device */
+			device = flex_device(optarg, 1);
+			break;
+
+		    case 'q':
+			qflag++;
+			break;
+
+		    case 'i':
+			iflag = 1;
+			if (pflag > 0)
+				usage();
+			pflag = 0;
+			break;
+
+		    case 'p':
+			pflag = 1;
+			if (iflag > 0)
+				usage();
+			iflag = 0;
+			break;
+
+		    case 'N':
+			Nflag++;
+			break;
+
+		    case 'L':
+			if (xflag || lflag || rflag) {
+				progerr(gettext(ERR_INCOMP0));
+				usage();
+			}
+			Lflag++;
+			break;
+
+		    case 'l':
+			if (xflag || rflag) {
+				progerr(gettext(ERR_INCOMP1));
+				usage();
+			}
+			lflag++;
+			break;
+
+		    case 'x':
+			/* bug # 1081606 */
+			if (lflag || rflag) {
+				progerr(gettext(ERR_INCOMP2));
+				usage();
+			}
+			xflag++;
+			break;
+
+		    case 'r':
+			if (lflag || xflag || Lflag) {
+				progerr(gettext(ERR_INCOMP0));
+				usage();
+			}
+			rflag++;
+			break;
+
+		    case 'c':
+			ckcatg[ncatg++] = strtok(optarg, " \t\n, ");
+			while (ckcatg[ncatg] = strtok(NULL, " \t\n, "))
+				ncatg++;
+			break;
+
+		/* added for newroot functions */
+		    case 'R':
+			if (!set_inst_root(optarg)) {
+				progerr(gettext(ERR_ROOT_CMD));
+				exit(1);
+			}
+			break;
+
+		    default:
+			usage();
+		}
+	}
+
+	/*
+	 * implement the newroot option
+	 */
+	set_PKGpaths(get_inst_root());	/* set up /var... directories */
+
+	/*
+	 * Open the install DB, if one exists.
+	 */
+
+	pkg = &argv[optind];
+	pkgcnt = (argc - optind);
+
+	if (pkg[0] && strcmp(pkg[0], "all") == NULL) {
+		pkgcnt = 0;
+		pkg[0] = NULL;
+	}
+
+	if (pkgdir == NULL)
+		pkgdir = get_PKGLOC(); 	/* we need this later */
+
+	/* convert device appropriately */
+	if (pkghead(device))
+		exit(1);
+
+	/*
+	 * If we are to inspect a spooled package we are only interested in
+	 * the pkginfo file in the spooled pkg so we skip any Reg 4 DB
+	 * lookups and use the old algorithm. We have a spooled pkg if
+	 * device is not NULL.
+	 */
+
+
+	look_for_installed();
+
+	if (lflag && strcmp(pkgdir, get_PKGLOC()) == NULL) {
+		/* look at contents file */
+		(void) snprintf(contents, sizeof (contents),
+		    "%s/contents", get_PKGADM());
+		rdcontents();
+
+	}
+
+	/*
+	 * If we are to inspect a spooled package we are only interested in
+	 * the pkginfo file in the spooled pkg so we skip any Reg 4 DB
+	 * lookups and use the old algorithm. We have a spooled pkg if
+	 * device is not NULL.
+	 */
+
+	report();
+
+	(void) pkghead(NULL);
+
+	return (errflg ? 1 : 0);
+}
+
+static void
+report(void)
+{
+	struct cfstat *dp, *choice;
+	int	i;
+	int	pkgLgth = 0;
+	int	longestPkg = 0;
+	boolean_t output = B_FALSE;
+
+	for (;;) {
+		choice = (struct cfstat *)0;
+		for (dp = data; dp; dp = dp->next) {
+			pkgLgth = strlen(dp->pkginst);
+			if (pkgLgth > longestPkg)
+				longestPkg = pkgLgth;
+		}
+		for (dp = data; dp; dp = dp->next) {
+			/* get information about this package */
+			if (dp->installed < 0)
+				continue; /* already used */
+			if (Lflag && pkgcnt) {
+				choice = dp;
+				break;
+			} else if (!choice ||
+			    (strcmp(choice->pkginst, dp->pkginst) > 0))
+				choice = dp;
+		}
+		if (!choice)
+			break; /* no more packages */
+
+		if (pkginfo(&info, choice->pkginst, ckarch, ckvers)) {
+			choice->installed = (-1);
+			continue;
+		}
+
+		/*
+		 * Confirm that the pkginfo file contains the
+		 * required information.
+		 */
+		if (info.name == NULL || *(info.name) == NULL ||
+		    info.arch == NULL || *(info.arch) == NULL ||
+		    info.version == NULL || *(info.version) == NULL ||
+		    info.catg == NULL || *(info.catg) == NULL) {
+			progerr(gettext(ERR_BADINFO));
+			errflg++;
+			return;
+		}
+
+		/* is it in an appropriate catgory? */
+		if (iscatg(info.catg)) {
+			choice->installed = (-1);
+			continue;
+		}
+
+		if (!pflag &&
+			/* don't include partially installed packages */
+			(choice->partial || (info.status == PI_PARTIAL) ||
+				(info.status == PI_UNKNOWN))) {
+			choice->installed = (-1);
+			continue;
+		}
+
+		if (Nflag && (info.status == PI_PRESVR4)) {
+			/* don't include preSVR4 packages */
+			choice->installed = (-1);
+			continue;
+		}
+
+		if (!iflag && ((info.status == PI_INSTALLED) ||
+		    (info.status == PI_PRESVR4))) {
+			/* don't include completely installed packages */
+			choice->installed = (-1);
+			continue;
+		}
+
+		output = B_TRUE;
+		dumpinfo(choice, longestPkg);
+		choice->installed = (-1);
+		if (pkgcnt) {
+			i = selectp(choice->pkginst);
+			if (i >= 0)
+				pkg[i] = NULL;
+			else {
+				if (qflag) {
+					errflg++;
+					return;
+				}
+			}
+		}
+	}
+
+	/* If no package matched and no output produced set error flag */
+	if (!output)
+		errflg++;
+
+	/* verify that each package listed on command line was output */
+	for (i = 0; i < pkgcnt; ++i) {
+		if (pkg[i]) {
+			errflg++;
+			if (!qflag) {
+				if (pflag == 1)
+					logerr(gettext(ERR_NOPINFO), pkg[i]);
+				else
+					logerr(gettext(ERR_NOINFO), pkg[i]);
+			} else
+				return;
+		}
+	}
+	(void) pkginfo(&info, NULL); /* free up all memory and open fds */
+}
+
+static void
+dumpinfo(struct cfstat *dp, int pkgLngth)
+{
+	register int i;
+	char	*pt;
+	char	category[128];
+
+	if (qflag) {
+		return; /* print nothing */
+	}
+
+	if (rflag) {
+		(void) puts((info.basedir) ? info.basedir : "none");
+		return;
+	}
+
+	if (Lflag) {
+		(void) puts(info.pkginst);
+		return;
+	} else if (xflag) {
+		(void) printf(XFMT, pkgLngth, pkgLngth, info.pkginst,
+		    info.name);
+
+		if (info.arch || info.version) {
+			(void) printf(CFMT, pkgLngth, pkgLngth, "");
+			if (info.arch)
+				(void) printf("(%s) ", info.arch);
+			if (info.version)
+				(void) printf("%s", info.version);
+			(void) printf("\n");
+		}
+		return;
+	} else if (!lflag) {
+		if (info.catg) {
+			(void) sscanf(info.catg, "%[^, \t\n]", category);
+		} else if (info.status == PI_PRESVR4) {
+			(void) strcpy(category, "preSVR4");
+		} else {
+			(void) strcpy(category, "(unknown)");
+		}
+		(void) printf(SFMT, category, pkgLngth, pkgLngth, info.pkginst,
+		    info.name);
+		return;
+	}
+	if (info.pkginst)
+		(void) printf(FMT, "PKGINST", info.pkginst);
+	if (info.name)
+		(void) printf(FMT, "NAME", info.name);
+	if (lflag && info.catg)
+		(void) printf(FMT, "CATEGORY", info.catg);
+	if (lflag && info.arch)
+		(void) printf(FMT, "ARCH", info.arch);
+	if (info.version)
+		(void) printf(FMT, "VERSION", info.version);
+	if (info.basedir)
+		(void) printf(FMT, "BASEDIR", info.basedir);
+	if (info.vendor)
+		(void) printf(FMT, "VENDOR", info.vendor);
+
+	if (info.status == PI_PRESVR4)
+		(void) printf(FMT, "STATUS", "preSVR4");
+	else {
+		for (i = 0; parmlst[i]; ++i) {
+			if ((pt = pkgparam(info.pkginst, parmlst[i])) != NULL &&
+			    *pt)
+				(void) printf(FMT, parmlst[i], pt);
+		}
+		if (info.status == PI_SPOOLED)
+			(void) printf(FMT, "STATUS", gettext("spooled"));
+		else if (info.status == PI_PARTIAL)
+			(void) printf(FMT, "STATUS",
+			    gettext("partially installed"));
+		else if (info.status == PI_INSTALLED)
+			(void) printf(FMT, "STATUS",
+			    gettext("completely installed"));
+		else
+			(void) printf(FMT, "STATUS", gettext("(unknown)"));
+	}
+	(void) pkgparam(NULL, NULL);
+
+	if (!lflag) {
+		(void) putchar('\n');
+		return;
+	}
+
+	if (info.status != PI_PRESVR4) {
+		if (strcmp(pkgdir, get_PKGLOC()))
+			getinfo(dp);
+
+		if (dp->spooled)
+			(void) printf(
+			    gettext("%10s:  %7ld spooled pathnames\n"),
+			    "FILES", dp->spooled);
+		if (dp->installed)
+			(void) printf(
+			    gettext("%10s:  %7ld installed pathnames\n"),
+			    "FILES", dp->installed);
+		if (dp->partial)
+			(void) printf(
+			    gettext("%20d partially installed pathnames\n"),
+			    dp->partial);
+		if (dp->shared)
+			(void) printf(gettext("%20d shared pathnames\n"),
+				dp->shared);
+		if (dp->link)
+			(void) printf(gettext("%20d linked files\n"), dp->link);
+		if (dp->dirs)
+			(void) printf(gettext("%20d directories\n"), dp->dirs);
+		if (dp->exec)
+			(void) printf(gettext("%20d executables\n"), dp->exec);
+		if (dp->setuid)
+			(void) printf(
+			    gettext("%20d setuid/setgid executables\n"),
+			    dp->setuid);
+		if (dp->info)
+			(void) printf(
+			    gettext("%20d package information files\n"),
+			    dp->info+1); /* pkgmap counts! */
+
+		if (dp->tblks)
+			(void) printf(gettext("%20ld blocks used (approx)\n"),
+				dp->tblks);
+	}
+	(void) putchar('\n');
+}
+
+static struct cfstat *
+fpkg(char *pkginst)
+{
+	struct cfstat *dp, *last;
+
+	dp = data;
+	last = (struct cfstat *)0;
+	while (dp) {
+		if (strcmp(dp->pkginst, pkginst) == NULL)
+			return (dp);
+		last = dp;
+		dp = dp->next;
+	}
+	dp = (struct cfstat *)calloc(1, sizeof (struct cfstat));
+	if (!dp) {
+		progerr(gettext("no memory, malloc() failed"));
+		exit(1);
+	}
+	if (!last)
+		data = dp;
+	else
+		last->next = dp; /* link list */
+	(void) strcpy(dp->pkginst, pkginst);
+	return (dp);
+}
+
+#define	SEPAR	','
+
+static int
+iscatg(char *list)
+{
+	register int i;
+	register char *pt;
+	int	match;
+
+	if (!ckcatg[0])
+		return (0); /* no specification implies all packages */
+	if (info.status == PI_PRESVR4) {
+		for (i = 0; ckcatg[i]; /* void */) {
+			if (strcmp(ckcatg[i++], "preSVR4") == NULL)
+				return (0);
+		}
+		return (1);
+	}
+	if (!list)
+		return (1); /* no category specified in pkginfo is a bug */
+
+	match = 0;
+	do {
+		if (pt = strchr(list, ','))
+			*pt = '\0';
+
+		for (i = 0; ckcatg[i]; /* void */) {
+			/* bug id 1081607 */
+			if (!strcasecmp(list, ckcatg[i++])) {
+				match++;
+				break;
+			}
+		}
+
+		if (pt)
+			*pt++ = ',';
+		if (match)
+			return (0);
+		list = pt; /* points to next one */
+	} while (pt);
+	return (1);
+}
+
+static void
+look_for_installed(void)
+{
+	struct dirent *drp;
+	struct stat	status;
+	DIR	*dirfp;
+	char	path[PATH_MAX];
+	int	n;
+
+	if (strcmp(pkgdir, get_PKGLOC()) == NULL &&
+	    (dirfp = opendir(get_PKGOLD()))) {
+		while (drp = readdir(dirfp)) {
+			if (drp->d_name[0] == '.')
+				continue;
+			n = strlen(drp->d_name);
+			if ((n > 5) &&
+			    strcmp(&drp->d_name[n-5], ".name") == NULL) {
+				(void) snprintf(path, sizeof (path),
+				    "%s/%s", get_PKGOLD(), drp->d_name);
+				if (lstat(path, &status))
+					continue;
+				if ((status.st_mode & S_IFMT) != S_IFREG)
+					continue;
+				drp->d_name[n-5] = '\0';
+				if (!pkgcnt || (selectp(drp->d_name) >= 0))
+					(void) fpkg(drp->d_name);
+			}
+		}
+		(void) closedir(dirfp);
+	}
+
+	if ((dirfp = opendir(pkgdir)) == NULL)
+		return;
+
+	while (drp = readdir(dirfp)) {
+		if (drp->d_name[0] == '.')
+			continue;
+
+		if (pkgcnt && (selectp(drp->d_name) < 0))
+			continue;
+
+		if (!pkginfofind(path, pkgdir, drp->d_name))
+			continue; /* doesn't appear to be a package */
+
+		(void) fpkg(drp->d_name);
+	}
+	(void) closedir(dirfp);
+}
+
+static int
+selectp(char *p)
+{
+	register int i;
+
+	for (i = 0; i < pkgcnt; ++i) {
+		if (pkg[i] && pkgnmchk(p, pkg[i], 1) == 0)
+			return (i);
+	}
+	return (-1);
+}
+
+static void
+rdcontents(void)
+{
+	VFP_T		*vfp;
+	struct cfstat	*dp;
+	struct pinfo	*pinfo;
+	int		n;
+
+	if (vfpOpen(&vfp, contents, "r", VFP_NEEDNOW) != 0) {
+		progerr(gettext("unable to open \"%s\" for reading"), contents);
+		exit(1);
+	}
+
+	/* check the contents file to look for referenced packages */
+	while ((n = srchcfile(&entry, "*", vfp, (VFP_T *)NULL)) > 0) {
+		for (pinfo = entry.pinfo; pinfo; pinfo = pinfo->next) {
+			/* see if entry is used by indicated packaged */
+			if (pkgcnt && (selectp(pinfo->pkg) < 0))
+				continue;
+
+			dp = fpkg(pinfo->pkg);
+			pkgusage(dp, &entry);
+
+			if (entry.npkgs > 1)
+				dp->shared++;
+
+			/*
+			 * Only objects specifically tagged with '!' event
+			 * character are considered "partial", everything
+			 * else is considered "installed" (even server
+			 * objects).
+			 */
+			switch (pinfo->status) {
+			case '!' :
+				dp->partial++;
+				break;
+			default :
+				dp->installed++;
+				break;
+			}
+		}
+	}
+	if (n < 0) {
+		char	*errstr = getErrstr();
+		progerr(gettext("bad entry read in contents file"));
+		logerr(gettext("pathname: %s"),
+		    (entry.path && *entry.path) ? entry.path : "Unknown");
+		logerr(gettext("problem: %s"),
+		    (errstr && *errstr) ? errstr : "Unknown");
+		exit(1);
+	}
+
+	(void) vfpClose(&vfp);
+}
+
+static void
+getinfo(struct cfstat *dp)
+{
+	int		n;
+	char		pkgmap[MAXPATHLEN];
+	VFP_T		*vfp;
+
+	(void) snprintf(pkgmap, sizeof (pkgmap),
+			"%s/%s/pkgmap", pkgdir, dp->pkginst);
+
+	if (vfpOpen(&vfp, pkgmap, "r", VFP_NEEDNOW) != 0) {
+		progerr(gettext("unable open \"%s\" for reading"), pkgmap);
+		exit(1);
+	}
+
+	dp->spooled = 1; /* pkgmap counts! */
+
+	while ((n = gpkgmapvfp(&entry, vfp)) > 0) {
+		dp->spooled++;
+		pkgusage(dp, &entry);
+	}
+
+	if (n < 0) {
+		char	*errstr = getErrstr();
+		progerr(gettext("bad entry read in pkgmap file"));
+		logerr(gettext("pathname: %s"),
+		    (entry.path && *entry.path) ? entry.path : "Unknown");
+		logerr(gettext("problem: %s"),
+		    (errstr && *errstr) ? errstr : "Unknown");
+		exit(1);
+	}
+
+	(void) vfpClose(&vfp);
+}
+
+static void
+pkgusage(struct cfstat *dp, struct cfent *pentry)
+{
+	if (pentry->ftype == 'i') {
+		dp->info++;
+		return;
+	} else if (pentry->ftype == 'l') {
+		dp->link++;
+	} else {
+		if ((pentry->ftype == 'd') || (pentry->ftype == 'x'))
+			dp->dirs++;
+
+		/* Only collect mode stats if they would be meaningful. */
+		if (pentry->ainfo.mode != BADMODE) {
+			if (pentry->ainfo.mode & 06000)
+				dp->setuid++;
+			if (!strchr("dxcbp", pentry->ftype) &&
+			(pentry->ainfo.mode & 0111))
+				dp->exec++;
+		}
+	}
+
+	if (strchr("ifve", pentry->ftype))
+		dp->tblks += nblock(pentry->cinfo.size);
+}
+
+static void
+usage(void)
+{
+	char *prog = get_prog_name();
+
+	/* bug # 1081606 */
+	(void) fprintf(stderr, gettext(ERR_USAGE), prog, prog);
+
+	exit(1);
+}
+
+void
+quit(int retval)
+{
+	exit(retval);
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/usr/src/cmd/svr4pkg/pkginstall/Makefile	Fri Jun 05 10:28:40 2009 -0400
@@ -0,0 +1,55 @@
+#
+# 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
+# or http://www.opensolaris.org/os/licensing.
+# 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 2009 Sun Microsystems, Inc.  All rights reserved.
+# Use is subject to license terms.
+#
+
+PROG=		pkginstall
+
+OBJS=		backup.o	\
+		check.o		\
+		cppath.o	\
+		dockspace.o	\
+		getinst.o	\
+		instvol.o	\
+		main.o		\
+		merginfo.o	\
+		pkgenv.o	\
+		pkgvolume.o	\
+		predepend.o	\
+		quit.o		\
+		reqexec.o	\
+		sortmap.o
+
+include $(SRC)/cmd/svr4pkg/Makefile.svr4pkg
+
+LDLIBS	+=	-lpkg -linstzones -ladm
+LDLIBS	+=	-lnsl -lsocket
+
+
+.KEEP_STATE:
+all:            $(PROG)
+
+install:	all $(ROOTPKGBINPROG)
+
+include $(SRC)/cmd/svr4pkg/Makefile.svr4pkg.targ
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/usr/src/cmd/svr4pkg/pkginstall/backup.c	Fri Jun 05 10:28:40 2009 -0400
@@ -0,0 +1,60 @@
+/*
+ * 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
+ * or http://www.opensolaris.org/os/licensing.
+ * 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 1993 Sun Microsystems, Inc.  All rights reserved.
+ * Use is subject to license terms.
+ */
+
+/* Copyright (c) 1984, 1986, 1987, 1988, 1989 AT&T */
+/* All Rights Reserved */
+
+
+#include <stdio.h>
+#include <locale.h>
+#include <libintl.h>
+#include <pkglib.h>
+
+extern char	savlog[];
+extern int	warnflag;
+
+void
+backup(char *path, int mode)
+{
+	static int	count = 0;
+	static FILE	*fp;
+
+	/* mode probably used in the future */
+	if (count++ == 0) {
+		if ((fp = fopen(savlog, "w")) == NULL) {
+			logerr(gettext("WARNING: unable to open logfile <%s>"),
+			    savlog);
+			warnflag++;
+		}
+	}
+
+	if (fp == NULL)
+		return;
+
+	(void) fprintf(fp, "%s%s", path, mode ? "\n" :
+	    gettext(" <attributes only>\n"));
+	/* we don't really back anything up; we just log the pathname */
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/usr/src/cmd/svr4pkg/pkginstall/check.c	Fri Jun 05 10:28:40 2009 -0400
@@ -0,0 +1,1097 @@
+/*
+ * 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
+ * or http://www.opensolaris.org/os/licensing.
+ * 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 2006 Sun Microsystems, Inc.  All rights reserved.
+ * Use is subject to license terms.
+ */
+
+/* Copyright (c) 1984, 1986, 1987, 1988, 1989 AT&T */
+/* All Rights Reserved */
+
+
+#include <stdio.h>
+#include <limits.h>
+#include <string.h>
+#include <stdlib.h>
+#include <sys/stat.h>	/* mkdir declaration is here? */
+#include <unistd.h>
+#include <errno.h>
+#include <utmpx.h>
+#include <dirent.h>
+#include <sys/types.h>
+#include <locale.h>
+#include <libintl.h>
+#include <pkgstrct.h>
+#include <pkglocs.h>
+#include "install.h"
+#include <pkglib.h>
+#include "libadm.h"
+#include "libinst.h"
+#include "pkginstall.h"
+
+extern struct admin adm;
+extern struct cfextra **extlist;
+extern int	ckquit, nocnflct, nosetuid, rprcflag;
+extern char	ilockfile[], rlockfile[], instdir[], savlog[],
+		tmpdir[], pkgloc[], pkgloc_sav[], pkgbin[], pkgsav[],
+		*pkginst, *msgtext;
+extern char	saveSpoolInstallDir[];
+
+static boolean_t	preinstallCheck = B_FALSE;
+static char		*zoneName = (char *)NULL;
+
+static char	ask_cont[100];
+
+#define	DISPSIZ	20	/* number of items to display on one page */
+
+#define	MSG_RUNLEVEL	"\\nThe current run-level of this machine is <%s>, " \
+			"which is not a run-level suggested for installation " \
+			"of this package.  Suggested run-levels (in order of " \
+			"preference) include:"
+#define	HLP_RUNLEVEL	"If this package is not installed in a run-level " \
+			"which has been suggested, it is possible that the " \
+			"package may not install or operate properly.  If " \
+			"you wish to follow the run-level suggestions, " \
+			"answer 'n' to stop installation of the package."
+#define	MSG_STATECHG	"\\nTo change states, execute\\n\\tshutdown -y " \
+			"-i%s -g0\\nafter exiting the installation process. " \
+			"Please note that after changing states you " \
+			"may have to mount appropriate filesystem(s) " \
+			"in order to install this package."
+
+#define	ASK_CONFLICT	"Do you want to install these conflicting files"
+#define	MSG_CONFLICT	"\\nThe following files are already installed on the " \
+			"system and are being used by another package:"
+#define	MSG_ROGUE	"\\n* - conflict with a file which does not " \
+			"belong to any package."
+#define	HLP_CONFLICT	"If you choose to install conflicting files, the " \
+			"files listed above will be overwritten and/or have " \
+			"their access permissions changed.  If you choose " \
+			"not to install these files, installation will " \
+			"proceed but these specific files will not be " \
+			"installed.  Note that sane operation of the " \
+			"software being installed may require these files " \
+			"be installed; thus choosing to not to do so may " \
+			"cause inapropriate operation.  If you wish to stop " \
+			"installation of this package, enter 'q' to quit."
+
+#define	ASK_SETUID	"Do you want to install these as setuid/setgid files"
+#define	MSG_SETUID	"\\nThe following files are being installed with " \
+			"setuid and/or setgid permissions:"
+#define	MSG_OVERWR	"\\n* - overwriting a file which is also " \
+			"setuid/setgid."
+#define	HLP_SETUID	"The package being installed appears to contain " \
+			"processes which will have their effective user or " \
+			"group ids set upon execution.  History has shown " \
+			"that these types of processes can be a source of " \
+			"security problems on your system.  If you choose " \
+			"not to install these as setuid files, installation " \
+			"will proceed but these specific files will be " \
+			"installed as regular files with setuid and/or " \
+			"setgid permissions reset.  Note that sane " \
+			"operation of the software being installed may " \
+			"require that these files be installed with setuid " \
+			"or setgid permissions as delivered; thus choosing " \
+			"to install them as regular files may cause " \
+			"inapropriate operation.  If you wish to stop " \
+			"installation of this package, enter 'q' to quit."
+#define	MSG_PARTINST	"\\nThe installation of this package was previously " \
+			"terminated and installation was never successfully " \
+			"completed."
+#define	MSG_PARTREM	"\\nThe removal of this package was terminated at " \
+			"some point in time, and package removal was only " \
+			"partially completed."
+#define	HLP_PARTIAL	"Installation of partially installed packages is " \
+			"normally allowable, but some packages providers " \
+			"may suggest that a partially installed package be " \
+			"completely removed before re-attempting " \
+			"installation.  Check the documentation provided " \
+			"with this package, and then answer 'y' if you feel " \
+			"it is advisable to continue the installation process."
+
+#define	HLP_SPACE	"It appears that there is not enough free space on " \
+			"your system in which to install this package.  It " \
+			"is possible that one or more filesystems are not " \
+			"properly mounted.  Neither installation of the " \
+			"package nor its operation can be guaranteed under " \
+			"these conditions.  If you choose to disregard this " \
+			"warning, enter 'y' to continue the installation " \
+			"process."
+#define	HLP_DEPEND	"The package being installed has indicated a " \
+			"dependency on the existence (or non-existence) " \
+			"of another software package.  If this dependency is " \
+			"not met before continuing, the package may not " \
+			"install or operate properly.  If you wish to " \
+			"disregard this dependency, answer 'y' to continue " \
+			"the installation process."
+
+#define	MSG_PRIV	"\\nThis package contains scripts which will be " \
+			"executed with super-user permission during the " \
+			"process of installing this package."
+#define	HLP_PRIV	"During the installation of this package, certain " \
+			"scripts provided with the package will execute with " \
+			"super-user permission.  These scripts may modify or " \
+			"otherwise change your system without your " \
+			"knowledge.  If you are certain of the origin and " \
+			"trustworthiness of the package being installed, " \
+			"answer 'y' to continue the installation process."
+
+#define	ASK_CONT	"Do you want to continue with the installation of <%s>"
+#define	HLP_CONT	"If you choose 'y', installation of this package " \
+			"will continue.  If you want to stop installation " \
+			"of this package, choose 'n'."
+
+#define	MSG_MKPKGDIR	"unable to make packaging directory <%s>"
+
+#define	MSG_CKCONFL_GZ	"## Checking for conflicts with packages already " \
+			"installed."
+#define	MSG_CKCONFL_LZ	"## Checking for conflicts with packages already " \
+			"installed in zone <%s>."
+#define	MSG_CKDEPEND_GZ	"## Verifying package dependencies."
+#define	MSG_CKDEPEND_LZ	"## Verifying package dependencies in zone <%s>."
+#define	MSG_CKSPACE_GZ	"## Verifying disk space requirements."
+#define	MSG_CKSPACE_LZ	"## Verifying disk space requirements in zone <%s>."
+#define	MSG_CKUID_GZ	"## Checking for setuid/setgid programs."
+#define	MSG_CKUID_LZ	"## Checking for setuid/setgid programs in zone <%s>."
+
+#define	MSG_SCRFND	"Package scripts were found."
+#define	MSG_UIDFND	"Setuid/setgid processes detected."
+#define	MSG_ATTRONLY	"!%s %s <attribute change only>"
+
+#define	MSG_CONTDISP	"[Hit <RETURN> to continue display]"
+
+#define	ERR_NO_RUNST	"unable to determine current run-state"
+#define	ERR_DEPFAILED	"Dependency checking failed."
+#define	ERR_SPCFAILED	"Space checking failed."
+#define	ERR_CNFFAILED	"Conflict checking failed."
+#define	ERR_BADFILE	"packaging file <%s> is corrupt"
+
+/*
+ * Return value:	int
+ *			0 - success
+ *			1 - end of file
+ *			2 - undefined error
+ *			3 - answer was not "y"/was "q"
+ *			4 - quit action taken
+ *			5 - interactive mode required
+ * If "preinstallcheck" is set to B_TRUE:
+ *			8 - partial install detected
+ *			9 - partial removal detected
+ */
+
+int
+ckpartial(void)
+{
+	char	ans[MAX_INPUT];
+	int	n;
+
+	if (ADM(partial, "nocheck")) {
+		return (0);
+	}
+
+	if (access(ilockfile, F_OK) == 0) {
+		if (preinstallCheck == B_TRUE) {
+			return (8);	/* partial install detected */
+		}
+
+		(void) snprintf(ask_cont, sizeof (ask_cont),
+			gettext(ASK_CONT), pkginst);
+
+		msgtext = gettext(MSG_PARTINST);
+		ptext(stderr, msgtext);
+
+		if (ADM(partial, "quit")) {
+			return (4);
+		}
+
+		if (echoGetFlag() == B_FALSE) {
+			return (5);
+		}
+
+		msgtext = NULL;
+
+		ckquit = 0;
+		if (n = ckyorn(ans, NULL, NULL, gettext(HLP_PARTIAL),
+				ask_cont)) {
+			return (n);
+		}
+
+		if (strchr("yY", *ans) == NULL) {
+			return (3);
+		}
+		ckquit = 1;
+	}
+
+	if (access(rlockfile, F_OK) == 0) {
+		if (preinstallCheck == B_TRUE) {
+			return (9);	/* partial removal detected */
+		}
+
+		(void) snprintf(ask_cont, sizeof (ask_cont),
+			gettext(ASK_CONT), pkginst);
+
+
+		msgtext = gettext(MSG_PARTREM);
+		ptext(stderr, msgtext);
+
+		if (ADM(partial, "quit")) {
+			return (4);
+		}
+
+		if (echoGetFlag() == B_FALSE) {
+			return (5);
+		}
+
+		msgtext = NULL;
+
+		ckquit = 0;
+		if (n = ckyorn(ans, NULL, NULL, gettext(HLP_PARTIAL),
+			ask_cont)) {
+			return (n);
+		}
+
+		if (strchr("yY", *ans) == NULL) {
+			return (3);
+		}
+		ckquit = 1;
+	}
+
+	return (0);
+}
+
+/*
+ * Return value:	int
+ *			0 - success
+ *			1 - end of file
+ *			2 - undefined error
+ *			3 - answer was not "y"/was "q"
+ *			4 - quit action taken
+ *			5 - interactive mode required
+ *			99 - fatal error
+ */
+
+int
+ckrunlevel(void)
+{
+	struct utmpx utmpx;
+	struct utmpx *putmpx;
+	char	ans[MAX_INPUT], *pt, *istates, *pstate;
+	int	n;
+	char	*uxstate;
+
+	if (ADM(runlevel, "nocheck")) {
+		return (0);
+	}
+
+	pt = getenv("ISTATES");
+	if (pt == NULL) {
+		return (0);
+	}
+
+	utmpx.ut_type = RUN_LVL;
+	putmpx = getutxid(&utmpx);
+	if (putmpx == NULL) {
+		progerr(gettext(ERR_NO_RUNST));
+		return (99);
+	}
+
+	(void) snprintf(ask_cont, sizeof (ask_cont),
+			gettext(ASK_CONT), pkginst);
+
+	/*
+	 * this cryptic code is trying to pull the run level
+	 * out of the utmpx entry...the level starts in column
+	 * 11 - looks like "run-level %c"
+	 */
+	uxstate = strtok(&putmpx->ut_line[10], " \t\n");
+
+	istates = qstrdup(pt);
+	if ((pt = strtok(pt, " \t\n, ")) == NULL) {
+		return (0); /* no list is no list */
+	}
+
+	pstate = pt;
+	do {
+		if (strcmp(pt, uxstate) == 0) {
+			free(istates);
+			return (0);
+		}
+	} while (pt = strtok(NULL, " \t\n, "));
+
+	if (preinstallCheck == B_FALSE) {
+		msgtext = gettext(MSG_RUNLEVEL);
+		ptext(stderr, msgtext, uxstate);
+	} else {
+		(void) fprintf(stdout, "runlevel=%s", uxstate);
+	}
+
+	pt = strtok(istates, " \t\n, ");
+	do {
+		if (preinstallCheck == B_FALSE) {
+			ptext(stderr, "\\t%s", pt);
+		} else {
+			(void) fprintf(stdout, ":%s", pt);
+		}
+	} while (pt = strtok(NULL, " \t\n, "));
+
+	if (preinstallCheck == B_TRUE) {
+		(void) fprintf(stdout, "\n");
+	}
+
+	free(istates);
+
+	if (preinstallCheck == B_TRUE) {
+		return (4);
+	}
+
+	if (ADM(runlevel, "quit")) {
+		return (4);
+	}
+
+	if (echoGetFlag() == B_FALSE) {
+		return (5);
+	}
+
+	msgtext = NULL;
+
+	ckquit = 0;
+	if (n = ckyorn(ans, NULL, NULL, gettext(HLP_RUNLEVEL),
+		ask_cont)) {
+		return (n);
+	}
+
+	ckquit = 1;
+
+	if (strchr("yY", *ans) != NULL) {
+		return (0);
+	} else {
+		if (preinstallCheck == B_FALSE) {
+			ptext(stderr, gettext(MSG_STATECHG), pstate);
+		}
+		return (3);
+	}
+}
+
+/*
+ * Return value:	int
+ *			0 - success
+ *			1 - end of file
+ *			2 - undefined error
+ *			3 - answer was not "y"/was "q"
+ *			4 - quit action taken
+ *			5 - interactive mode required
+ */
+
+int
+ckdepend(void)
+{
+	int	n;
+	char	ans[MAX_INPUT];
+	char	path[PATH_MAX];
+
+	if (ADM(idepend, "nocheck")) {
+		return (0);
+	}
+
+	(void) snprintf(path, sizeof (path), "%s/%s", instdir, DEPEND_FILE);
+	if (access(path, F_OK) != 0) {
+		return (0); /* no dependency file provided by package */
+	}
+
+	if (zoneName == (char *)NULL) {
+		echo(gettext(MSG_CKDEPEND_GZ));
+	} else {
+		echo(gettext(MSG_CKDEPEND_LZ), zoneName);
+	}
+
+	if (dockdeps(path, 0, preinstallCheck)) {
+		(void) snprintf(ask_cont, sizeof (ask_cont),
+			gettext(ASK_CONT), pkginst);
+		msgtext = gettext(ERR_DEPFAILED);
+
+		if (preinstallCheck == B_TRUE) {
+			return (4);
+		}
+
+		if (ADM(idepend, "quit")) {
+			return (4);
+		}
+
+		if (echoGetFlag() == B_FALSE) {
+			return (5);
+		}
+
+		msgtext = NULL;
+
+		ckquit = 0;
+		if (n = ckyorn(ans, NULL, NULL, gettext(HLP_DEPEND),
+			ask_cont)) {
+			return (n);
+		}
+
+		if (strchr("yY", *ans) == NULL) {
+			return (3);
+		}
+
+		ckquit = 1;
+	}
+
+	return (0);
+}
+
+void
+cksetZoneName(char *a_zoneName)
+{
+	zoneName = a_zoneName;
+}
+
+void
+cksetPreinstallCheck(boolean_t a_preinstallCheck)
+{
+	preinstallCheck = a_preinstallCheck;
+}
+
+/*
+ * Return value:	int
+ *			0 - success
+ *			1 - end of file
+ *			2 - undefined error
+ *			3 - answer was not "y"/was "q"
+ *			4 - quit action taken
+ *			5 - interactive mode required
+ */
+int
+ckspace(void)
+{
+	int	n;
+	char	ans[MAX_INPUT];
+	char	path[PATH_MAX];
+
+	if (ADM(space, "nocheck")) {
+		return (0);
+	}
+
+	if (zoneName == (char *)NULL) {
+		echo(gettext(MSG_CKSPACE_GZ));
+	} else {
+		echo(gettext(MSG_CKSPACE_LZ), zoneName);
+	}
+
+	(void) snprintf(path, sizeof (path), "%s/install/space", instdir);
+	if (access(path, F_OK) == 0) {
+		n = dockspace(path);
+	} else {
+		n = dockspace(NULL);
+	}
+
+	if (n) {
+		msgtext = gettext(ERR_SPCFAILED);
+		(void) snprintf(ask_cont, sizeof (ask_cont),
+			gettext(ASK_CONT), pkginst);
+
+		if (preinstallCheck == B_TRUE) {
+			return (4);
+		}
+
+		if (ADM(space, "quit")) {
+			return (4);
+		}
+
+		if (echoGetFlag() == B_FALSE) {
+			return (5);
+		}
+
+		msgtext = NULL;
+
+		ckquit = 0;
+		n = ckyorn(ans, NULL, NULL, gettext(HLP_SPACE), ask_cont);
+		if (n != 0) {
+			return (n);
+		}
+
+		if (strchr("yY", *ans) == NULL) {
+			return (3);
+		}
+
+		ckquit = 1;
+	}
+	return (0);
+}
+
+void
+ckdirs(void)
+{
+	char	path[PATH_MAX];
+
+	if (mkpath(get_PKGADM())) {
+		if (preinstallCheck == B_TRUE) {
+			(void) fprintf(stdout, "ckdirs=%s\n", get_PKGADM());
+		} else {
+			progerr(gettext(MSG_MKPKGDIR), get_PKGADM());
+		}
+		quit(99);
+	}
+
+	(void) snprintf(path, sizeof (path), "%s/admin", get_PKGADM());
+
+	if (mkpath(path)) {
+		if (preinstallCheck == B_TRUE) {
+			(void) fprintf(stdout, "ckdirs=%s\n", path);
+		} else {
+			progerr(gettext(MSG_MKPKGDIR), path);
+		}
+		quit(99);
+	}
+
+	(void) snprintf(path, sizeof (path), "%s/logs", get_PKGADM());
+
+	if (mkpath(path)) {
+		if (preinstallCheck == B_TRUE) {
+			(void) fprintf(stdout, "ckdirs=%s\n", path);
+		} else {
+			progerr(gettext(MSG_MKPKGDIR), path);
+		}
+		quit(99);
+	}
+
+	if (mkpath(PKGSCR)) {
+		if (preinstallCheck == B_TRUE) {
+			(void) fprintf(stdout, "ckdirs=%s\n", PKGSCR);
+		} else {
+			progerr(gettext(MSG_MKPKGDIR), PKGSCR);
+		}
+		quit(99);
+	}
+
+	if (mkpath(get_PKGLOC())) {
+		if (preinstallCheck == B_TRUE) {
+			(void) fprintf(stdout, "ckdirs=%s\n", get_PKGLOC());
+		} else {
+			progerr(gettext(MSG_MKPKGDIR), get_PKGLOC());
+		}
+		quit(99);
+	}
+}
+
+/*
+ * Return value:	int
+ *			0 - success
+ *			99 - failure
+ */
+
+int
+ckpkgdirs(void)
+{
+	boolean_t nonExistentPkgloc = B_FALSE;
+
+	/*
+	 * If pkgloc doesn't exist make sure it gets removed after creating
+	 * it if this is a preinstall check. All dryrun and preinstallation
+	 * checks must not modify the file system.
+	 */
+
+	if (access(pkgloc, F_OK) != 0) {
+		nonExistentPkgloc = B_TRUE;
+	}
+
+	if (mkpath(pkgloc)) {
+		if (preinstallCheck == B_TRUE) {
+			(void) fprintf(stdout, "ckdirs=%s\n", pkgloc);
+		} else {
+			progerr(gettext(MSG_MKPKGDIR), pkgloc);
+		}
+		return (99);
+	}
+
+	if (mkpath(pkgbin)) {
+		if (preinstallCheck == B_TRUE) {
+			(void) fprintf(stdout, "ckdirs=%s\n", pkgbin);
+		} else {
+			progerr(gettext(MSG_MKPKGDIR), pkgbin);
+		}
+		return (99);
+	}
+
+	if (mkpath(pkgsav)) {
+		if (preinstallCheck == B_TRUE) {
+			(void) fprintf(stdout, "ckdirs=%s\n", pkgsav);
+		} else {
+			progerr(gettext(MSG_MKPKGDIR), pkgsav);
+		}
+		return (99);
+	}
+
+	if (!is_spool_create() && mkpath(saveSpoolInstallDir)) {
+		if (preinstallCheck == B_TRUE) {
+			(void) fprintf(stdout, "ckdirs=%s\n", pkgsav);
+		} else {
+			progerr(gettext(MSG_MKPKGDIR), pkgsav);
+		}
+		return (99);
+	}
+
+	if (preinstallCheck && nonExistentPkgloc) {
+		rrmdir(pkgloc);
+	}
+
+	return (0);
+}
+
+/*
+ * Return value:	int
+ *			0 - success
+ *			1 - end of file
+ *			2 - undefined error
+ *			3 - answer was not "y"/was "q"
+ *			4 - quit action taken
+ *			5 - interactive mode required
+ */
+
+int
+ckconflct(void)
+{
+	int	i, n, count, has_a_rogue = 0;
+	char	ans[MAX_INPUT];
+
+	if (ADM(conflict, "nochange")) {
+		nocnflct++;
+		return (0);
+	}
+
+	if (ADM(conflict, "nocheck")) {
+		return (0);
+	}
+
+	if (zoneName == (char *)NULL) {
+		echo(gettext(MSG_CKCONFL_GZ));
+	} else {
+		echo(gettext(MSG_CKCONFL_LZ), zoneName);
+	}
+
+	count = 0;
+	for (i = 0; extlist[i]; i++) {
+		struct cfent *ept;
+		struct mergstat *mstat;
+
+		if (extlist[i]->cf_ent.ftype == 'i') {
+			continue;
+		}
+
+		ept = &(extlist[i]->cf_ent);
+		mstat = &(extlist[i]->mstat);
+
+		if (is_remote_fs(ept->path, &(extlist[i]->fsys_value)) &&
+			!is_fs_writeable(ept->path,
+				&(extlist[i]->fsys_value))) {
+			continue;
+		}
+
+		/*
+		 * If no other package claims it or it's from a continuation
+		 * file, skip it.
+		 */
+		if (!mstat->shared || mstat->preloaded) {
+			continue;
+		}
+
+		if (ept->ftype == 'e') {
+			continue;
+		}
+
+		if (mstat->rogue) {
+			has_a_rogue = 1;
+		}
+
+		if (mstat->contchg) {
+			if (!count++) {
+				if (preinstallCheck == B_FALSE) {
+					ptext(stderr, gettext(MSG_CONFLICT));
+				}
+			} else if ((echoGetFlag() == B_TRUE) &&
+					((count % DISPSIZ) == 0)) {
+				echo(gettext(MSG_CONTDISP));
+				(void) getc(stdin);
+			}
+			/*
+			 * NOTE : The leading "!" in this string forces
+			 * puttext() to print leading white space.
+			 */
+
+			if (preinstallCheck == B_FALSE) {
+				ptext(stderr, "!%s %s",
+					(mstat->rogue) ? "*" : " ", ept->path);
+			} else {
+				(void) fprintf(stdout,
+					"conflict-contents=%s\n", ept->path);
+			}
+		} else if (mstat->attrchg) {
+			if (!count++) {
+				if (preinstallCheck == B_FALSE) {
+					ptext(stderr, gettext(MSG_CONFLICT));
+				}
+			} else if ((echoGetFlag() == B_TRUE) &&
+					((count % DISPSIZ) == 0)) {
+				echo(gettext(MSG_CONTDISP));
+				(void) getc(stdin);
+			}
+			if (preinstallCheck == B_FALSE) {
+				ptext(stderr, gettext(MSG_ATTRONLY),
+					(mstat->rogue) ? "*" : " ", ept->path);
+			} else {
+				(void) fprintf(stdout,
+					"conflict-attributes=%s\n", ept->path);
+			}
+		}
+	}
+
+	if (count) {
+		if (has_a_rogue) {
+			if (preinstallCheck == B_FALSE) {
+				ptext(stderr, gettext(MSG_ROGUE));
+			}
+		}
+
+		msgtext = gettext(ERR_CNFFAILED);
+
+		if (preinstallCheck == B_TRUE) {
+			return (4);
+		}
+
+		if (ADM(conflict, "quit")) {
+			return (4);
+		}
+
+		if (echoGetFlag() == B_FALSE) {
+			return (5);
+		}
+
+		msgtext = NULL;
+
+		if (n = ckyorn(ans, NULL, NULL, gettext(HLP_CONFLICT),
+			gettext(ASK_CONFLICT))) {
+			return (n);
+		}
+
+		if (strchr("yY", *ans) == NULL) {
+			ckquit = 0;
+			(void) snprintf(ask_cont, sizeof (ask_cont),
+				gettext(ASK_CONT), pkginst);
+
+			if (n = ckyorn(ans, NULL, NULL, gettext(HLP_CONT),
+				ask_cont)) {
+				return (n);
+			}
+
+			if (strchr("yY", *ans) == NULL) {
+				return (3);
+			}
+			ckquit = 1;
+			nocnflct++;
+			rprcflag++;
+		}
+	}
+	return (0);
+}
+
+/*
+ * Return value:	int
+ *			0 - success
+ *			1 - end of file
+ *			2 - undefined error
+ *			3 - answer was not "y"/was "q"
+ *			4 - quit action taken
+ *			5 - interactive mode required
+ */
+
+int
+cksetuid(void)
+{
+	int	i, n, count, overwriting = 0;
+	char	ans[MAX_INPUT];
+
+	/* See if the administrative defaults already resolve this check. */
+	if (ADM(setuid, "nocheck")) {
+		return (0);
+	}
+
+	if (ADM(setuid, "nochange")) {
+		nosetuid++;	/* Do not install processes as setuid/gid. */
+		return (0);
+	}
+
+	/* The administrative defaults require review of the package. */
+
+	if (zoneName == (char *)NULL) {
+		echo(gettext(MSG_CKUID_GZ));
+	} else {
+		echo(gettext(MSG_CKUID_LZ), zoneName);
+	}
+
+	count = 0;
+	for (i = 0; extlist[i]; i++) {
+		int overwr;
+		struct mergstat *mstat = &(extlist[i]->mstat);
+
+		/*
+		 * Provide the administrator with info as to whether there is
+		 * already a setuid process in place. This is only necessary
+		 * to help the administrator decide whether or not to lay
+		 * down the process, it doesn't have anything to do with the
+		 * administrative defaults.
+		 */
+		if (mstat->osetuid || mstat->osetgid) {
+			overwr = 1;
+			overwriting = 1;
+		} else
+			overwr = 0;
+
+		if (mstat->setuid || mstat->setgid) {
+			if (!count++) {
+				if (preinstallCheck == B_FALSE) {
+					ptext(stderr, gettext(MSG_SETUID));
+				}
+			} else if ((echoGetFlag() == B_TRUE) &&
+					((count % DISPSIZ) == 0)) {
+				echo(gettext(MSG_CONTDISP));
+				(void) getc(stdin);
+			}
+			/*
+			 * NOTE : The leading "!" in these strings forces
+			 * puttext() to print leading white space.
+			 */
+
+			if (mstat->setuid && mstat->setgid) {
+				if (preinstallCheck == B_FALSE) {
+					ptext(stderr, gettext(
+						"!%s %s <setuid %s setgid %s>"),
+						(overwr) ? "*" : " ",
+						extlist[i]->cf_ent.path,
+						extlist[i]->cf_ent.ainfo.owner,
+						extlist[i]->cf_ent.ainfo.group);
+				} else {
+					(void) fprintf(stdout, "setuid=%s:%s\n",
+						extlist[i]->cf_ent.path,
+						extlist[i]->cf_ent.ainfo.owner);
+					(void) fprintf(stdout, "setgid=%s:%s\n",
+						extlist[i]->cf_ent.path,
+						extlist[i]->cf_ent.ainfo.group);
+				}
+			} else if (mstat->setuid) {
+				if (preinstallCheck == B_FALSE) {
+					ptext(stderr, gettext(
+						"!%s %s <setuid %s>"),
+						(overwr) ? "*" : " ",
+						extlist[i]->cf_ent.path,
+						extlist[i]->cf_ent.ainfo.owner);
+				} else {
+					(void) fprintf(stdout, "setuid=%s:%s\n",
+						extlist[i]->cf_ent.path,
+						extlist[i]->cf_ent.ainfo.owner);
+				}
+			} else if (mstat->setgid) {
+				if (preinstallCheck == B_FALSE) {
+					ptext(stderr, gettext(
+						"!%s%s <setgid %s>"),
+						(overwr) ? "*" : " ",
+						extlist[i]->cf_ent.path,
+						extlist[i]->cf_ent.ainfo.group);
+				} else {
+					(void) fprintf(stdout, "setgid=%s:%s\n",
+						extlist[i]->cf_ent.path,
+						extlist[i]->cf_ent.ainfo.group);
+				}
+			}
+		}
+	}
+
+	if (count) {
+		if (overwriting) {
+			if (preinstallCheck == B_FALSE) {
+				ptext(stderr, gettext(MSG_OVERWR));
+			} else {
+				(void) fprintf(stdout,
+					"setuid-overwrite=true\n");
+			}
+		}
+
+		msgtext = gettext(MSG_UIDFND);
+
+		if (preinstallCheck == B_TRUE) {
+			return (4);
+		}
+
+		if (ADM(setuid, "quit")) {
+			return (4);
+		}
+		if (echoGetFlag() == B_FALSE) {
+			return (5);
+		}
+		msgtext = NULL;
+
+		if (n = ckyorn(ans, NULL, NULL, gettext(HLP_SETUID),
+			gettext(ASK_SETUID))) {
+			return (n);
+		}
+		if (strchr("yY", *ans) == NULL) {
+			ckquit = 0;
+			(void) snprintf(ask_cont, sizeof (ask_cont),
+				gettext(ASK_CONT), pkginst);
+			if (n = ckyorn(ans, NULL, NULL, gettext(HLP_CONT),
+				ask_cont)) {
+				return (n);
+			}
+			if (strchr("yY", *ans) == NULL) {
+				return (3);
+			}
+			ckquit = 1;
+			nosetuid++;
+			rprcflag++;
+		}
+	}
+
+	return (0);
+}
+
+/*
+ * Return value:	int
+ *			0 - success
+ *			1 - end of file
+ *			2 - undefined error
+ *			3 - answer was not "y"/was "q"
+ *			4 - quit action taken
+ *			5 - interactive mode required
+ */
+
+int
+ckpriv(void)
+{
+	struct dirent *dp;
+	DIR	*dirfp;
+	int	n, found;
+	char	ans[MAX_INPUT], path[PATH_MAX];
+
+	if (ADM(action, "nocheck")) {
+		return (0);
+	}
+
+	(void) snprintf(path, sizeof (path), "%s/install", instdir);
+	if ((dirfp = opendir(path)) == NULL) {
+		return (0);
+	}
+
+	found = 0;
+	while ((dp = readdir(dirfp)) != NULL) {
+		if (strcmp(dp->d_name, "preinstall") == 0 ||
+			strcmp(dp->d_name, "postinstall") == 0 ||
+			strncmp(dp->d_name, "i.", 2) == 0) {
+			found++;
+			break;
+		}
+	}
+	(void) closedir(dirfp);
+
+	if (found) {
+		if (preinstallCheck == B_FALSE) {
+			ptext(stderr, gettext(MSG_PRIV));
+			msgtext = gettext(MSG_SCRFND);
+		}
+		(void) snprintf(ask_cont, sizeof (ask_cont),
+				gettext(ASK_CONT), pkginst);
+
+		if (preinstallCheck == B_TRUE) {
+			return (4);
+		}
+
+		if (ADM(action, "quit")) {
+			return (4);
+		}
+
+		if (echoGetFlag() == B_FALSE) {
+			return (5);
+		}
+
+		msgtext = NULL;
+
+		ckquit = 0;
+		if (n = ckyorn(ans, NULL, NULL, gettext(HLP_PRIV),
+			ask_cont)) {
+			return (n);
+		}
+
+		if (strchr("yY", *ans) == NULL) {
+			return (3);
+		}
+		ckquit = 1;
+	}
+
+	return (0);
+}
+
+/*
+ * Return value:	int
+ *			0 - success
+ *			99 - failure
+ */
+
+int
+ckpkgfiles(void)
+{
+	register int i;
+	struct cfent	*ept;
+	int	errflg;
+	char	source[PATH_MAX];
+
+	errflg = 0;
+	for (i = 0; extlist[i]; i++) {
+		ept = &(extlist[i]->cf_ent);
+		if (ept->ftype != 'i') {
+			continue;
+		}
+
+		if (ept->ainfo.local) {
+			(void) snprintf(source, sizeof (source),
+				"%s/%s", instdir, ept->ainfo.local);
+		} else if (strcmp(ept->path, PKGINFO) == 0) {
+			(void) snprintf(source, sizeof (source),
+				"%s/%s", instdir, ept->path);
+		} else {
+			(void) snprintf(source, sizeof (source),
+				"%s/install/%s", instdir, ept->path);
+		}
+		if (cverify(0, &ept->ftype, source, &ept->cinfo, 1)) {
+			errflg++;
+			if (preinstallCheck == B_FALSE) {
+				progerr(gettext(ERR_BADFILE), source);
+				logerr(getErrbufAddr());
+			} else {
+				(void) fprintf(stdout, "ckpkgfilebad=%s",
+					source);
+			}
+		}
+	}
+
+	if (errflg) {
+		return (99);
+	} else {
+		return (0);
+	}
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/usr/src/cmd/svr4pkg/pkginstall/cppath.c	Fri Jun 05 10:28:40 2009 -0400
@@ -0,0 +1,376 @@
+/*
+ * 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
+ * or http://www.opensolaris.org/os/licensing.
+ * 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 2005 Sun Microsystems, Inc.  All rights reserved.
+ * Use is subject to license terms.
+ */
+
+/* Copyright (c) 1984, 1986, 1987, 1988, 1989 AT&T */
+/* All Rights Reserved */
+
+
+/*
+ * System includes
+ */
+
+#include <stdio.h>
+#include <string.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <utime.h>
+#include <locale.h>
+#include <libintl.h>
+#include <pkglocs.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+
+/*
+ * consolidation pkg command library includes
+ */
+
+#include <pkglib.h>
+
+/*
+ * local pkg command library includes
+ */
+
+#include "libadm.h"
+#include "libinst.h"
+#include "install.h"
+#include "messages.h"
+#include "pkginstall.h"
+
+/*
+ * forward declarations
+ */
+
+static int	write_file(char **r_linknam, int a_ctrl, mode_t a_mode,
+			char *a_file);
+static int	create_path(int a_ctrl, char *a_file);
+
+/*
+ * Name:	cppath
+ * Description:	copy a path object (install new file on system)
+ * Arguments:
+ *    - a_cntrl - determine how the destination file mode is set:
+ *	|= MODE_0666 - force mode to 0666
+ *      |= MODE_SET - mode is a_mode (no mask SET?ID bits)
+ *      |= MODE_SRC - mode from source file (mask SET?ID bits)
+ *      |= DIR_DISPLAY - display "%s <implied directory>" if directory created
+ *    - a_srcPath - path to source to copy
+ *    - a_dstPath - path to copy source to
+ *    - a_mode - mode to set a_dstpath to (mode controlled by a_ctrl)
+ * Returns:	int
+ *	== 0 - success
+ *	!= 0 - failure
+ */
+
+int
+cppath(int a_ctrl, char *a_srcPath, char *a_dstPath, mode_t a_mode)
+{
+	char		*linknam = (char *)NULL;
+	int		dstFd;
+	int		len;
+	int		srcFd;
+	long		status;
+	struct stat	srcStatbuf;
+	struct utimbuf	times;
+
+	/* entry debugging info */
+
+	echoDebug(DBG_CPPATH_ENTRY, a_ctrl, a_mode, a_srcPath, a_dstPath);
+
+	/* open source file for reading */
+
+	srcFd = open(a_srcPath, O_RDONLY);
+	if (srcFd < 0) {
+		progerr(ERR_OPEN_READ, a_srcPath,
+				errno, strerror(errno));
+		return (1);
+	}
+
+	/* obtain file status of source file */
+
+	if (fstat(srcFd, &srcStatbuf) != 0) {
+		progerr(ERR_FSTAT, srcFd, a_srcPath, errno, strerror(errno));
+		(void) close(srcFd);
+		return (1);
+	}
+
+	/*
+	 * Determine the permissions mode for the destination:
+	 * - if MODE_SET is specified:
+	 * --> use a_mode (do not mask off any portion)
+	 * --> If a_mode is unknown (? in the pkgmap), then the file gets
+	 * --> installed with the default 0644 mode
+	 * - if MODE_SRC is specified:
+	 * --> use the mode of the source (srcStatbuf.st_mode) but mask off all
+	 * --> non-access mode bits (remove SET?UID bits)
+	 * - otherwise:
+	 * --> use 0666
+	 */
+
+	if (a_ctrl & MODE_SET) {
+		mode_t	usemode;
+
+		usemode = (a_mode ^ BADMODE) ? a_mode : 0644;
+		if (a_mode != usemode && usemode == 0644) {
+			logerr(WRN_DEF_MODE, a_dstPath);
+			a_mode = usemode;
+		}
+	} else if (a_ctrl & MODE_SRC) {
+		a_mode = (srcStatbuf.st_mode & S_IAMB);
+	} else {
+		a_mode = 0666;
+	}
+
+	/*
+	 * Get fd of newly created destination file or, if this
+	 * is an overwrite,  a temporary file (linknam).
+	 */
+
+	dstFd = write_file(&linknam, a_ctrl, a_mode, a_dstPath);
+	if (dstFd < 0) {
+		(void) close(srcFd);
+		return (1);
+	}
+
+	/*
+	 * source and target files are open: copy data
+	 */
+
+	status = copyFile(srcFd, dstFd, a_srcPath, a_dstPath, &srcStatbuf, 0);
+
+	(void) close(srcFd);
+	(void) close(dstFd);
+
+	if (status != 0) {
+		progerr(ERR_INPUT, a_srcPath, errno, strerror(errno));
+		if (linknam) {
+			(void) remove(linknam);
+		}
+		return (1);
+	}
+
+	/*
+	 * If this is an overwrite, rename temp over original
+	 */
+
+	if ((linknam != (char *)NULL) && (rename(linknam, a_dstPath) != 0)) {
+		FILE	*logfp = (FILE *)NULL;
+		char	busylog[PATH_MAX];
+
+		/* output log message if busy else program error */
+
+		if (errno == ETXTBSY) {
+			logerr(MSG_PROCMV, linknam);
+		} else {
+			progerr(ERR_OUTPUT_WRITING, a_dstPath, errno,
+				strerror(errno));
+		}
+
+		(void) remove(linknam);
+
+		/* open the log file and append log entry */
+
+		len = snprintf(busylog, sizeof (busylog),
+				"%s/textbusy", get_PKGADM());
+		if (len > sizeof (busylog)) {
+			progerr(ERR_CREATE_PATH_2, get_PKGADM(),
+				"textbusy");
+		} else {
+			logfp = fopen(busylog, "a");
+			if (logfp == NULL) {
+				progerr(ERR_LOG, busylog, errno,
+					strerror(errno));
+			} else {
+				(void) fprintf(logfp, "%s\n", linknam);
+				(void) fclose(logfp);
+			}
+		}
+	}
+
+	/* set access/modification times for target */
+
+	times.actime = srcStatbuf.st_atime;
+	times.modtime = srcStatbuf.st_mtime;
+
+	if (utime(a_dstPath, &times) != 0) {
+		progerr(ERR_MODTIM, a_dstPath, errno, strerror(errno));
+		return (1);
+	}
+
+	/* success! */
+
+	return (0);
+}
+
+/*
+ * This function creates all of the directory components of the specified path.
+ */
+static int
+create_path(int a_ctrl, char *a_file)
+{
+	char	*pt;
+	int	found = 0;
+
+	for (pt = a_file; *pt; pt++) {
+		/* continue if not at path separator or at start of path */
+
+		if ((*pt != '/') || (pt == a_file)) {
+			continue;
+		}
+
+		/* at '/' - terminate path at current entry */
+
+		*pt = '\0';
+
+		/* continue if path element exists */
+
+		if (access(a_file, F_OK) == 0) {
+			*pt = '/';
+			continue;
+		}
+
+		/* create directory in path */
+
+		if (mkdir(a_file, 0755)) {
+			progerr(ERR_MAKE_DIR, a_file, errno, strerror(errno));
+			*pt = '/';
+			return (1);
+		}
+
+		/* display 'implied directory created' message */
+
+		if (a_ctrl & DIR_DISPLAY) {
+			echo(MSG_IMPDIR, a_file);
+		}
+
+		found++;
+
+		*pt = '/';
+	}
+
+	return (!found);
+}
+
+/*
+ * Name:	write_file
+ * Description:	creates a new destination file if the file does not already
+ *		exist; otherwise, creates a temporary file and places a
+ *		pointer to the temporary file name in 'r_linknam'.
+ * Arguments:	r_linknam - pointer to (char*) where name of temporary file
+ *			created is returned
+ *		a_ctrl - determine if the destination file name is displayed:
+ *		     |= DIR_DISPLAY - display "%s <implied directory>"
+ *			if directory created
+ *		a_mode - permissions mode to set a_file to
+ *		a_file - name of destination file to open
+ * Returns:	int
+ *			success - file descriptor of the file it opened.
+ *			failure - returns -1
+ */
+
+static int
+write_file(char **r_linknam, int a_ctrl, mode_t a_mode, char *a_file)
+{
+	int		len;
+	int		fd = -1;
+	static char	loc_link[PATH_MAX];
+
+	/* entry debugging */
+
+	echoDebug(DBG_WRITEFILE_ENTRY, a_ctrl, a_mode, a_file);
+
+	/* reset pointer to returned 'temporary file name' */
+
+	*r_linknam = (char *)NULL;
+
+	/*
+	 * If we are overwriting an existing file, arrange to replace
+	 * it transparently.
+	 */
+
+	if (access(a_file, F_OK) == 0) {
+		/*
+		 * link the file to be copied to a temporary name in case
+		 * it is executing or it is being written/used (e.g., a shell
+		 * script currently being executed
+		 */
+
+		if (!RELATIVE(a_file)) {
+			len = snprintf(loc_link, sizeof (loc_link),
+					"%sXXXXXX", a_file);
+			if (len > sizeof (loc_link)) {
+				progerr(ERR_CREATE_PATH_2, a_file, "XXXXXX");
+			}
+		} else {
+			logerr(WRN_RELATIVE, a_file);
+			len = snprintf(loc_link, sizeof (loc_link),
+					"./%sXXXXXX", a_file);
+			if (len > sizeof (loc_link)) {
+				progerr(ERR_CREATE_PATH_3, "./", a_file,
+					"XXXXXX");
+			}
+		}
+
+		/* create and open temporary file */
+
+		fd = mkstemp(loc_link);
+		if (fd == -1) {
+			progerr(ERR_MKTEMP, loc_link, errno, strerror(errno));
+			return (-1);
+		}
+
+		/* remember name of temporary file */
+
+		*r_linknam = loc_link;
+
+		/* make sure temporary file has correct mode */
+
+		if (fchmod(fd, a_mode) < 0) {
+			progerr(ERR_FCHMOD, loc_link, a_mode, errno,
+				strerror(errno));
+		}
+
+		return (fd);
+	}
+
+	/*
+	 * We are not overwriting an existing file, create a new one directly.
+	 */
+
+	fd = open(a_file, O_WRONLY | O_CREAT | O_TRUNC, a_mode);
+	if (fd == -1) {
+		if (create_path(a_ctrl, a_file) == 0) {
+			fd = open(a_file, O_WRONLY | O_CREAT | O_TRUNC, a_mode);
+		}
+	}
+
+	if (fd == -1) {
+		progerr(ERR_OPEN_WRITE, a_file, errno, strerror(errno));
+	}
+
+	return (fd);
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/usr/src/cmd/svr4pkg/pkginstall/dockspace.c	Fri Jun 05 10:28:40 2009 -0400
@@ -0,0 +1,405 @@
+/*
+ * 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
+ * or http://www.opensolaris.org/os/licensing.
+ * 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.
+ */
+
+/* Copyright (c) 1984, 1986, 1987, 1988, 1989 AT&T */
+/* All Rights Reserved */
+
+
+#include <stdio.h>
+#include <ctype.h>
+#include <string.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <sys/types.h>
+#include <sys/param.h>
+#include <sys/stat.h>
+#include <sys/statvfs.h>
+#include <limits.h>
+#include <locale.h>
+#include <libintl.h>
+#include <pkgstrct.h>
+#include "install.h"
+#include <pkglib.h>
+#include "libadm.h"
+#include "libinst.h"
+#include "pkginstall.h"
+
+extern struct cfextra **extlist;
+extern char	pkgloc[];
+extern char	instdir[];
+
+#define	LSIZE		256
+#define	LIM_BFREE	150LL
+#define	LIM_FFREE	25LL
+
+#define	WRN_STATVFS	"WARNING: unable to stat filesystem mounted on <%s>"
+
+#define	WRN_NOBLKS	"The %s filesystem has %llu free blocks. The current " \
+			"installation requires %llu blocks, which includes a " \
+			"required %llu block buffer for open " \
+			"deleted files. %llu more blocks are needed."
+
+#define	WRN_NOFILES	"The %s filesystem has %llu free file nodes. The " \
+			"current installation requires %llu file nodes, " \
+			"which includes a required %llu file node buffer " \
+			"for temporary files. %llu more file nodes " \
+			"are needed."
+
+#define	TYPE_BLCK	0
+#define	TYPE_NODE	1
+static void	warn(int type, char *name, fsblkcnt_t need, fsblkcnt_t avail,
+			fsblkcnt_t limit);
+static int	fsys_stat(int n);
+static int	readmap(int *error);
+static int	readspace(char *spacefile, int *error);
+
+int
+dockspace(char *spacefile)
+{
+	struct fstable *fs_tab;
+	int	i, error;
+
+	error = 0;
+
+	/*
+	 * Also, vanilla SVr4 code used the output from popen()
+	 * on the "/etc/mount" command.  However, we need to get more
+	 * information about mounted filesystems, so we use the C
+	 * interfaces to the mount table, which also happens to be
+	 * much faster than running another process.  Since several
+	 * of the pkg commands need access to the mount table, this
+	 * code is now in libinst.  However, mount table info is needed
+	 * at the time the base directory is determined, so the call
+	 * to get the mount table information is in main.c
+	 */
+
+	if (readmap(&error) || readspace(spacefile, &error))
+		return (-1);
+
+	for (i = 0; fs_tab = get_fs_entry(i); ++i) {
+		if ((!fs_tab->fused) && (!fs_tab->bused))
+			continue; /* not used by us */
+
+		if (fs_tab->bfree < (LIM_BFREE + fs_tab->bused)) {
+			warn(TYPE_BLCK, fs_tab->name, fs_tab->bused,
+				fs_tab->bfree, LIM_BFREE);
+			error++;
+		}
+
+		/* bug id 1091292 */
+		if ((long)fs_tab->ffree == -1L)
+			continue;
+		if (fs_tab->ffree < (LIM_FFREE + fs_tab->fused)) {
+			warn(TYPE_NODE, fs_tab->name, fs_tab->fused,
+				fs_tab->ffree, LIM_FFREE);
+			error++;
+		}
+	}
+	return (error);
+}
+
+static void
+warn(int type, char *name, fsblkcnt_t need, fsblkcnt_t avail, fsblkcnt_t limit)
+{
+	logerr(gettext("WARNING:"));
+	if (type == TYPE_BLCK) {
+		logerr(gettext(WRN_NOBLKS), name, avail, (need + limit), limit,
+		    (need + limit - avail));
+	} else {
+		logerr(gettext(WRN_NOFILES), name, avail, (need + limit), limit,
+		    (need + limit - avail));
+	}
+}
+
+static int
+fsys_stat(int n)
+{
+	struct statvfs64 svfsb;
+	struct fstable *fs_tab;
+
+	if (n == BADFSYS)
+		return (1);
+
+	fs_tab = get_fs_entry(n);
+
+	/*
+	 * At this point, we know we need information
+	 * about a particular filesystem, so we can do the
+	 * statvfs() now.  For performance reasons, we only want to
+	 * stat the filesystem once, at the first time we need to,
+	 * and so we can key on whether or not we have the
+	 * block size for that filesystem.
+	 */
+	if (fs_tab->bsize != 0)
+		return (0);
+
+	if (statvfs64(fs_tab->name, &svfsb)) {
+		logerr(gettext(WRN_STATVFS), fs_tab->name);
+		return (1);
+	}
+
+	/*
+	 * statvfs returns number of fragment size blocks
+	 * so will change this to number of 512 byte blocks
+	 */
+	fs_tab->bsize  = svfsb.f_bsize;
+	fs_tab->frsize = svfsb.f_frsize;
+	fs_tab->bfree  = ((svfsb.f_frsize > 0) ?
+	    howmany(svfsb.f_frsize, DEV_BSIZE) :
+	    howmany(svfsb.f_bsize, DEV_BSIZE)) * svfsb.f_bavail;
+	fs_tab->ffree  = (svfsb.f_favail > 0) ? svfsb.f_favail : svfsb.f_ffree;
+	return (0);
+}
+
+/*
+ * This function reads all of the package objects, maps them to their target
+ * filesystems and adds up the amount of space used on each. Wherever you see
+ * "fsys_value", that's the apparent filesystem which could be a temporary
+ * loopback mount for the purpose of constructing the client filesystem. It
+ * isn't necessarily the real target filesystem. Where you see "fsys_base"
+ * that's the real filesystem to which fsys_value may just refer. If this is
+ * installing to a standalone or a server, fsys_value will almost always be
+ * the same as fsys_base.
+ */
+static int
+readmap(int *error)
+{
+	struct fstable *fs_tab;
+	struct cfextra *ext;
+	struct cfent *ept;
+	struct stat statbuf;
+	char	tpath[PATH_MAX];
+	fsblkcnt_t	blk;
+	int	i, n;
+
+	/*
+	 * Handle the installation files (ftype i) that are in the
+	 * pkgmap/eptlist.
+	 */
+	for (i = 0; (ext = extlist[i]) != NULL; i++) {
+		ept = &(ext->cf_ent);
+
+		if (ept->ftype != 'i')
+			continue;
+
+		/*
+		 * These paths are treated differently from the others
+		 * since their full pathnames are not included in the
+		 * pkgmap.
+		 */
+		if (strcmp(ept->path, "pkginfo") == 0)
+			(void) sprintf(tpath, "%s/%s", pkgloc, ept->path);
+		else
+			(void) sprintf(tpath, "%s/install/%s", pkgloc,
+			    ept->path);
+
+		/* If we haven't done an fsys() series, do one */
+		if (ext->fsys_value == BADFSYS)
+			ext->fsys_value = fsys(tpath);
+
+		/*
+		 * Now check if this is a base or apparent filesystem. If
+		 * it's just apparent, get the resolved filesystem entry,
+		 * otherwise, base and value are the same.
+		 */
+		if (use_srvr_map_n(ext->fsys_value))
+			ext->fsys_base = resolved_fsys(tpath);
+		else
+			ext->fsys_base = ext->fsys_value;
+
+		if (fsys_stat(ext->fsys_base)) {
+			(*error)++;
+			continue;
+		}
+
+		/*
+		 * Don't accumulate space requirements on read-only
+		 * remote filesystems.
+		 */
+		if (is_remote_fs_n(ext->fsys_value) &&
+		    !is_fs_writeable_n(ext->fsys_value))
+			continue;
+
+		fs_tab = get_fs_entry(ext->fsys_base);
+
+		fs_tab->fused++;
+		if (ept->cinfo.size != BADCONT)
+			blk = nblk(ept->cinfo.size,
+			    fs_tab->bsize,
+			    fs_tab->frsize);
+		else
+			blk = 0;
+		fs_tab->bused += blk;
+	}
+
+	/*
+	 * Handle the other files in the eptlist.
+	 */
+	for (i = 0; (ext = extlist[i]) != NULL; i++) {
+		ept = &(extlist[i]->cf_ent);
+
+		if (ept->ftype == 'i')
+			continue;
+
+		/*
+		 * Don't recalculate package objects that are already in the
+		 * table.
+		 */
+		if (ext->mstat.preloaded)
+			continue;
+
+		/*
+		 * Don't accumulate space requirements on read-only
+		 * remote filesystems.
+		 */
+		if (is_remote_fs(ept->path, &(ext->fsys_value)) &&
+		    !is_fs_writeable(ept->path, &(ext->fsys_value)))
+			continue;
+
+		/*
+		 * Now check if this is a base or apparent filesystem. If
+		 * it's just apparent, get the resolved filesystem entry,
+		 * otherwise, base and value are the same.
+		 */
+		if (use_srvr_map_n(ext->fsys_value))
+			ext->fsys_base = resolved_fsys(tpath);
+		else
+			ext->fsys_base = ext->fsys_value;
+
+		/* At this point we know we have a good fsys_base. */
+		if (fsys_stat(ext->fsys_base)) {
+			(*error)++;
+			continue;
+		}
+
+		/*
+		 * We have to stat this path based upon it's real location.
+		 * If this is a server-remap, ept->path isn't the real
+		 * location.
+		 */
+		if (use_srvr_map_n(ext->fsys_value))
+			strcpy(tpath, server_map(ept->path, ext->fsys_value));
+		else
+			strcpy(tpath, ept->path);
+
+		fs_tab = get_fs_entry(ext->fsys_base);
+		if (stat(tpath, &statbuf)) {
+			/* path cannot be accessed */
+			fs_tab->fused++;
+			if (strchr("dxs", ept->ftype))
+				blk =
+				    nblk(fs_tab->bsize,
+				    fs_tab->bsize,
+				    fs_tab->frsize);
+			else if (ept->cinfo.size != BADCONT)
+				blk = nblk(ept->cinfo.size,
+				    fs_tab->bsize,
+				    fs_tab->frsize);
+			else
+				blk = 0;
+		} else {
+			/* path already exists */
+			if (strchr("dxs", ept->ftype))
+				blk = 0;
+			else if (ept->cinfo.size != BADCONT) {
+				fsblkcnt_t new_size, old_size;
+				new_size = nblk(ept->cinfo.size,
+				    fs_tab->bsize,
+				    fs_tab->frsize);
+				old_size = nblk(statbuf.st_size,
+				    fs_tab->bsize,
+				    fs_tab->frsize);
+				/*
+				 * negative blocks show room freed, but since
+				 * order of installation is uncertain show
+				 * 0 blocks usage
+				 */
+				if (new_size < old_size)
+					blk = 0;
+				else
+					blk = new_size - old_size;
+			} else
+				blk = 0;
+		}
+		fs_tab->bused += blk;
+	}
+	return (0);
+}
+
+static int
+readspace(char *spacefile, int *error)
+{
+	FILE	*fp;
+	char	line[LSIZE];
+	long	blocks, nodes;
+	int	n;
+
+	if (spacefile == NULL)
+		return (0);
+
+	if ((fp = fopen(spacefile, "r")) == NULL) {
+		progerr(gettext("unable to open spacefile %s"), spacefile);
+		return (-1);
+	}
+
+	while (fgets(line, LSIZE, fp)) {
+		struct fstable *fs_tab;
+		char *pt, path[PATH_MAX];
+
+		blocks = nodes = 0;
+		for (pt = line; isspace(*pt); /* void */)
+			pt++;
+		if (*pt == '#' || *pt == '\0')
+			continue;
+
+		(void) sscanf(line, "%s %ld %ld", path, &blocks, &nodes);
+		mappath(2, path);
+		basepath(path, get_basedir(), get_inst_root());
+		canonize(path);
+
+		n = resolved_fsys(path);
+		if (fsys_stat(n)) {
+			(*error)++;
+			continue;
+		}
+
+		/*
+		 * Don't accumulate space requirements on read-only
+		 * remote filesystems. NOTE: For some reason, this
+		 * used to check for !remote && read only. If this
+		 * blows up later, then maybe that was correct -- JST
+		 */
+		if (is_remote_fs_n(n) && !is_fs_writeable_n(n))
+			continue;
+
+		fs_tab = get_fs_entry(n);
+
+		fs_tab->bused += blocks;
+		fs_tab->fused += nodes;
+	}
+	(void) fclose(fp);
+	return (0);
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/usr/src/cmd/svr4pkg/pkginstall/getinst.c	Fri Jun 05 10:28:40 2009 -0400
@@ -0,0 +1,298 @@
+/*
+ * 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
+ * or http://www.opensolaris.org/os/licensing.
+ * 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 2006 Sun Microsystems, Inc.  All rights reserved.
+ * Use is subject to license terms.
+ */
+
+/* Copyright (c) 1984, 1986, 1987, 1988, 1989 AT&T */
+/* All Rights Reserved */
+
+
+#include <stdio.h>
+#include <string.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <valtools.h>
+#include <locale.h>
+#include <libintl.h>
+#include <pkginfo.h>
+#include "install.h"
+#include <pkglib.h>
+#include "libadm.h"
+#include "libinst.h"
+#include "pkginstall.h"
+#include "messages.h"
+
+extern struct admin adm;
+extern char	*pkgarch, *pkgvers, *msgtext, *pkgabrv;
+extern int	opresvr4, maxinst;
+
+static char	newinst[PKGSIZ];
+static char	*nextinst(void);
+static char	*prompt(struct pkginfo *info, int npkgs);
+static int	same_pkg;	/* same PKG, ARCH and VERSION */
+
+/*
+ * This returns the correct package instance based on how many packages are
+ * already installed. If there are none (npkgs == 0), it just returns the
+ * package abbreviation. Otherwise, it interacts with the user (or reads the
+ * admin file) to determine if we should overwrite an instance which is
+ * already installed, or possibly install a new instance of this package
+ */
+char *
+getinst(int *updatingExisting, struct pkginfo *info, int npkgs,
+	boolean_t a_preinstallCheck)
+{
+	char	*inst;
+	char	*sameinst;
+	int	i;
+	int	nsamearch;
+	int	samearch;
+
+	/* entry debugging info */
+
+	same_pkg = 0;
+
+	/*
+	 * If this is the first instance of the package, it's called the by
+	 * the package abbreviation.
+	 */
+
+	if (npkgs == 0) {
+		return (pkgabrv);
+	}
+
+	/*
+	 * this package is already installed; determine how to handle the
+	 * new instance of the package to install
+	 */
+
+	if (ADM(instance, "newonly") || ADM(instance, "quit")) {
+		/*
+		 * new instance is required, or quit if not new
+		 */
+
+		msgtext = MSG_NEWONLY;
+		if (a_preinstallCheck == B_FALSE) {
+			ptext(stderr, msgtext, pkgabrv);
+		} else {
+			(void) fprintf(stdout, "install-new-only=true\n");
+			(void) fprintf(stdout, "ckinstance=4\n");
+		}
+		quit(4);
+	}
+
+	/*
+	 * package already installed and new instance not required
+	 * see if updating the same instance of the package
+	 */
+
+	samearch = nsamearch = 0;
+	sameinst  = NULL;
+	for (i = 0; i < npkgs; i++) {
+		if (strcmp(info[i].arch, pkgarch) == NULL) {
+			samearch = i;
+			nsamearch++;
+			if (strcmp(info[i].version, pkgvers) == NULL) {
+				sameinst = info[i].pkginst;
+			}
+		}
+	}
+
+	if (sameinst) {
+		/* same instance of package */
+		if (a_preinstallCheck == B_FALSE) {
+			ptext(stderr, MSG_SAME);
+		} else {
+			(void) fprintf(stdout, "install-same-instance=true\n");
+			(void) fprintf(stdout, "ckinstance=0\n");
+		}
+
+		inst = sameinst; /* can't be overwriting a pre-svr4 package */
+		same_pkg++;
+		(*updatingExisting)++;
+		return (inst);
+	}
+
+	if (ADM(instance, "overwrite")) {
+		/* not the same instance of the package */
+		if (npkgs == 1) {
+			samearch = 0; /* use only package we know about */
+		} else if (nsamearch != 1) {
+			/*
+			 * more than one instance of the same ARCH is already
+			 * installed on this machine
+			 */
+			msgtext = MSG_OVERWRITE;
+			if (a_preinstallCheck == B_FALSE) {
+				ptext(stderr, msgtext);
+			} else {
+				(void) fprintf(stdout,
+					"install-ovewrite=true\n");
+				(void) fprintf(stdout, "ckinstance=4\n");
+			}
+			quit(4);
+		}
+
+		inst = info[samearch].pkginst;
+		if (info[samearch].status == PI_PRESVR4) {
+			opresvr4++; /* overwriting a pre-svr4 package */
+		}
+
+		(*updatingExisting)++;
+		return (inst);
+	}
+
+	if (ADM(instance, "unique")) {
+		if (maxinst <= npkgs) {
+			/* too many instances */
+			msgtext = MSG_UNIQ1;
+			if (a_preinstallCheck == B_FALSE) {
+				ptext(stderr, msgtext, pkgabrv);
+			} else {
+				(void) fprintf(stdout,
+					"install-too-many-instances=true\n");
+				(void) fprintf(stdout, "ckinstance=4\n");
+			}
+			quit(4);
+		}
+		inst = nextinst();
+		return (inst);
+	}
+
+	if (a_preinstallCheck == B_FALSE) {
+		if (echoGetFlag() == B_FALSE) {
+			msgtext = MSG_NOINTERACT;
+			ptext(stderr, msgtext);
+			quit(5);
+		}
+	} else {
+		(void) fprintf(stdout, "install-new-instance=true\n");
+		(void) fprintf(stdout, "ckinstance=1\n");
+	}
+
+	inst = prompt(info, npkgs);
+	if (strcmp(inst, "new") == NULL) {
+		inst = nextinst();
+		return (inst);
+	}
+
+	(*updatingExisting)++;
+
+	/* see if this instance is presvr4 */
+	for (i = 0; i < npkgs; i++) {
+		if (strcmp(inst, info[i].pkginst) == NULL) {
+			if (info[i].status == PI_PRESVR4) {
+				opresvr4++;
+			}
+			break;
+		}
+	}
+
+	return (inst);
+}
+
+/*
+ * This informs the caller whether the package in question is the same
+ * version and architecture as an installed package of the same name.
+ */
+
+int
+is_samepkg(void) {
+	return (same_pkg);
+}
+
+static char *
+nextinst(void)
+{
+	struct pkginfo info;
+	int	n;
+
+	n = 2; /* requirements say start at 2 */
+
+	info.pkginst = NULL;
+	(void) strcpy(newinst, pkgabrv);
+	while (pkginfo(&info, newinst, NULL, NULL) == 0) {
+		(void) snprintf(newinst, sizeof (newinst),
+				"%s.%d", pkgabrv, n++);
+	}
+	return (newinst);
+}
+
+static char *
+prompt(struct pkginfo *info, int npkgs)
+{
+	CKMENU	*menup;
+	char	*inst;
+	char	ans[MAX_INPUT];
+	char	header[256];
+	char	temp[256];
+	int	i;
+	int	n;
+
+	if (maxinst > npkgs) {
+		/*
+		 * the user may choose to install a completely new
+		 * instance of this package
+		 */
+		n = ckyorn(ans, NULL, NULL, MSG_GETINST_HELP1,
+			MSG_GETINST_PROMPT1);
+		if (n != 0) {
+			quit(n);
+		}
+		if (strchr("yY", *ans) != NULL) {
+			return ("new");
+		}
+	}
+
+	(void) snprintf(header, sizeof (header), MSG_GETINST_HEADER, pkgabrv);
+	menup = allocmenu(header, CKALPHA);
+
+	for (i = 0; i < npkgs; i++) {
+		(void) snprintf(temp, sizeof (temp),
+				"%s %s\n(%s) %s", info[i].pkginst,
+			info[i].name, info[i].arch, info[i].version);
+		if (setitem(menup, temp)) {
+			progerr("no memory");
+			quit(99);
+		}
+	}
+
+	if (npkgs == 1) {
+		printmenu(menup);
+		if (n = ckyorn(ans, NULL, NULL, NULL, MSG_GETINST_PROMPT0))
+			quit(n);
+		if (strchr("yY", *ans) == NULL)
+			quit(3);
+		(void) strcpy(newinst, info[0].pkginst);
+	} else {
+		if (n = ckitem(menup, &inst, 1, NULL, NULL, MSG_GETINST_HELP2,
+		    MSG_GETINST_PROMPT2))
+			quit(n);
+		(void) strcpy(newinst, inst);
+	}
+	(void) setitem(menup, 0); /* clear resource usage */
+	free(menup); /* clear resource usage */
+
+	return (newinst);
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/usr/src/cmd/svr4pkg/pkginstall/instvol.c	Fri Jun 05 10:28:40 2009 -0400
@@ -0,0 +1,1781 @@
+/*
+ * 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
+ * or http://www.opensolaris.org/os/licensing.
+ * 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 2009 Sun Microsystems, Inc.  All rights reserved.
+ * Use is subject to license terms.
+ */
+
+/* Copyright (c) 1984, 1986, 1987, 1988, 1989 AT&T */
+/* All Rights Reserved */
+
+
+#include <stdio.h>
+#include <string.h>
+#include <locale.h>
+#include <libintl.h>
+#include <dirent.h>
+#include <pkgstrct.h>
+#include <pkgdev.h>
+#include <pkglocs.h>
+#include <archives.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <sys/stat.h>
+#include <sys/param.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <assert.h>
+#include <wait.h>
+
+/*
+ * libinstzones includes
+ */
+
+#include <instzones_api.h>
+
+/*
+ * consolidation pkg command library includes
+ */
+
+#include <pkglib.h>
+#include <pkgweb.h>
+
+/*
+ * local pkg command library includes
+ */
+
+#include <install.h>
+#include <libinst.h>
+#include <libadm.h>
+#include <dryrun.h>
+#include <messages.h>
+
+/*
+ * pkginstall local includes
+ */
+
+#include "pkginstall.h"
+
+extern int		pkgverbose;
+extern fsblkcnt_t	pkgmap_blks; 		/* main.c */
+
+extern struct pkgdev pkgdev;
+
+extern char	tmpdir[];
+extern char	pkgbin[];
+extern char	instdir[];
+extern char	saveSpoolInstallDir[];
+extern char	*pkginst;
+
+extern int	dbchg;
+extern int	nosetuid;
+extern int	nocnflct;
+extern int	warnflag;
+
+#define	DMRG_DONE	-1
+
+#define	ck_efile(s, p)	\
+		((p->cinfo.modtime >= 0) && \
+		p->ainfo.local && \
+		cverify(0, &p->ftype, s, &p->cinfo, 1))
+
+static int	eocflag;
+
+/*
+ * The variable below indicates that fix_attributes() will be inadequate
+ * because a replacement was permitted.
+ */
+static int	repl_permitted = 0;
+
+static int	domerg(struct cfextra **extlist, int part, int nparts,
+			int myclass, char **srcp, char **dstp,
+			char **r_updated, char **r_skipped,
+			char **r_anyPathLocal);
+static void	endofclass(struct cfextra **extlist, int myclass,
+			int ckflag, VFP_T **a_cfVfp, VFP_T **a_cfTmpVfp);
+static int	fix_attributes(struct cfextra **, int);
+static int	dir_is_populated(char *dirpath);
+static boolean_t absolutepath(char *path);
+static boolean_t parametricpath(char *path, char **relocpath);
+
+/* Used to keep track of the entries in extlist that are regular files. */
+struct reg_files {
+	struct reg_files *next;
+	int val;
+};
+static struct reg_files *regfiles_head = NULL;
+
+/*
+ * This is the function that actually installs one volume (usually that's
+ * all there is). Upon entry, the extlist is entirely correct:
+ *
+ *	1. It contains only those files which are to be installed
+ *	   from all volumes.
+ *	2. The mode bits in the ainfo structure for each file are set
+ *	   correctly in accordance with administrative defaults.
+ *	3. mstat.setuid/setgid reflect what the status *was* before
+ *	   pkgdbmerg() processed compliance.
+ */
+void
+instvol(struct cfextra **extlist, char *srcinst, int part,
+	int nparts, VFP_T **a_cfVfp, VFP_T **a_cfTmpVfp,
+	char **r_updated, char **r_skipped,
+	char *a_zoneName)
+{
+	FILE		*listfp;
+	char		*updated = (char *)NULL;
+	char		*skipped = (char *)NULL;
+	char		*anyPathLocal = (char *)NULL;
+	char		*relocpath = (char *)NULL;
+	char		*dstp;
+	char		*listfile;
+	char		*srcp;
+	char		*pspool_loc;
+	char		scrpt_dst[PATH_MAX];
+	int		count;
+	int		entryidx;	/* array of current package objects */
+	int		n;
+	int		nc = 0;
+	int		pass;		/* pass count through the for loop. */
+	int		tcount;
+	struct cfent	*ept;
+	struct cfextra	*ext;
+	struct mergstat	*mstat;
+	struct reg_files *rfp = NULL;
+
+	/*
+	 * r_updated and r_skipped are optional parameters that can be passed in
+	 * by the caller if the caller wants to know if any objects are either
+	 * updated or skipped. Do not initialize either r_updated or r_skipped;
+	 * the call to instvol could be cumulative and any previous update or
+	 * skipped indication must not be disturbed - these flags are only set,
+	 * they must never be reset. These flags are "char *" pointers so that
+	 * the object that was skipped or updated can be displayed in debugging
+	 * output.
+	 */
+
+	if (part == 1) {
+		pkgvolume(&pkgdev, srcinst, part, nparts);
+	}
+
+	tcount = 0;
+	nc = cl_getn();
+
+	/*
+	 * For each class in this volume, install those files.
+	 *
+	 * NOTE : This loop index may be decremented by code below forcing a
+	 * second trip through for the same class. This happens only when a
+	 * class is split between an archive and the tree. Examples would be
+	 * old WOS packages and the occasional class containing dynamic
+	 * libraries which require special treatment.
+	 */
+
+	if (is_depend_pkginfo_DB() == B_FALSE) {
+	    int		classidx;	/* the current class */
+
+	    for (classidx = 0; classidx < nc; classidx++) {
+		int pass_relative = 0;
+		int rel_init = 0;
+
+		eocflag = count = pass = 0;
+		listfp = (FILE *)0;
+		listfile = NULL;
+
+		/* Now what do we pass to the class action script */
+
+		if (cl_pthrel(classidx) == REL_2_CAS) {
+			pass_relative = 1;
+		}
+
+		for (;;) {
+			if (!tcount++) {
+				/* first file to install */
+				if (a_zoneName == (char *)NULL) {
+					echo(MSG_INS_N_N, part, nparts);
+				} else {
+					echo(MSG_INS_N_N_LZ, part, nparts,
+						a_zoneName);
+				}
+			}
+
+			/*
+			 * If there's an install class action script and no
+			 * list file has been created yet, create that file
+			 * and provide the pointer in listfp.
+			 */
+			if (cl_iscript(classidx) && !listfp) {
+				/* create list file */
+				putparam("TMPDIR", tmpdir);
+				listfile = tempnam(tmpdir, "list");
+				if ((listfp = fopen(listfile, "w")) == NULL) {
+					progerr(ERR_WTMPFILE, listfile);
+					quit(99);
+				}
+			}
+
+			/*
+			 * The following function goes through the package
+			 * object list returning the array index of the next
+			 * regular file. If it encounters a directory,
+			 * symlink, named pipe or device, it just creates it.
+			 */
+
+			entryidx = domerg(extlist, (pass++ ? 0 : part), nparts,
+				classidx, &srcp, &dstp, &updated, &skipped,
+				&anyPathLocal);
+
+			/* Evaluate the return code */
+			if (entryidx == DMRG_DONE) {
+				/*
+				 * Set ept to the first entry in extlist
+				 * which is guaranteed to exist so
+				 * later checks against ept->ftype are
+				 * not compared to NULL.
+				 */
+				ext = extlist[0];
+				ept = &(ext->cf_ent);
+				break; /* no more entries to process */
+			}
+
+			ext = extlist[entryidx];
+			ept = &(ext->cf_ent);
+			mstat = &(ext->mstat);
+
+			/*
+			 * If not installing from a partially spooled package
+			 * (the "save/pspool" area), and the file contents can
+			 * be changed (type is 'e' or 'v'), and the class is not
+			 * "none": copy the file from the package (in pristine
+			 * state with no actions performed) into the appropriate
+			 * location in the packages destination "save/pspool"
+			 * area.
+			 */
+
+			if ((!is_partial_inst()) &&
+				((ept->ftype == 'e') || (ept->ftype == 'v')) &&
+				(strcmp(ept->pkg_class, "none") != 0)) {
+
+				if (absolutepath(ext->map_path) == B_TRUE &&
+					parametricpath(ext->cf_ent.ainfo.local,
+						&relocpath) == B_FALSE) {
+					pspool_loc = ROOT;
+				} else {
+					pspool_loc = RELOC;
+				}
+
+				n = snprintf(scrpt_dst, PATH_MAX, "%s/%s/%s",
+					saveSpoolInstallDir, pspool_loc,
+					relocpath ? relocpath : ext->map_path);
+
+				if (n >= PATH_MAX) {
+					progerr(ERR_CREATE_PATH_2,
+						saveSpoolInstallDir,
+						ext->map_path);
+					quit(99);
+				}
+
+				/* copy, preserve source file mode */
+
+				if (cppath(MODE_SRC, srcp, scrpt_dst, 0644)) {
+					warnflag++;
+				}
+			}
+
+			/*
+			 * If this isn't writeable anyway, it's not going
+			 * into the list file. Only count it if it's going
+			 * into the list file.
+			 */
+			if (is_fs_writeable(ext->cf_ent.path,
+				&(ext->fsys_value)))
+				count++;
+
+			pkgvolume(&pkgdev, srcinst, part, nparts);
+
+			/*
+			 * If source verification is OK for this class, make
+			 * sure the source we're passing to the class action
+			 * script is useable.
+			 */
+			if (cl_svfy(classidx) != NOVERIFY) {
+				if (cl_iscript(classidx) ||
+					((ept->ftype == 'e') ||
+					(ept->ftype == 'n'))) {
+					if (ck_efile(srcp, ept)) {
+						progerr(ERR_CORRUPT,
+							srcp);
+						logerr(getErrbufAddr());
+						warnflag++;
+						continue;
+					}
+				}
+			}
+
+			/*
+			 * If there's a class action script for this class,
+			 * just collect names in a temporary file
+			 * that will be used as the stdin when the
+			 * class action script is invoked.
+			 */
+
+			if ((cl_iscript(classidx)) &&
+					((is_fs_writeable(ept->path,
+						&(ext->fsys_value))))) {
+				if (pass_relative) {
+					if (!rel_init) {
+						(void) fputs(instdir, listfp);
+						(void) putc('\n', listfp);
+						rel_init++;
+					}
+					(void) fputs(ext->map_path, listfp);
+					(void) putc('\n', listfp);
+				} else {
+					(void) fputs(srcp ?
+						srcp : "/dev/null", listfp);
+					(void) putc(' ', listfp);
+					(void) fputs(dstp, listfp);
+					(void) putc('\n', listfp);
+				}
+				/*
+				 * Note which entries in extlist are regular
+				 * files to be installed via the class action
+				 * script.
+				 */
+				if (regfiles_head == NULL) {
+					assert(rfp == NULL);
+					regfiles_head =
+					    malloc(sizeof (struct reg_files));
+					if (regfiles_head == NULL) {
+						progerr(ERR_MEMORY, errno);
+						quit(99);
+					}
+					regfiles_head->next = NULL;
+					regfiles_head->val = entryidx;
+					rfp = regfiles_head;
+				} else {
+					assert(rfp != NULL);
+					rfp->next =
+					    malloc(sizeof (struct reg_files));
+					if (rfp->next == NULL) {
+						progerr(ERR_MEMORY, errno);
+						quit(99);
+					}
+					rfp = rfp->next;
+					rfp->next = NULL;
+					rfp->val = entryidx;
+				}
+
+				/*
+				 * A warning message about unwritable targets
+				 * in a class may be appropriate here.
+				 */
+				continue;
+			}
+
+			/*
+			 * If not installing from a partially spooled package
+			 * (the "save/pspool" area), and the file contents can
+			 * be changed (type is 'e' or 'v') and the class
+			 * identifier is not "none": copy the file from the
+			 * package (in pristine state with no actions performed)
+			 * into the appropriate location in the packages
+			 * destination "save/pspool" area.
+			 */
+
+			if ((!is_partial_inst()) &&
+			    ((ept->ftype == 'e') || (ept->ftype == 'v') &&
+			    (strcmp(ept->pkg_class, "none") != 0))) {
+
+				if (absolutepath(ext->map_path) == B_TRUE &&
+					parametricpath(ext->cf_ent.ainfo.local,
+						&relocpath) == B_FALSE) {
+					pspool_loc = ROOT;
+				} else {
+					pspool_loc = RELOC;
+				}
+
+				n = snprintf(scrpt_dst, PATH_MAX, "%s/%s/%s",
+					saveSpoolInstallDir, pspool_loc,
+					relocpath ? relocpath : ext->map_path);
+
+				if (n >= PATH_MAX) {
+					progerr(ERR_CREATE_PATH_2,
+						saveSpoolInstallDir,
+						ext->map_path);
+					quit(99);
+				}
+
+				/* copy, preserve source file mode */
+
+				if (cppath(MODE_SRC, srcp, scrpt_dst, 0644)) {
+					warnflag++;
+				}
+			}
+
+			/*
+			 * There are several tests here to determine
+			 * how we're going to deal with objects
+			 * intended for remote read-only filesystems.
+			 * We don't use is_served() because this may be
+			 * a server. We're actually interested in if
+			 * it's *really* remote and *really* not
+			 * writeable.
+			 */
+
+			n = is_remote_fs(ept->path, &(ext->fsys_value));
+			if ((n != 0) &&
+				!is_fs_writeable(ept->path,
+				&(ext->fsys_value))) {
+
+				/*
+				 * Don't change the file, we can't write
+				 * to it anyway.
+				 */
+
+				mstat->attrchg = 0;
+				mstat->contchg = 0;
+
+				/*
+				 * If it's currently mounted, we can
+				 * at least test it for existence.
+				 */
+
+				if (is_mounted(ept->path, &(ext->fsys_value))) {
+					if (!isfile(NULL, dstp)) {
+						echo(MSG_IS_PRESENT, dstp);
+					} else {
+						echo(WRN_INSTVOL_NONE, dstp);
+					}
+				} else {
+					char *server_host;
+
+					server_host = get_server_host(
+						ext->fsys_value);
+
+					/* If not, we're just stuck. */
+					echo(WRN_INSTVOL_NOVERIFY,
+						dstp, server_host);
+				}
+
+				continue;
+			}
+
+			/* echo output destination name */
+
+			echo("%s", dstp);
+
+			/*
+			 * if no source then no need to copy/verify
+			 */
+
+			if (srcp == (char *)NULL) {
+				continue;
+			}
+
+			/*
+			 * If doing a partial installation (creating a
+			 * non-global zone), extra steps need to be taken:
+			 *
+			 * 1) if the file is not type 'e' and not type 'v' and
+			 * the class is "none": then the file must already
+			 * exist (as a result of the initial non-global zone
+			 * installation which caused all non-e/v files to be
+			 * copied from the global zone to the non-global
+			 * zone). If this is the case, verify that the file
+			 * exists and has the correct attributes.
+			 *
+			 * 2) if the file is not type 'e' and not type 'v'
+			 * and the class is NOT "none", *OR* if the file is
+			 * type 'e' or type 'v': then check to see if the
+			 * file is located in an area inherited from the
+			 * global zone. If so, then there is no ability to
+			 * change the file since inherited file systems are
+			 * "read only" - just verify that the file exists and
+			 * verify attributes only if not 'e' or 'v'.
+			 */
+
+			if (is_partial_inst() != 0) {
+
+				/*
+				 * determine if the destination package is in an
+				 * area inherited from the global zone
+				 */
+
+				n = pkgMatchInherited(srcp, dstp,
+					get_inst_root(), ept->ainfo.mode,
+					ept->cinfo.modtime, ept->ftype,
+					ept->cinfo.cksum);
+
+				echoDebug(DBG_INSTVOL_PARTIAL_INST,
+					srcp ? srcp : "", dstp ? dstp: "",
+					((get_inst_root()) &&
+					(strcmp(get_inst_root(), "/") != 0)) ?
+					get_inst_root() : "",
+					ept->ainfo.mode, ept->cinfo.modtime,
+					ept->ftype, ept->cinfo.cksum, n);
+
+				/*
+				 * if not type 'e|v' and class 'none', then the
+				 * file must already exist.
+				 */
+
+				if ((ept->ftype != 'e') &&
+					(ept->ftype != 'v') &&
+					(strcmp(cl_nam(ept->pkg_class_idx),
+								"none") == 0)) {
+
+					/*
+					 * if the file is in a space inherited
+					 * from the global zone, and if the
+					 * contents or attributes are incorrect,
+					 * then generate a warning that the
+					 * global zone file contents and/or file
+					 * attributes have been modified and
+					 * that the modifications are extended
+					 * to the non-global zone (inherited
+					 * from the global zone).
+					 */
+
+					if (n == 0) {
+						/* is file changed? */
+						n = finalck(ept, 1, 1, B_TRUE);
+
+						/* no - ok - continue */
+						if (n == 0) {
+							continue;
+						}
+
+						/* output warning message */
+						logerr(NOTE_INSTVOL_FINALCKFAIL,
+							pkginst, ext->map_path,
+							a_zoneName, ept->path);
+						continue;
+					} else if (!finalck(ept, 1, 1,
+								B_FALSE)) {
+						/*
+						 * non-e/v file of class "none"
+						 * not inherited from the global
+						 * zone: verify file already
+						 * exists:everything checks here
+						 */
+						mstat->attrchg = 0;
+						mstat->contchg = 0;
+					}
+					continue;
+				}
+
+				/*
+				 * non-e/v file with class action script, or
+				 * e/v file: if the file is in an area inherited
+				 * from the global zone, then no need (or the
+				 * ability) to update just accept the file as is
+				 */
+
+				if (n == B_TRUE) {
+					/*
+					 * the object is in an area inherited
+					 * from the global zone and the objects
+					 * attributes are verified
+					 */
+
+					mstat->attrchg = 0;
+					mstat->contchg = 0;
+
+					/* NOTE: package object skipped */
+
+					if (skipped == (char *)NULL) {
+						skipped = dstp;
+					echoDebug(DBG_INSTVOL_OBJ_SKIPPED,
+								skipped);
+					}
+					continue;
+				}
+			}
+
+			/*
+			 * Copy from source media to target path and fix file
+			 * mode and permission now in case installation halted.
+			 */
+
+			if (z_path_is_inherited(dstp, ept->ftype,
+			    get_inst_root()) == B_FALSE) {
+
+				/*
+				 * If the filesystem is read-only don't attempt
+				 * to copy a file. Just check that the content
+				 * and attributes of the file are correct.
+				 *
+				 * Normally this doesn't happen, because files,
+				 * which don't change, are not returned by
+				 * domerg(). However when installing a patch in
+				 * a sparse zone, which was already installed
+				 * in global zone with -G option, NGZ's
+				 * contents db still contains the old record
+				 * for this file and therefore domerg()
+				 * considers these files to be different even
+				 * though they are the same.
+				 */
+				n = 0;
+				if (is_fs_writeable(ept->path,
+				    &(ext->fsys_value)))
+					n = cppath(MODE_SET|DIR_DISPLAY, srcp,
+					    dstp, ept->ainfo.mode);
+
+				if (n != 0) {
+					warnflag++;
+				} else if (!finalck(ept, 1, 1, B_FALSE)) {
+					/*
+					 * everything checks here
+					 */
+					mstat->attrchg = 0;
+					mstat->contchg = 0;
+				}
+			}
+
+			/* NOTE: a package object was updated */
+
+			if (updated == (char *)NULL) {
+				echoDebug(DBG_INSTVOL_OBJ_UPDATED, dstp);
+				updated = dstp;
+			}
+		}
+
+		/*
+		 * We have now completed processing of all pathnames
+		 * associated with this volume and class.
+		 */
+		if (cl_iscript(classidx)) {
+			/*
+			 * Execute appropriate class action script
+			 * with list of source/destination pathnames
+			 * as the input to the script.
+			 */
+
+			if (chdir(pkgbin)) {
+				progerr(ERR_CHGDIR, pkgbin);
+				quit(99);
+			}
+
+			if (listfp) {
+				(void) fclose(listfp);
+			}
+
+			/*
+			 * if the object associated with the class action script
+			 * is in an area inherited from the global zone, then
+			 * there is no need to run the class action script -
+			 * assume that anything the script would do has already
+			 * been done in the area shared from the global zone.
+			 */
+
+			/* nothing updated, nothing skipped */
+
+			echoDebug(DBG_INSTVOL_CAS_INFO, is_partial_inst(),
+				updated ? updated : "",
+				skipped ? skipped : "",
+				anyPathLocal ? anyPathLocal : "");
+
+			if ((is_partial_inst() != 0) &&
+					(updated == (char *)NULL) &&
+					(anyPathLocal == (char *)NULL)) {
+
+				/*
+				 * installing in non-global zone, and no object
+				 * has been updated (installed/verified in non-
+				 * inherited area), and no path delivered by the
+				 * package is in an area not inherited from the
+				 * global zone (all paths delivered are in
+				 * areas inherited from the global zone): do not
+				 * run the class action script because the only
+				 * affected areas are inherited (read only).
+				 */
+
+				echoDebug(DBG_INSTVOL_NOT_RUNNING_CAS,
+					a_zoneName ? a_zoneName : "?",
+					eocflag ? "ENDOFCLASS" :
+							cl_iscript(classidx),
+					cl_nam(classidx),
+					cl_iscript(classidx));
+
+				if ((r_skipped != (char **)NULL) &&
+					(*r_skipped == (char *)NULL) &&
+					(skipped == (char *)NULL)) {
+					skipped = "postinstall";
+					echoDebug(DBG_INSTVOL_OBJ_SKIPPED,
+								skipped);
+				}
+			} else {
+				/* run the class action script */
+
+				echoDebug(DBG_INSTVOL_RUNNING_CAS,
+					a_zoneName ? a_zoneName : "?",
+					eocflag ? "ENDOFCLASS" :
+							cl_iscript(classidx),
+					cl_nam(classidx),
+					cl_iscript(classidx));
+
+				/* Use ULIMIT if supplied. */
+				set_ulimit(cl_iscript(classidx), ERR_CASFAIL);
+
+				if (eocflag) {
+					/*
+					 * end of class detected.
+					 * Since there are no more volumes which
+					 * contain pathnames associated with
+					 * this class, execute class action
+					 * script with the ENDOFCLASS argument;
+					 * we do this even if none of the path
+					 * names associated with this class and
+					 * volume needed installation to
+					 * guarantee the class action script is
+					 * executed at least once during package
+					 * installation.
+					 */
+					if (pkgverbose) {
+						n = pkgexecl((listfp ?
+							listfile : CAS_STDIN),
+							CAS_STDOUT,
+							CAS_USER, CAS_GRP,
+							SHELL, "-x",
+							cl_iscript(classidx),
+							"ENDOFCLASS", NULL);
+					} else {
+						n = pkgexecl(
+							(listfp ?
+							listfile : CAS_STDIN),
+							CAS_STDOUT, CAS_USER,
+							CAS_GRP, SHELL,
+							cl_iscript(classidx),
+							"ENDOFCLASS", NULL);
+					}
+					ckreturn(n, ERR_CASFAIL);
+				} else if (count) {
+					/* execute class action script */
+					if (pkgverbose) {
+						n = pkgexecl(listfile,
+							CAS_STDOUT, CAS_USER,
+							CAS_GRP, SHELL, "-x",
+							cl_iscript(classidx),
+							NULL);
+					} else {
+						n = pkgexecl(listfile,
+							CAS_STDOUT, CAS_USER,
+							CAS_GRP, SHELL,
+							cl_iscript(classidx),
+							NULL);
+					}
+					ckreturn(n, ERR_CASFAIL);
+				}
+
+				/*
+				 * Ensure the mod times on disk match those
+				 * in the pkgmap. In this case, call cverify
+				 * with checksumming disabled, since the only
+				 * action that needs to be done is to verify
+				 * that the attributes are correct.
+				 */
+
+				if ((rfp = regfiles_head) != NULL) {
+					while (rfp != NULL) {
+					    ept = &(extlist[rfp->val]->cf_ent);
+					    cverify(1, &ept->ftype, ept->path,
+						&ept->cinfo, 0);
+					    rfp = rfp->next;
+					}
+					regfiles_free();
+				}
+
+				clr_ulimit();
+
+				if ((r_updated != (char **)NULL) &&
+					(*r_updated == (char *)NULL) &&
+					(updated == (char *)NULL)) {
+					updated = "postinstall";
+					echoDebug(DBG_INSTVOL_OBJ_UPDATED,
+								updated);
+				}
+			}
+			if (listfile) {
+				(void) remove(listfile);
+			}
+		}
+
+		if (eocflag && (!is_partial_inst() || (is_partial_inst() &&
+			strcmp(cl_nam(classidx), "none") != 0))) {
+			if (cl_dvfy(classidx) == QKVERIFY && !repl_permitted) {
+				/*
+				 * The quick verify just fixes everything.
+				 * If it returns 0, all is well. If it
+				 * returns 1, then the class installation
+				 * was incomplete and we retry on the
+				 * stuff that failed in the conventional
+				 * way (without a CAS). this is primarily
+				 * to accomodate old archives such as are
+				 * found in pre-2.5 WOS; but, it is also
+				 * used when a critical dynamic library
+				 * is not archived with its class.
+				 */
+				if (!fix_attributes(extlist, classidx)) {
+					/*
+					 * Reset the CAS pointer. If the
+					 * function returns 0 then there
+					 * was no script there in the first
+					 * place and we'll just have to
+					 * call this a miss.
+					 */
+					if (cl_deliscript(classidx))
+						/*
+						 * Decrement classidx for
+						 * next pass.
+						 */
+						classidx--;
+				}
+			} else {
+				/*
+				 * Finalize merge. This checks to make sure
+				 * file attributes are correct and any links
+				 * specified are created.
+				 */
+				(void) endofclass(extlist, classidx,
+					(cl_iscript(classidx) ? 0 : 1),
+					a_cfVfp, a_cfTmpVfp);
+			}
+		}
+	    }
+	}
+
+	/*
+	 * Instead of creating links back to the GZ files the logic is
+	 * to let zdo recreate the files from the GZ then invoke pkgadd to
+	 * install the editable files and skip over any 'f'type files.
+	 * The commented out block is to create the links which should be
+	 * removed once the current code is tested to be correct.
+	 */
+
+	/*
+	 * Go through extlist creating links for 'f'type files
+	 * if we're in a global zone. Note that this code lies
+	 * here instead of in the main loop to support CAF packages.
+	 * In a CAF package the files are installed by the i.none script
+	 * and don't exist until all files are done being processed, thus
+	 * the additional loop through extlist.
+	 */
+
+	/*
+	 * output appropriate completion message
+	 */
+
+	if (is_depend_pkginfo_DB() == B_TRUE) {
+		/* updating database only (hollow package) */
+		if (a_zoneName == (char *)NULL) {
+			echo(MSG_DBUPD_N_N, part, nparts);
+		} else {
+			echo(MSG_DBUPD_N_N_LZ, part, nparts, a_zoneName);
+		}
+	} else if (tcount == 0) {
+		/* updating package (non-hollow package) */
+		if (a_zoneName == (char *)NULL) {
+			echo(MSG_INST_N_N, part, nparts);
+		} else {
+			echo(MSG_INST_N_N_LZ, part, nparts, a_zoneName);
+		}
+	}
+
+	/*
+	 * if any package objects were updated (not inherited from the
+	 * global zone or otherwise already in existence), set the updated
+	 * flag as appropriate
+	 */
+
+	if (updated != (char *)NULL) {
+		echoDebug(DBG_INSTVOL_OBJ_UPDATED, updated);
+		if (r_updated != (char **)NULL) {
+			*r_updated = updated;
+		}
+	}
+
+	/*
+	 * if any package objects were skipped (verified inherited from the
+	 * global zone), set the skipped flag as appropriate
+	 */
+
+	if (skipped != (char *)NULL) {
+		echoDebug(DBG_INSTVOL_OBJ_SKIPPED, skipped);
+		if (r_skipped != (char **)NULL) {
+			*r_skipped = skipped;
+		}
+	}
+}
+
+/*
+ * Name:	domerg
+ * Description: For the specified class, review each entry and return the array
+ *		index number of the next regular file to process. Hard links are
+ *		skipped (they are created in endofclass() and directories,
+ *		symlinks, pipes and devices are created here, as well as any
+ *		file that already exists and has the correct attributes.
+ * Arguments:	struct cfextra **extlist - [RO, *RW]
+ *			- Pointer to list of cfextra structures representing
+ *			  the pkgmap of the package to be installed
+ *		int part - [RO, *RO]
+ *			- the part of the package currently being processed;
+ *			  packages begin with part "1" and proceed for the
+ *			  number (nparts) that comprise the package (volume).
+ *		int nparts - [RO, *RO]
+ *			- the number of parts the package is divided into
+ *		int myclass - [RO, *RO]
+ *			- index into class array of the current class
+ *		char **srcp - [RW, *RW]
+ *			- pointer to pointer to string representing the source
+ *			  path for the next package to process - if this
+ *			  function returns != DMRG_DONE then this pointer is
+ *			  set to a pointer to a string representing the source
+ *			  path for the next object from the package to process
+ *		char **dstp - [RW, *RW]
+ *			- pointer to pointer to string representing the target
+ *			  path for the next package to process - if this
+ *			  function returns != DMRG_DONE then this pointer is
+ *			  set to a pointer to a string representing the target
+ *			  path for the next object from the package to process
+ *		char **r_updated - [RO, *RW]
+ *			- pointer to pointer to string - set if the last path
+ *			  returned exists or does not need updating and the
+ *			  object is NOT located in an area inherited from the
+ *			  global zone. This is used to determine if the last
+ *			  path object returned DOES exist in an area that is
+ *			  inherited from the global zone. If no paths are
+ *			  inherited from the global zone, this is always set
+ *			  when a path to be installed exists and has the
+ *			  correct contents.
+ *		char **r_skipped - [RO, *RW]
+ *			- pointer to pointer to string - set if the last path
+ *			  returned exists or does not need updating and the
+ *			  object IS located in an area inherited from the
+ *			  global zone. This is used to determine if the last
+ *			  path object returned does NOT exist in an area that
+ *			  is inherited from the global zone. If no paths are
+ *			  inherited from the global zone, this is never set.
+ *		char **r_anyPathLocal - [RO, *RW]
+ *			- pointer to pointer to string - set if any object
+ *			  belonging to the package is NOT located in an area
+ *			  inherited from the global zone. This is used to
+ *			  determine if the package references ANY objects that
+ *			  are NOT located in an area inherited from the global
+ *			  zone - regardless of whether or not they need to be
+ *			  updated (installed/copied). If no paths are inherited
+ *			  from the global zone, this is always set when a path
+ *			  to be installed already exists and has the correct
+ *			  contents.
+ * Returns:	int
+ *			!= DMRG_DONE - index into extlist of the next path to
+ *				be processed - that needs to be installed/copied
+ *			== DMRG_DONE - all entries processed
+ */
+
+static int
+domerg(struct cfextra **extlist, int part, int nparts,
+	int myclass, char **srcp, char **dstp,
+	char **r_updated, char **r_skipped,
+	char **r_anyPathLocal)
+{
+	boolean_t	stateFlag = B_FALSE;
+	int		i;
+	int		msg_ugid;
+	static int	maxvol = 0;
+	static int	svindx = 0;
+	static int	svpart = 0;
+	struct cfent	*ept = (struct cfent *)NULL;
+	struct mergstat *mstat = (struct mergstat *)NULL;
+
+	/* reset returned path pointers */
+
+	*dstp = (char *)NULL;
+	*srcp = (char *)NULL;
+
+	/* set to start or continue based on which part being processed */
+
+	if (part != 0) {
+		maxvol = 0;
+		svindx = 0;
+		svpart = part;
+	} else {
+		i = svindx;
+		part = svpart;
+	}
+
+	/*
+	 * This goes through the pkgmap entries one by one testing them
+	 * for inclusion in the package database as well as for validity
+	 * against existing files.
+	 */
+	for (i = svindx; extlist[i]; i++) {
+		ept = &(extlist[i]->cf_ent);
+		mstat = &(extlist[i]->mstat);
+
+		/*
+		 * as paths are processed, if the "anyPathLocal" flag has not
+		 * been set, if the object is not of type 'i' (package script),
+		 * check to see if the object is in an area inherited from the
+		 * global zone - if not, set "anyPathLocal" to the path found,
+		 * indicating that at least one path is in an area that is not
+		 * inherited from the global zone.
+		 */
+
+		if ((r_anyPathLocal != (char **)NULL) &&
+			(*r_anyPathLocal == (char *)NULL) &&
+			(ept->ftype != 'i') &&
+			(z_path_is_inherited(ept->path, ept->ftype,
+						get_inst_root()) == B_FALSE)) {
+			echoDebug(DBG_INSTVOL_OBJ_LOCAL, ept->path);
+			*r_anyPathLocal = ept->path;
+		}
+
+		/* if this isn't the class of current interest, skip it */
+
+		if (myclass != ept->pkg_class_idx) {
+			continue;
+		}
+
+		/* if the class is invalid, announce it & exit */
+		if (ept->pkg_class_idx == -1) {
+			progerr(ERR_CLIDX, ept->pkg_class_idx,
+			    (ept->path && *ept->path) ? ept->path : "unknown");
+			logerr(gettext("pathname=%s\n"),
+			    (ept->path && *ept->path) ? ept->path : "unknown");
+			logerr(gettext("class=<%s>\n"),
+			    (ept->pkg_class && *ept->pkg_class) ?
+			    ept->pkg_class : "Unknown");
+			logerr(gettext("CLASSES=<%s>\n"),
+			    getenv("CLASSES") ? getenv("CLASSES") : "Not Set");
+			quit(99);
+		}
+
+		/*
+		 * Next check to see if we are going to try to delete a
+		 * populated directory in some distressing way.
+		 */
+		if (mstat->dir2nondir)
+			if (dir_is_populated(ept->path)) {
+				logerr(WRN_INSTVOL_NOTDIR, ept->path);
+				warnflag++;
+				mstat->denied = 1;	/* install denied! */
+				continue;
+			} else {	/* Replace is OK. */
+				/*
+				 * Remove this directory, so it won't
+				 * interfere with creation of the new object.
+				 */
+				if (rmdir(ept->path)) {
+					/*
+					 * If it didn't work, there's nothing
+					 * we can do. To continue would
+					 * likely corrupt the filesystem
+					 * which is unacceptable.
+					 */
+					progerr(ERR_RMDIR, ept->path);
+					quit(99);
+				}
+
+				repl_permitted = 1;	/* flag it */
+			}
+
+		/* adjust the max volume number appropriately */
+
+		if (ept->volno > maxvol) {
+			maxvol = ept->volno;
+		}
+
+		/* if this part goes into another volume, skip it */
+
+		if (part != ept->volno) {
+			continue;
+		}
+
+		/*
+		 * If it's a conflicting file and it's not supposed to be
+		 * installed, note it and skip.
+		 */
+		if (nocnflct && mstat->shared && ept->ftype != 'e') {
+			if (mstat->contchg || mstat->attrchg) {
+				echo(MSG_SHIGN, ept->path);
+			}
+			continue;
+		}
+
+		/*
+		 * If we want to set uid or gid but user says no, note it.
+		 * Remember that the actual mode bits in the structure have
+		 * already been adjusted and the mstat flag is telling us
+		 * about the original mode.
+		 */
+		if (nosetuid && (mstat->setuid || mstat->setgid)) {
+			msg_ugid = 1;	/* don't repeat attribute message. */
+			if (is_fs_writeable(ept->path,
+				&(extlist[i]->fsys_value))) {
+				if (!(mstat->contchg) && mstat->attrchg) {
+					echo(MSG_UGMOD, ept->path);
+				} else {
+					echo(MSG_UGID, ept->path);
+				}
+			}
+		} else {
+			msg_ugid = 0;
+		}
+
+		switch (ept->ftype) {
+			case 'l':	/* hard link */
+				/* links treated as object "update/skip" */
+				stateFlag = B_TRUE;
+				continue; /* defer to final proc */
+
+			case 's': /* for symlink, verify without fix first */
+				/* links treated as object "update/skip" */
+				stateFlag = B_TRUE;
+
+				/* Do this only for default verify */
+				if (cl_dvfy(myclass) == DEFAULT) {
+					if (averify(0, &ept->ftype,
+						ept->path, &ept->ainfo))
+						echo(MSG_SLINK, ept->path);
+				}
+
+				/*FALLTHRU*/
+
+			case 'd':	/* directory */
+			case 'x':	/* exclusive directory */
+			case 'c':	/* character special device */
+			case 'b':	/* block special device */
+			case 'p':	/* named pipe */
+				/* these NOT treated as object "update/skip" */
+				stateFlag = B_FALSE;
+
+				/*
+				 * If we can't get to it for legitimate reasons,
+				 * don't try to verify it.
+				 */
+				if ((z_path_is_inherited(ept->path, ept->ftype,
+				    get_inst_root())) ||
+				    is_remote_fs(ept->path,
+				    &(extlist[i]->fsys_value)) &&
+				    !is_fs_writeable(ept->path,
+				    &(extlist[i]->fsys_value))) {
+					mstat->attrchg = 0;
+					mstat->contchg = 0;
+					break;
+				}
+
+				if (averify(1, &ept->ftype, ept->path,
+							&ept->ainfo) == 0) {
+					mstat->contchg = mstat->attrchg = 0;
+				} else {
+					progerr(ERR_CREATE_PKGOBJ, ept->path);
+					logerr(getErrbufAddr());
+					warnflag++;
+				}
+
+				break;
+
+			case 'i':	/* information file */
+				/* not treated as object "update/skip" */
+				stateFlag = B_FALSE;
+				break;
+
+			default:
+				/* all files treated as object "update/skip" */
+				stateFlag = B_TRUE;
+				break;
+		}
+
+		/*
+		 * Both contchg and shared flags have to be taken into
+		 * account. contchg is set if the file is already present
+		 * in the package database, if it does not exist or if it
+		 * exists and is modified.
+		 * The shared flag is set when 'e' or 'v' file is not
+		 * present in the package database, exists and is not
+		 * modified. It also has to be checked here.
+		 * Shared flag is also set when file is present in package
+		 * database and owned by more than one package, but for
+		 * this case contchg has already been set.
+		 */
+		if (mstat->contchg || (mstat->shared &&
+		    ((ept->ftype == 'e') || (ept->ftype == 'v')))) {
+			*dstp = ept->path;
+			if ((ept->ftype == 'f') || (ept->ftype == 'e') ||
+				(ept->ftype == 'v')) {
+				*srcp = ept->ainfo.local;
+				if (is_partial_inst() != 0) {
+					if (*srcp[0] == '~') {
+						/* translate source pathname */
+						*srcp = srcpath(instdir,
+							extlist[i]->map_path,
+							part, nparts);
+					} else {
+						*srcp = extlist[i]->map_path;
+					}
+				} else {
+					if (*srcp[0] == '~') {
+						/* translate source pathname */
+						*srcp = srcpath(instdir,
+						    &(ept->ainfo.local[1]),
+						    part, nparts);
+					}
+				}
+
+				echoDebug(DBG_DOMERG_NO_SUCH_FILE,
+					ept->ftype, cl_nam(ept->pkg_class_idx),
+					ept->path);
+			} else {
+				/*
+				 * At this point, we're returning a non-file
+				 * that couldn't be created in the standard
+				 * way. If it refers to a filesystem that is
+				 * not writeable by us, don't waste the
+				 * calling process's time.
+				 */
+				if (!is_fs_writeable(ept->path,
+					&(extlist[i]->fsys_value))) {
+					echoDebug(DBG_DOMERG_NOT_WRITABLE,
+						ept->ftype,
+						cl_nam(ept->pkg_class_idx),
+						ept->path);
+					continue;
+				}
+
+				*srcp = NULL;
+				echoDebug(DBG_DOMERG_NOT_THERE,
+					ept->ftype, cl_nam(ept->pkg_class_idx),
+					ept->path);
+			}
+
+			svindx = i+1;
+			backup(*dstp, 1);
+			return (i);
+		}
+
+		if (mstat->attrchg) {
+			backup(ept->path, 0);
+			if (!msg_ugid)
+				echo(MSG_ATTRIB, ept->path);
+
+			/* fix the attributes now for robustness sake */
+			if (averify(1, &ept->ftype,
+				ept->path,
+				&ept->ainfo) == 0) {
+				mstat->attrchg = 0;
+			}
+		}
+
+		/*
+		 * package object exists, or does not need updating: if the path
+		 * is in an area inherited from the global zone, then treat
+		 * the object as if it were "skipped" - if the path is not in an
+		 * area inherited from the global zone, then treat the object as
+		 * if it were "updated"
+		 */
+
+		/* LINTED warning: statement has no consequent: if */
+		if ((stateFlag == B_FALSE) || (ept == (struct cfent *)NULL)) {
+			/*
+			 * the object in question is a directory or special
+			 * file - the fact that this type of object already
+			 * exists or does not need updating must not trigger
+			 * the object updated/object skipped indication -
+			 * that would cause class action scripts to be run
+			 * when installing a new non-global zone - that action
+			 * must only be done when a file object that is in
+			 * an area inherited from the global zone is present.
+			 */
+		} else if (z_path_is_inherited(ept->path, ept->ftype,
+						get_inst_root()) == B_TRUE) {
+			if (r_skipped != (char **)NULL) {
+				if (*r_skipped == (char *)NULL) {
+					echoDebug(DBG_INSTVOL_OBJ_SKIPPED,
+								ept->path);
+					*r_skipped = ept->path;
+				}
+			}
+		} else {
+			if (r_updated != (char **)NULL) {
+				if (*r_updated == (char *)NULL) {
+					echoDebug(DBG_INSTVOL_OBJ_UPDATED,
+								ept->path);
+				}
+				*r_updated = ept->path;
+			}
+		}
+	}
+
+	if (maxvol == part) {
+		eocflag++;	/* endofclass */
+	}
+
+	return (DMRG_DONE);	/* no remaining entries on this volume */
+}
+
+/*
+ * Determine if the provided directory is populated. Return 0 if so and 1 if
+ * not. This also returns 0 if the dirpath is not a directory or if it does
+ * not exist.
+ */
+static int
+dir_is_populated(char *dirpath) {
+	DIR	*dirfp;
+	struct	dirent *drp;
+	int	retcode = 0;
+
+	if ((dirfp = opendir(dirpath)) != NULL) {
+		while ((drp = readdir(dirfp)) != NULL) {
+			if (strcmp(drp->d_name, ".") == 0) {
+				continue;
+			}
+			if (strcmp(drp->d_name, "..") == 0) {
+				continue;
+			}
+			/*
+			 * If we get here, there's a real file in the
+			 * directory
+			 */
+			retcode = 1;
+			break;
+		}
+		(void) closedir(dirfp);
+	}
+
+	return (retcode);
+}
+
+/*
+ * This is the function that cleans up the installation of this class.
+ * This is where hard links get put in since the stuff they're linking
+ * probably exists by now.
+ */
+static void
+endofclass(struct cfextra **extlist, int myclass, int ckflag,
+	VFP_T **a_cfVfp, VFP_T **a_cfTmpVfp)
+{
+	char		*temppath;
+	char 		*pspool_loc;
+	char 		*relocpath = (char *)NULL;
+	char 		scrpt_dst[PATH_MAX];
+	int		flag;
+	int		idx;
+	int		n;
+	struct cfent	*ept;	/* entry from the internal list */
+	struct cfextra	entry;	/* entry from the package database */
+	struct mergstat	*mstat;	/* merge status */
+	struct pinfo	*pinfo;
+
+	/* open the package database (contents) file */
+
+	if (!ocfile(a_cfVfp, a_cfTmpVfp, pkgmap_blks)) {
+		quit(99);
+	}
+
+	echo(MSG_VERIFYING_CLASS, cl_nam(myclass));
+
+	for (idx = 0; /* void */; idx++) {
+		/* find next package object in this class */
+		while (extlist[idx]) {
+			if ((extlist[idx]->cf_ent.ftype != 'i') &&
+				extlist[idx]->cf_ent.pkg_class_idx == myclass) {
+				break;
+			}
+			idx++;
+		}
+
+		if (extlist[idx] == NULL) {
+			/* finish copying contents file and exit loop */
+			(void) srchcfile(&(entry.cf_ent), NULL,
+					*a_cfVfp, *a_cfTmpVfp);
+			break;
+		}
+
+		ept = &(extlist[idx]->cf_ent);
+		mstat = &(extlist[idx]->mstat);
+
+		temppath =
+			extlist[idx] ? extlist[idx]->client_path :
+			NULL;
+
+		/*
+		 * At this point  the only difference between the entry
+		 * in the contents file and the entry in extlist[] is
+		 * that the status indicator contains CONFIRM_CONT.
+		 * So for the new DB we use this knowledge and just
+		 * verify everything in accordance with extlist without
+		 * trying to retrieve the entry from the DB.
+		 */
+
+		n = srchcfile(&(entry.cf_ent),
+			(ept ? temppath : NULL), *a_cfVfp, *a_cfTmpVfp);
+
+		if (n == 0) {
+			break;
+		} else if (n < 0) {
+			char	*errstr = getErrstr();
+			progerr(ERR_CFBAD);
+			logerr(gettext("pathname=%s\n"),
+				entry.cf_ent.path && *entry.cf_ent.path ?
+				entry.cf_ent.path : "Unknown");
+			logerr(gettext("problem=%s\n"),
+				(errstr && *errstr) ? errstr : "Unknown");
+			quit(99);
+		} else if (n != 1) {
+			/*
+			 * Check if path should be in the package
+			 * database.
+			 */
+			if ((mstat->shared && nocnflct)) {
+				continue;
+			}
+			progerr(ERR_CFMISSING, ept->path);
+			quit(99);
+		}
+
+		/*
+		 * If merge was not appropriate for this object, now is the
+		 * time to choose one or the other.
+		 */
+		if (mstat->denied) {
+			/*
+			 * If installation was denied AFTER the package
+			 * database was updated, skip this. We've already
+			 * announced the discrepancy and the verifications
+			 * that follow will make faulty decisions based on
+			 * the ftype, which may not be correct.
+			 */
+			progerr(ERR_COULD_NOT_INSTALL, ept->path);
+			warnflag++;
+		} else {
+			if (mstat->replace)
+				/*
+				 * This replaces the old entry with the new
+				 * one. This should never happen in the new
+				 * DB since the entries are already identical.
+				 */
+				repl_cfent(ept, &(entry.cf_ent));
+
+			/*
+			 * Validate this entry and change the status flag in
+			 * the package database.
+			 */
+			if (ept->ftype == RM_RDY) {
+				(void) eptstat(&(entry.cf_ent), pkginst,
+					STAT_NEXT);
+			} else {
+				/* check the hard link now. */
+				if (ept->ftype == 'l') {
+					if (averify(0, &ept->ftype,
+						ept->path, &ept->ainfo)) {
+						echo(MSG_HRDLINK,
+							ept->path);
+						mstat->attrchg++;
+					}
+				}
+
+				/*
+				 * Don't install or verify objects for
+				 * remote, read-only filesystems.  We need
+				 * only flag them as shared from some server.
+				 * Otherwise, ok to do final check.
+				 */
+				if (is_remote_fs(ept->path,
+					&(extlist[idx]->fsys_value)) &&
+					!is_fs_writeable(ept->path,
+					&(extlist[idx]->fsys_value))) {
+					flag = -1;
+				} else {
+					boolean_t inheritedFlag;
+					inheritedFlag =
+					    z_path_is_inherited(ept->path,
+						ept->ftype, get_inst_root());
+					flag = finalck(ept, mstat->attrchg,
+						(ckflag ? mstat->contchg :
+						(-1)), inheritedFlag);
+				}
+
+				pinfo = entry.cf_ent.pinfo;
+
+				/* Find this package in the list. */
+				while (pinfo) {
+					if (strcmp(pkginst, pinfo->pkg) == 0) {
+						break;
+					}
+					pinfo = pinfo->next;
+				}
+
+				/*
+				 * If this package owns this file, then store
+				 * it in the database with the appropriate
+				 * status. Need to check pinfo in case it
+				 * points to NULL which could happen if
+				 * pinfo->next = NULL above.
+				 */
+				if (pinfo) {
+					if (flag < 0 || is_served(ept->path,
+						&(extlist[idx]->fsys_value))) {
+						/*
+						 * This is provided to
+						 * clients by a server.
+						 */
+						pinfo->status = SERVED_FILE;
+					} else {
+						/*
+						 * It's either there or it's
+						 * not.
+						 */
+						pinfo->status = (flag ?
+							NOT_FND : ENTRY_OK);
+					}
+				}
+			}
+		}
+
+		/*
+		 * If not installing from a partially spooled package, the
+		 * "save/pspool" area, and the file contents can be
+		 * changed (type is 'e' or 'v'), and the class IS "none":
+		 * copy the installed volatile file into the appropriate
+		 * location in the packages destination "save/pspool" area.
+		 */
+
+		if ((!is_partial_inst()) &&
+			((ept->ftype == 'e') || (ept->ftype == 'v')) &&
+			(strcmp(ept->pkg_class, "none") == 0)) {
+
+			if (absolutepath(extlist[idx]->map_path) == B_TRUE &&
+				parametricpath(extlist[idx]->cf_ent.ainfo.local,
+					&relocpath) == B_FALSE) {
+				pspool_loc = ROOT;
+			} else {
+				pspool_loc = RELOC;
+			}
+
+			n = snprintf(scrpt_dst, PATH_MAX, "%s/%s/%s",
+				saveSpoolInstallDir, pspool_loc,
+				relocpath ? relocpath : extlist[idx]->map_path);
+
+			if (n >= PATH_MAX) {
+				progerr(ERR_CREATE_PATH_2,
+					saveSpoolInstallDir,
+					extlist[idx]->map_path);
+				quit(99);
+			}
+
+			/* copy, preserve source file mode */
+
+			if (cppath(MODE_SRC, ept->path, scrpt_dst, 0644)) {
+				warnflag++;
+			}
+		}
+
+		/*
+		 * Now insert this potentially changed package database
+		 * entry.
+		 */
+		if (entry.cf_ent.npkgs) {
+			if (putcvfpfile(&(entry.cf_ent), *a_cfTmpVfp)) {
+				quit(99);
+			}
+		}
+	}
+
+	n = swapcfile(a_cfVfp, a_cfTmpVfp, pkginst, dbchg);
+	if (n == RESULT_WRN) {
+		warnflag++;
+	} else if (n == RESULT_ERR) {
+		quit(99);
+	}
+}
+
+/*
+ * This function goes through and fixes all the attributes. This is called
+ * out by using DST_QKVERIFY=this_class in the pkginfo file. The primary
+ * use for this is to fix up files installed by a class action script
+ * which is time-critical and reliable enough to assume likely success.
+ * The first such format was for WOS compressed-cpio'd file sets.
+ * The second format is the Class Archive Format.
+ */
+static int
+fix_attributes(struct cfextra **extlist, int idx)
+{
+	struct	cfextra *ext;
+	int	i, retval = 1;
+	int 	nc = cl_getn();
+	int	n;
+	struct cfent *ept;
+	struct mergstat *mstat;
+	char scrpt_dst[PATH_MAX];
+	char *pspool_loc;
+	char *relocpath = (char *)NULL;
+
+	for (i = 0; extlist[i]; i++) {
+		ext = extlist[i];
+		ept = &(extlist[i]->cf_ent);
+		mstat = &(extlist[i]->mstat);
+
+		/*
+		 * We don't care about 'i'nfo files because, they
+		 * aren't laid down, 'e'ditable files can change
+		 * anyway, so who cares and 's'ymlinks were already
+		 * fixed in domerg(); however, certain old WOS
+		 * package symlinks depend on a bug in the old
+		 * pkgadd which has recently been expunged. For
+		 * those packages in 2.2, we repeat the verification
+		 * of symlinks.
+		 *
+		 * By 2.6 or so, ftype == 's' should be added to this.
+		 */
+		if (ept->ftype == 'i' || ept->ftype == 'e' ||
+			(mstat->shared && nocnflct))
+			continue;
+
+		if (mstat->denied) {
+			progerr(ERR_COULD_NOT_INSTALL, ept->path);
+			warnflag++;
+			continue;
+		}
+
+		if (ept->pkg_class_idx < 0 || ept->pkg_class_idx > nc) {
+			progerr(ERR_CLIDX, ept->pkg_class_idx,
+			    (ept->path && *ept->path) ? ept->path : "unknown");
+			continue;
+		}
+
+		/* If this is the right class, do the fast verify. */
+		if (ept->pkg_class_idx == idx) {
+			if (fverify(1, &ept->ftype, ept->path,
+				&ept->ainfo, &ept->cinfo) == 0) {
+				mstat->attrchg = 0;
+				mstat->contchg =  0;
+			} else	/* We'll try full verify later */
+				retval = 0;
+		}
+		/*
+		 * Need to copy the installed volitale file back to the
+		 * partial spooled area if we are installing to a local zone
+		 * or similar installation method.
+		 */
+
+		if ((!is_partial_inst()) &&
+			((ept->ftype == 'e') || (ept->ftype == 'v')) &&
+			(strcmp(ept->pkg_class, "none") == 0)) {
+
+			if (absolutepath(ext->map_path) == B_TRUE &&
+				parametricpath(ext->cf_ent.ainfo.local,
+					&relocpath) == B_FALSE) {
+				pspool_loc = ROOT;
+			} else {
+				pspool_loc = RELOC;
+			}
+
+			n = snprintf(scrpt_dst, PATH_MAX, "%s/%s/%s",
+				saveSpoolInstallDir, pspool_loc,
+				relocpath ? relocpath : ext->map_path);
+
+			if (n >= PATH_MAX) {
+				progerr(ERR_CREATE_PATH_2,
+					saveSpoolInstallDir,
+					ext->map_path);
+				quit(99);
+			}
+
+			/* copy, preserve source file mode */
+
+			if (cppath(MODE_SRC, ept->path, scrpt_dst, 0644)) {
+				warnflag++;
+			}
+		}
+	}
+
+	return (retval);
+}
+
+/*
+ * Check to see if first charcter in path is a '/'.
+ *
+ * Return:
+ * 			B_TRUE - if path is prepended with '/'
+ * 			B_FALSE - if not
+ */
+static boolean_t
+absolutepath(char *path)
+{
+	assert(path != NULL);
+	assert(path[0] != '\0');
+
+	return (path[0] == '/' ? B_TRUE : B_FALSE);
+}
+
+/*
+ * Check to see if path contains a '$' which makes it
+ * a parametric path and therefore relocatable.
+ *
+ * Parameters:
+ *             path - The path to determine if it is absolute
+ *             relocpath - The value of the unconditioned path
+ *                         i.e. $OPTDIR/usr/ls
+ * Return:
+ * 			B_TRUE - if path is a parametric path
+ * 			B_FALSE - if not
+ */
+static boolean_t
+parametricpath(char *path, char **relocpath)
+{
+	assert(path != NULL);
+	assert(path[0] != '\0');
+
+	/*
+	 * If this is a valid parametric path then a '$' MUST occur at the
+	 * first or second character.
+	 */
+
+	if (path[0] == '$' || path[1] == '$') {
+		/*
+		 * If a parametric path exists then when copying the
+		 * path to the pspool directoy from the installing
+		 * pkgs reloc directory we want to use the uncononditional
+		 * varaiable path.
+		 */
+		*relocpath = (path + 1);
+		return (B_TRUE);
+	}
+	return (B_FALSE);
+}
+
+void
+regfiles_free()
+{
+	if (regfiles_head != NULL) {
+		struct reg_files *rfp = regfiles_head->next;
+
+		while (rfp != NULL) {
+			free(regfiles_head);
+			regfiles_head = rfp;
+			rfp = regfiles_head->next;
+		}
+		free(regfiles_head);
+		regfiles_head = NULL;
+	}
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/usr/src/cmd/svr4pkg/pkginstall/main.c	Fri Jun 05 10:28:40 2009 -0400
@@ -0,0 +1,2988 @@
+/*
+ * 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
+ * or http://www.opensolaris.org/os/licensing.
+ * 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 2009 Sun Microsystems, Inc.  All rights reserved.
+ * Use is subject to license terms.
+ */
+
+/* Copyright (c) 1984, 1986, 1987, 1988, 1989 AT&T */
+/* All Rights Reserved */
+
+
+#include <stdio.h>
+#include <time.h>
+#include <wait.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <ulimit.h>
+#include <sys/stat.h>
+#include <sys/statvfs.h>
+#include <fcntl.h>
+#include <errno.h>
+#include <ctype.h>
+#include <dirent.h>
+#include <string.h>
+#include <signal.h>
+#include <locale.h>
+#include <libintl.h>
+#include <pkgstrct.h>
+#include <pkginfo.h>
+#include <pkgdev.h>
+#include <pkglocs.h>
+#include <pwd.h>
+#include <assert.h>
+#include <instzones_api.h>
+#include <pkglib.h>
+#include <pkgweb.h>
+#include <install.h>
+#include <libinst.h>
+#include <libadm.h>
+#include <dryrun.h>
+#include <messages.h>
+#include "pkginstall.h"
+
+/* imported globals */
+
+extern char	**environ;
+extern char	*pkgabrv;
+extern char	*pkgname;
+extern char	*pkgarch;
+extern char	*pkgvers;
+extern char	pkgwild[];
+
+/* libadm(3LIB) */
+
+extern char	*get_install_root(void);
+
+/* quit.c */
+
+extern sighdlrFunc_t	*quitGetTrapHandler(void);
+extern void		quitSetDstreamTmpdir(char *a_dstreamTempDir);
+extern void		quitSetInstallStarted(boolean_t a_installStarted);
+extern void		quitSetPkgask(boolean_t a_pkgaskFlag);
+extern void		quitSetSilentExit(boolean_t a_silentExit);
+extern void		quitSetUpdatingExisting(boolean_t a_updatingExisting);
+extern void		quitSetZoneName(char *a_zoneName);
+
+
+/* static globals */
+
+static char	path[PATH_MAX];
+static int	ck_instbase(void);
+static int	cp_pkgdirs(void);
+static int	merg_pkginfos(struct cl_attr **pclass,
+		struct cl_attr ***mpclass);
+static int	merg_respfile(void);
+static int	mv_pkgdirs(void);
+static int	rdonly(char *p);
+static void	ck_w_dryrun(int (*func)(), int type);
+static void	copyright(void), usage(void);
+static void	do_pkgask(boolean_t a_run_request_as_root);
+static void	rm_icas(char *casdir);
+static void	set_dryrun_dir_loc(void);
+static void	unpack(void);
+
+void	ckreturn(int retcode, char *msg);
+
+static char	*ro_params[] = {
+	"PATH", "NAME", "PKG", "PKGINST",
+	"VERSION", "ARCH",
+	"INSTDATE", "CATEGORY",
+	NULL
+};
+
+/*
+ * The following variable is the name of the device to which stdin
+ * is connected during execution of a procedure script. PROC_STDIN is
+ * correct for all ABI compliant packages. For non-ABI-compliant
+ * packages, the '-o' command line switch changes this to PROC_XSTDIN
+ * to allow user interaction during these scripts. -- JST
+ */
+static char	*script_in = PROC_STDIN;	/* assume ABI compliance */
+
+static char	*pkgdrtarg = NULL;
+static char	*pkgcontsrc = NULL;
+static int	non_abi_scripts = 0;
+static char	*respfile = NULL;
+static char	*srcinst = NULL;
+static int	suppressCopyright = 0;
+static int	nointeract = 0;
+
+/* exported globals */
+
+char		*msgtext;
+char		*pkginst = (char *)NULL;
+char		*rw_block_size = NULL;
+char		ilockfile[PATH_MAX];
+char		instdir[PATH_MAX];
+char		saveSpoolInstallDir[PATH_MAX];
+char		pkgbin[PATH_MAX];
+char		pkgloc[PATH_MAX];
+char		pkgloc_sav[PATH_MAX];
+char		pkgsav[PATH_MAX];
+char		rlockfile[PATH_MAX];
+char		savlog[PATH_MAX];
+char		tmpdir[PATH_MAX];
+int		dbchg;
+int		dparts = 0;
+int		dreboot = 0;
+int		failflag = 0;
+static int	askflag = 0;		/* non-zero if invoked as "pkgask" */
+int		ireboot = 0;
+int		maxinst = 1;
+int		nocnflct;
+int		nosetuid;
+int		opresvr4 = 0;
+int		pkgverbose = 0;
+int		rprcflag;
+int		warnflag = 0;
+struct admin	adm;
+struct cfextra	**extlist; /* pkgmap structure and other path info */
+struct pkgdev	pkgdev;
+fsblkcnt_t	pkgmap_blks = 0LL;
+
+/*
+ * this global is referenced by:
+ * getinst - [RW] - incremented if:
+ * - installing same instance again
+ * - overwriting an existing instance
+ * - not installing a new instance
+ * quit - [RO] - if non-zero and started non-zero:
+ * - the new <PKGINST>/install directory and rename <PKGINST>/install.save
+ * - back to <PKGINST>/install
+ * main.c - [RO] - if non-zero:
+ * - alter manner in which parameters are setup for scripts
+ * - set UPDATE=yes in environment
+ */
+static int		update = 0;
+
+/* Set by -O debug: debug output is enabled? */
+
+static boolean_t	debugFlag = B_FALSE;
+
+/* Set by the -G option: install packages in global zone only */
+
+static boolean_t	globalZoneOnly = B_FALSE;
+
+/* Set by -O patchPkgInstall */
+
+static boolean_t patchPkgInstall = B_FALSE;
+
+/* Set by -O patchPkgRemoval */
+
+static boolean_t patchPkgRemoval = B_FALSE;
+
+/* Set by -O preinstallcheck */
+
+static boolean_t	preinstallCheck = B_FALSE;
+
+/* Set by -O parent-zone-name= */
+
+static char		*parentZoneName = (char *)NULL;
+
+/* Set by -O parent-zone-type= */
+
+static char		*parentZoneType = (char *)NULL;
+
+#define	DEFPATH		"/sbin:/usr/sbin:/usr/bin"
+#define	MALSIZ	4	/* best guess at likely maximum value of MAXINST */
+#define	LSIZE	256	/* maximum line size supported in copyright file */
+
+#ifdef	ALLOW_EXCEPTION_PKG_LIST
+#define	SCRIPT	0	/* which exception_pkg() pkg list to use (SCRIPTS) */
+#define	LINK	1	/* which exception_pkg() pkg list to use (SYMLINKS) */
+#endif
+
+#if !defined(TEXT_DOMAIN)	/* Should be defined by cc -D */
+#define	TEXT_DOMAIN "SYS_TEST"
+#endif
+
+/* This is the text for the "-O inherited-filesystem=" option */
+
+#define	INHERITFS	"inherited-filesystem="
+#define	INHERITFS_LEN	((sizeof (INHERITFS))-1)
+
+/* This is the text for the "-O parent-zone-name=" option */
+
+#define	PARENTZONENAME	"parent-zone-name="
+#define	PARENTZONENAME_LEN	((sizeof (PARENTZONENAME))-1)
+
+/* This is the text for the "-O parent-zone-type=" option */
+
+#define	PARENTZONETYPE	"parent-zone-type="
+#define	PARENTZONETYPE_LEN	((sizeof (PARENTZONETYPE))-1)
+
+static char *cpio_names[] = {
+	"root",
+	"root.cpio",
+	"reloc",
+	"reloc.cpio",
+	"root.Z",
+	"root.cpio.Z",
+	"reloc.Z",
+	"reloc.cpio.Z",
+	0
+};
+
+int
+main(int argc, char *argv[])
+{
+	VFP_T			*cfTmpVfp = (VFP_T *)NULL;	/* t.contents */
+	VFP_T			*cfVfp = (VFP_T *)NULL;		/* contents */
+	VFP_T			*pkgmapVfp;	/* "../pkgmap" file */
+	boolean_t		run_request_as_root = B_FALSE;
+	char			**np;
+	char			*abi_comp_ptr;
+	char			*abi_nm_ptr;
+	char			*abi_sym_ptr;
+	char			*admnfile = NULL;
+	char			*device;
+	char			*p;
+	char			*prog_full_name = NULL;
+	char			*pt;
+	char			*skipped = (char *)NULL;
+	char			*updated = (char *)NULL;
+	char			*vfstab_file = NULL;
+	char			*zoneName = (char *)NULL;
+	char			cbuf[MAX_PKG_PARAM_LENGTH];
+	char			cmdbin[PATH_MAX];
+	char			p_pkginfo[PATH_MAX];
+	char			p_pkgmap[PATH_MAX];
+	char			param[MAX_PKG_PARAM_LENGTH];
+	char			script[PATH_MAX];
+	char			altscript[PATH_MAX];
+	int			c;
+	int			disableAttributes = 0;
+	int			err;
+	int			init_install = 0;
+	int			is_comp_arch;
+	int			live_continue = 0;
+	int			map_client = 1;
+	int			n;
+	int			nparts;
+	int			npkgs;
+	int			part;
+	int			saveSpoolInstall = 0;
+	boolean_t		cont_file_read;
+	struct cl_attr		**pclass = NULL;
+	struct cl_attr		**mergd_pclass = NULL;
+	struct pkginfo		*prvinfo;
+	struct sigaction	nact;
+	struct sigaction	oact;
+	struct stat		statb;
+	struct statvfs64	svfsb;
+	time_t			clock;
+
+	/* reset contents of all default paths */
+
+	(void) memset(path, '\0', sizeof (path));
+	(void) memset(cmdbin, '\0', sizeof (cmdbin));
+	(void) memset(script, '\0', sizeof (script));
+	(void) memset(cbuf, '\0', sizeof (cbuf));
+	(void) memset(param, '\0', sizeof (param));
+
+	/* initialize locale environment */
+
+	(void) setlocale(LC_ALL, "");
+	(void) textdomain(TEXT_DOMAIN);
+
+	/* initialize program name */
+
+	prog_full_name = argv[0];
+	(void) set_prog_name(argv[0]);
+
+	/* tell spmi zones interface how to access package output functions */
+
+	z_set_output_functions(echo, echoDebug, progerr);
+
+	/* exit if not root */
+
+	if (getuid()) {
+		progerr(ERR_NOT_ROOT, get_prog_name());
+		exit(1);
+		/* NOTREACHED */
+	}
+
+	/*
+	 * determine how pkgmap() deals with environment variables:
+	 *  - MAPALL - resolve all variables
+	 *  - MAPBUILD - map only build variables
+	 *  - MAPINSTALL - map only install variables
+	 *  - MAPNONE - map no variables
+	 */
+
+	setmapmode(MAPINSTALL);
+
+	/* set sane umask */
+
+	(void) umask(0022);
+
+	/* initially no source "device" */
+
+	device = NULL;
+
+	/* reset npkgs (used as pkg remaining count in quit.c) */
+
+	npkgs = 0;
+
+	/* Read PKG_INSTALL_ROOT from the environment, if it's there. */
+
+	if (!set_inst_root(getenv("PKG_INSTALL_ROOT"))) {
+		progerr(ERR_ROOT_SET);
+		exit(1);
+	}
+
+	/* parse command line options */
+
+	while ((c = getopt(argc, argv,
+		"?Aa:B:b:Cc:D:d:eFf:GhIiMm:N:noO:p:R:r:StV:vyz")) != EOF) {
+
+		switch (c) {
+
+		/*
+		 * Same as pkgadd: This disables attribute checking.
+		 * It speeds up installation a little bit.
+		 */
+		case 'A':
+			disableAttributes++;
+			break;
+
+		/*
+		 * Same as pkgadd: Define an installation administration
+		 * file, admin, to be used in place of the default
+		 * administration file.  The token none overrides the use
+		 * of any admin file, and thus forces interaction with the
+		 * user. Unless a full path name is given, pkgadd first
+		 * looks in the current working directory for the
+		 * administration file.  If the specified administration
+		 * file is not in the current working directory, pkgadd
+		 * looks in the /var/sadm/install/admin directory for the
+		 * administration file.
+		 */
+		case 'a':
+			admnfile = flex_device(optarg, 0);
+			break;
+
+		/*
+		 * Same as pkgadd: control block size given to
+		 * pkginstall - block size used in read()/write() loop;
+		 * default is st_blksize from stat() of source file.
+		 */
+		case 'B':
+			rw_block_size = optarg;
+			break;
+
+		/*
+		 * Same as pkgadd: location where executables needed
+		 * by procedure scripts can be found
+		 * default is /usr/sadm/install/bin.
+		 */
+		case 'b':
+			if (!path_valid(optarg)) {
+				progerr(ERR_PATH, optarg);
+				exit(1);
+			}
+			if (isdir(optarg) != 0) {
+				char *p = strerror(errno);
+				progerr(ERR_CANNOT_USE_DIR, optarg, p);
+				exit(1);
+			}
+			(void) strlcpy(cmdbin, optarg, sizeof (cmdbin));
+			break;
+
+		/*
+		 * Same as pkgadd: This disables checksum tests on
+		 * the source files. It speeds up installation a little bit.
+		 */
+		case 'C':
+			(void) checksum_off();
+			break;
+
+		/*
+		 * Same as pkgadd: This allows designation of a
+		 * continuation file. It is the same format as a dryrun file
+		 * but it is used to take up where the dryrun left off.
+		 */
+		case 'c':
+			pkgcontsrc = optarg;
+			set_continue_mode();
+			set_dr_info(DR_TYPE, INSTALL_TYPE);
+			init_contfile(pkgcontsrc);
+			break;
+
+		/*
+		 * Same as pkgadd: This allows designation of a
+		 * dryrun file. This pkgadd will create dryrun files
+		 * in the directory provided.
+		 */
+		case 'D':
+			pkgdrtarg = optarg;
+			set_dryrun_mode();
+			set_dr_info(DR_TYPE, INSTALL_TYPE);
+			break;
+
+		/*
+		 * Same as pkgadd: Install or copy a package from
+		 * device. device can be a full path name to a directory
+		 * or the identifiers for tape, floppy disk, or removable
+		 * disk - for example, /var/tmp or /floppy/floppy_name.
+		 * It can also be a device alias - for example,
+		 * /floppy/floppy0, or a datastream created by pkgtrans.
+		 */
+		case 'd':
+			device = flex_device(optarg, 1);
+			break;
+
+		/*
+		 * Different from pkgadd: disable the 32 char name
+		 * limit extension
+		 */
+		case 'e':
+			(void) set_ABI_namelngth();
+			break;
+
+		/*
+		 * Different from pkgadd: specify file system type for
+		 * the package device. Must be used with -m.
+		 */
+		case 'f':
+			pkgdev.fstyp = optarg;
+			break;
+
+		/*
+		 * Same as pkgadd: install package in global zone only.
+		 */
+		case 'G':
+			globalZoneOnly = B_TRUE;
+			break;
+
+		/*
+		 * Same as pkgadd: Enable hollow package support. When
+		 * specified, for any package that has SUNW_PKG_HOLLOW=true:
+		 *  Do not calculate and verify package size against target.
+		 *  Do not run any package procedure or class action scripts.
+		 *  Do not create any target directories.
+		 *  Do not perform any script locking.
+		 *  Do not install any components of any package.
+		 *  Do not output any status or database update messages.
+		 */
+		case 'h':
+			set_depend_pkginfo_DB(B_TRUE);
+			break;
+
+		/*
+		 * Same as pkgadd: Informs scripts that this is
+		 * an initial install by setting the environment parameter
+		 * PKG_INIT_INSTALL=TRUE for all scripts. They may use it as
+		 * they see fit, safe in the knowledge that the target
+		 * filesystem is tabula rasa.
+		 */
+		case 'I':
+			init_install++;
+			break;
+
+		/*
+		 * Different from pkgadd: use by pkgask.
+		 */
+		case 'i':
+			askflag++;
+			quitSetPkgask(B_TRUE);
+			break;
+
+		/*
+		 * Same as pkgadd: Instruct pkgadd not to use the
+		 * $root_path/etc/vfstab file for determining the client's
+		 * mount points. This option assumes the mount points are
+		 * correct on the server and it behaves consistently with
+		 * Solaris 2.5 and earlier releases.
+		 */
+		case 'M':
+			map_client = 0;
+			break;
+
+		/*
+		 * Different from pkgadd: specify device to use for package
+		 * source.
+		 */
+		case 'm':
+			pkgdev.mount = optarg;
+			pkgdev.rdonly++;
+			pkgdev.mntflg++;
+			break;
+
+		/*
+		 * Different from pkgadd: specify program name to use
+		 * for messages.
+		 */
+		case 'N':
+			(void) set_prog_name(optarg);
+			break;
+
+		/*
+		 * Same as pkgadd: installation occurs in
+		 * non-interactive mode.  Suppress output of the list of
+		 * installed files. The default mode is interactive.
+		 */
+		case 'n':
+			nointeract++;
+			(void) echoSetFlag(B_FALSE);
+			break;
+
+		/*
+		 * Almost same as pkgadd: the -O option allows the behavior
+		 * of the package tools to be modified. Recognized options:
+		 * -> debug
+		 * ---> enable debugging output
+		 * -> preinstallcheck
+		 * ---> perform a "pre installation" check of the specified
+		 * ---> package - suppress all regular output and cause a
+		 * ---> series of one or more "name=value" pair format lines
+		 * ---> to be output that describes the "installability" of
+		 * ---> the specified package
+		 * -> enable-hollow-package-support
+		 * --> Enable hollow package support. When specified, for any
+		 * --> package that has SUNW_PKG_HOLLOW=true:
+		 * --> Do not calculate and verify package size against target
+		 * --> Do not run any package procedure or class action scripts
+		 * --> Do not create or remove any target directories
+		 * --> Do not perform any script locking
+		 * --> Do not install or uninstall any components of any package
+		 * --> Do not output any status or database update messages
+		 */
+		case 'O':
+			for (p = strtok(optarg, ","); p != (char *)NULL;
+				p = strtok(NULL, ",")) {
+
+				/* process debug option */
+
+				if (strcmp(p, "debug") == 0) {
+					/* set debug flag/enable debug output */
+					if (debugFlag == B_TRUE) {
+						smlSetVerbose(B_TRUE);
+					}
+					debugFlag = B_TRUE;
+					(void) echoDebugSetFlag(debugFlag);
+
+					/* debug info on arguments to pkgadd */
+					for (n = 0; n < argc && argv[n]; n++) {
+						echoDebug(DBG_ARG, n, argv[n]);
+					}
+
+					continue;
+				}
+
+				/* process enable-hollow-package-support opt */
+
+				if (strcmp(p,
+					"enable-hollow-package-support") == 0) {
+					set_depend_pkginfo_DB(B_TRUE);
+					continue;
+				}
+
+				/* process inherited-filesystem= option */
+
+				if (strncmp(p, INHERITFS, INHERITFS_LEN) == 0) {
+					if (z_add_inherited_file_system(
+						p+INHERITFS_LEN) == B_FALSE) {
+						progerr(ERR_NOSUCH_INHERITED,
+							p+INHERITFS_LEN);
+						quit(1);
+						/* NOTREACHED */
+					}
+					continue;
+				}
+
+				/* process preinstallcheck option */
+
+				if (strcmp(p, "preinstallcheck") == 0) {
+					preinstallCheck = B_TRUE;
+					nointeract++;	/* -n */
+					suppressCopyright++;	/* -S */
+					quitSetSilentExit(B_TRUE);
+					continue;
+				}
+
+				/* process addzonename option */
+
+				if (strcmp(p, "addzonename") == 0) {
+					/*
+					 * set zone name to add to messages;
+					 * first look in the current environment
+					 * and use the default package zone name
+					 * if it is set; otherwise, use the name
+					 * of the current zone
+					 */
+					zoneName =
+						getenv(PKG_ZONENAME_VARIABLE);
+
+					if ((zoneName == (char *)NULL) ||
+							(*zoneName == '\0')) {
+						zoneName = z_get_zonename();
+					}
+
+					if (zoneName != (char *)NULL) {
+						if (*zoneName != '\0') {
+							quitSetZoneName(
+								zoneName);
+						} else {
+							zoneName = (char *)NULL;
+						}
+					}
+					continue;
+				}
+
+				/*
+				 * If this is a patch installation
+				 * then call setPatchUpdate().
+				 */
+
+				if (strcmp(p, "patchPkgInstall") == 0) {
+					setPatchUpdate();
+					patchPkgInstall = B_TRUE;
+					continue;
+				}
+
+				/*
+				 * If this is a patch removal
+				 * then call setPatchUpdate() and set
+				 * patchPkgRemoval flag.
+				 */
+
+				if (strcmp(p, "patchPkgRemoval") == 0) {
+					setPatchUpdate();
+					patchPkgRemoval = B_TRUE;
+					continue;
+				}
+
+				/* process parent-zone-name option */
+
+				if (strncmp(p, PARENTZONENAME,
+						PARENTZONENAME_LEN) == 0) {
+					parentZoneName = p+PARENTZONENAME_LEN;
+					continue;
+				}
+
+				/* process parent-zone-type option */
+
+				if (strncmp(p, PARENTZONETYPE,
+						PARENTZONETYPE_LEN) == 0) {
+					parentZoneType = p+PARENTZONETYPE_LEN;
+					continue;
+				}
+
+				/* option not recognized - issue warning */
+
+				progerr(ERR_INVALID_O_OPTION, p);
+				continue;
+
+			}
+			break;
+
+		/*
+		 * Different from pkgadd: This is an old non-ABI package
+		 */
+		case 'o':
+			non_abi_scripts++;
+			break;
+
+		/*
+		 * Different from pkgadd: specify number of parts to package.
+		 */
+		case 'p':
+			dparts = ds_getinfo(optarg);
+			break;
+
+		/*
+		 * Same as pkgadd: Define the full path name of a
+		 * directory to use as the root_path.  All files,
+		 * including package system information files, are
+		 * relocated to a directory tree starting in the specified
+		 * root_path. The root_path may be specified when
+		 * installing to a client from a server (for example,
+		 * /export/root/client1).
+		 */
+		case 'R':
+			if (!set_inst_root(optarg)) {
+				progerr(ERR_ROOT_CMD);
+				exit(1);
+			}
+			break;
+
+		/*
+		 * Same as pkgadd: Identify a file or directory which
+		 * contains output from a previous pkgask(1M)
+		 * session. This file supplies the interaction responses
+		 * that would be requested by the package in interactive
+		 * mode. response must be a full pathname.
+		 */
+		case 'r':
+			respfile = flex_device(optarg, 2);
+			break;
+
+		/*
+		 * Same as pkgadd: suppress copyright notice being
+		 * output during installation.
+		 */
+		case 'S':
+			suppressCopyright++;
+			break;
+
+		/*
+		 * Same as pkgadd: disable save spool area creation;
+		 * do not spool any partial package contents, that is,
+		 * suppress the creation and population of the package save
+		 * spool area (var/sadm/pkg/PKG/save/pspool/PKG).
+		 */
+		case 't':
+			disable_spool_create();
+			break;
+
+		/*
+		 * Same as pkgadd: Specify an alternative fs_file to map
+		 * the client's file systems.  For example, used in
+		 * situations where the $root_path/etc/vfstab file is
+		 * non-existent or unreliable. Informs the pkginstall
+		 * portion to mount up a client filesystem based upon the
+		 * supplied vfstab-like file of stable format.
+		 */
+		case 'V':
+			vfstab_file = flex_device(optarg, 2);
+			map_client = 1;
+			break;
+
+		/*
+		 * Same as pkgadd: Trace all of the scripts that get
+		 * executed by pkgadd, located in the pkginst/install
+		 * directory. This option is used for debugging the
+		 * procedural and non-procedural scripts
+		 */
+		case 'v':
+			pkgverbose++;
+			break;
+
+		/*
+		 * Different from pkgadd: process this package using
+		 * old non-ABI symlinks
+		 */
+		case 'y':
+			set_nonABI_symlinks();
+			break;
+
+		/*
+		 * Same as pkgadd: perform fresh install from
+		 * package save spool area. When set, the package contents
+		 * are installed from the package spool save area instead
+		 * of from the package root area, so that the original
+		 * source packages are not required to install the
+		 * package. If the -h option is also specified and the
+		 * package is hollow, then this option is ignored. When -z
+		 * is specified:
+		 *  - Editable files are installed from the package instance
+		 *    save area.
+		 *  - Volatile files are installed from the package instance
+		 *    save area.
+		 *  - Executable and data files are installed from the final
+		 *    installed location as specified in the pkgmap file.
+		 *  - Installation scripts are run from the package spool
+		 *    save area.
+		 */
+		case 'z':
+			saveSpoolInstall++;
+			break;
+
+		/*
+		 * unrecognized option
+		 */
+		default:
+			usage();
+			/*NOTREACHED*/
+			/*
+			 * Although usage() calls a noreturn function,
+			 * needed to add return (1);  so that main() would
+			 * pass compilation checks. The statement below
+			 * should never be executed.
+			 */
+			return (1);
+		}
+	}
+
+	/*
+	 * ********************************************************************
+	 * validate command line options
+	 * ********************************************************************
+	 */
+
+	/* set "debug echo" flag according to setting of "-O debug" option */
+
+	(void) echoDebugSetFlag(debugFlag);
+	(void) log_set_verbose(debugFlag);
+
+	/* output entry debugging information */
+
+	if (z_running_in_global_zone()) {
+		echoDebug(DBG_ENTRY_IN_GZ, prog_full_name);
+	} else {
+		echoDebug(DBG_ENTRY_IN_LZ, prog_full_name, getzoneid(),
+			z_get_zonename());
+	}
+
+	if (in_continue_mode() && !in_dryrun_mode()) {
+		progerr(ERR_LIVE_CONTINUE_NOT_SUPPORTED);
+		usage();
+		/*NOTREACHED*/
+	}
+
+	/* pkgask requires a response file */
+
+	if (askflag && (respfile == NULL)) {
+		usage();
+		/*NOTREACHED*/
+	}
+
+	/* if device specified, set appropriate device in pkgdev */
+
+	if (device) {
+		if (pkgdev.mount) {
+			pkgdev.bdevice = device;
+		} else {
+			pkgdev.cdevice = device;
+		}
+	}
+
+	/* if file system type specified, must have a device to mount */
+
+	if (pkgdev.fstyp && !pkgdev.mount) {
+		progerr(ERR_F_REQUIRES_M);
+		usage();
+		/*NOTREACHED*/
+	}
+
+	/* BEGIN DATA GATHERING PHASE */
+
+	/*
+	 * Get the mount table info and store internally.
+	 */
+	cont_file_read = B_FALSE;
+	if (in_continue_mode()) {
+		int error;
+		cont_file_read = read_continuation(&error);
+		if (error == -1) {
+			quit(99);
+			/*NOTREACHED*/
+		}
+		if (!in_dryrun_mode()) {
+			live_continue = 1;
+		}
+	}
+	/* Read the mount table if not done in continuation mode */
+	if (!cont_file_read) {
+		if (get_mntinfo(map_client, vfstab_file)) {
+			quit(99);
+			/*NOTREACHED*/
+		}
+	}
+
+	/*
+	 * This function defines the standard /var/... directories used later
+	 * to construct the paths to the various databases.
+	 */
+
+	set_PKGpaths(get_inst_root());
+
+	/*
+	 * If this is being installed on a client whose /var filesystem is
+	 * mounted in some odd way, remap the administrative paths to the
+	 * real filesystem. This could be avoided by simply mounting up the
+	 * client now; but we aren't yet to the point in the process where
+	 * modification of the filesystem is permitted.
+	 */
+	if (is_an_inst_root()) {
+		int fsys_value;
+
+		fsys_value = fsys(get_PKGLOC());
+		if (use_srvr_map_n(fsys_value))
+			set_PKGLOC(server_map(get_PKGLOC(), fsys_value));
+
+		fsys_value = fsys(get_PKGADM());
+		if (use_srvr_map_n(fsys_value))
+			set_PKGADM(server_map(get_PKGADM(), fsys_value));
+	}
+
+	/*
+	 * Initialize pkginfo PKGSAV entry, just in case we dryrun to
+	 * somewhere else.
+	 */
+	set_infoloc(get_PKGLOC());
+
+	/* pull off directory and package name from end of command line */
+
+	switch (argc-optind) {
+	case 0:	/* missing directory and package instance */
+		progerr(ERR_MISSING_DIR_AND_PKG);
+		usage();
+		/*NOTREACHED*/
+	case 1: /* missing package instance */
+		progerr(ERR_MISSING_PKG_INSTANCE);
+		usage();
+		/*NOTREACHED*/
+	case 2:	/* just right! */
+		pkgdev.dirname = argv[optind++];
+		srcinst = argv[optind++];
+		break;
+	default:	/* too many args! */
+		progerr(ERR_TOO_MANY_CMD_ARGS);
+		usage();
+		break;
+	}
+
+	(void) pkgparam(NULL, NULL);  /* close up prior pkg file if needed */
+
+	/*
+	 * Initialize installation admin parameters by reading
+	 * the adminfile.
+	 */
+
+	if (!askflag && !live_continue) {
+		echoDebug(DBG_PKGINSTALL_ADMINFILE, admnfile ? admnfile : "");
+		setadminFile(admnfile);
+	}
+
+	/*
+	 * about to perform first operation that could be modified by the
+	 * preinstall check option - if preinstall check is selected (that is,
+	 * only gathering dependencies), then output a debug message to
+	 * indicate that the check is beginning. Also turn echo() output
+	 * off and set various other flags.
+	 */
+
+	if (preinstallCheck == B_TRUE) {
+		(void) echoSetFlag(B_FALSE);
+		echoDebug(DBG_PKGINSTALL_PREINSCHK,
+			pkginst ? pkginst : (srcinst ? srcinst : ""),
+			zoneName ? zoneName : "global");
+		cksetPreinstallCheck(B_TRUE);
+		cksetZoneName(zoneName);
+		/* inform quit that the install has started */
+		quitSetInstallStarted(B_TRUE);
+	}
+
+	/*
+	 * validate the "rscriptalt" admin file setting
+	 * The rscriptalt admin file parameter may be set to either
+	 * RSCRIPTALT_ROOT or RSCRIPTALT_NOACCESS:
+	 * --> If rscriptalt is not set, or is set to RSCRIPTALT_NOACCESS,
+	 * --> or is set to any value OTHER than RSCRIPTALT_ROOT, then
+	 * --> assume that the parameter is set to RSCRIPTALT_NOACCESS
+	 * If rscriptalt is set to RSCRIPTALT_ROOT, then run request scripts
+	 * as the "root" user if user "install" is not defined.
+	 * Otherwise, assume rscriptalt is set to RSCRIPTALT_NOACCESS, and run
+	 * request scripts as the "alternative" user if user "install" is not
+	 * defined, as appropriate for the current setting of the NONABI_SCRIPTS
+	 * environment variable.
+	 */
+
+	if (ADMSET(RSCRIPTALT)) {
+		p = adm.RSCRIPTALT;
+		echoDebug(DBG_PKGINSTALL_RSCRIPT_SET_TO, RSCRIPTALT_KEYWORD, p);
+		if (strcasecmp(p, RSCRIPTALT_ROOT) == 0) {
+			/* rscriptalt=root */
+			run_request_as_root = B_TRUE;
+		} else if (strcasecmp(p, RSCRIPTALT_NOACCESS) == 0) {
+			/* rscriptalt=noaccess */
+			run_request_as_root = B_FALSE;
+		} else {
+			/* rscriptalt=??? */
+			logerr(WRN_RSCRIPTALT_BAD, RSCRIPTALT_KEYWORD, p,
+				RSCRIPTALT_ROOT, RSCRIPTALT_NOACCESS);
+			logerr(WRN_RSCRIPTALT_USING, RSCRIPTALT_KEYWORD,
+				RSCRIPTALT_NOACCESS);
+			run_request_as_root = B_FALSE;
+		}
+	} else {
+		/* rscriptalt not set - assume rscriptalt=noaccess */
+		echoDebug(DBG_PKGINSTALL_RSCRIPT_NOT_SET, RSCRIPTALT_KEYWORD);
+		run_request_as_root = B_FALSE;
+	}
+
+	echoDebug(DBG_PKGINSTALL_RSCRIPT_IS_ROOT, run_request_as_root);
+
+	/*
+	 * hook SIGINT and SIGHUP interrupts into quit.c's trap handler
+	 */
+
+	/* hold SIGINT/SIGHUP interrupts */
+
+	(void) sighold(SIGHUP);
+	(void) sighold(SIGINT);
+
+	/* connect quit.c:trap() to SIGINT */
+
+	nact.sa_handler = quitGetTrapHandler();
+	nact.sa_flags = SA_RESTART;
+	(void) sigemptyset(&nact.sa_mask);
+
+	(void) sigaction(SIGINT, &nact, &oact);
+
+	/* connect quit.c:trap() to SIGHUP */
+
+	nact.sa_handler = quitGetTrapHandler();
+	nact.sa_flags = SA_RESTART;
+	(void) sigemptyset(&nact.sa_mask);
+
+	(void) sigaction(SIGHUP, &nact, &oact);
+
+	/* release hold on signals */
+
+	(void) sigrelse(SIGHUP);
+	(void) sigrelse(SIGINT);
+
+	/*
+	 * create required /var... directories if they do not exist;
+	 * this function will call quit(99) if any required path cannot
+	 * be created.
+	 */
+
+	ckdirs();
+
+	tzset();
+
+	/*
+	 * create path to temporary directory "installXXXXXX" - if TMPDIR
+	 * environment variable is set, create the directory in $TMPDIR;
+	 * otherwise, create the directory in P_tmpdir.
+	 */
+
+	pt = getenv("TMPDIR");
+	(void) snprintf(tmpdir, sizeof (tmpdir), "%s/installXXXXXX",
+		((pt != (char *)NULL) && (*pt != '\0')) ? pt : P_tmpdir);
+
+	echoDebug(DBG_PKGINSTALL_TMPDIR, tmpdir);
+
+	if ((mktemp(tmpdir) == NULL) || mkdir(tmpdir, 0771)) {
+		progerr(ERR_MKDIR, tmpdir);
+		quit(99);
+		/*NOTREACHED*/
+	}
+
+	/*
+	 * if the package device is a file containing a package stream,
+	 * unpack the stream into a temporary directory
+	 */
+
+	if ((isdir(pkgdev.dirname) != 0) &&
+		(pkgdev.cdevice == (char *)NULL) &&
+		(pkgdev.bdevice == (char *)NULL) &&
+		(isfile((char *)NULL, pkgdev.dirname) == 0)) {
+
+		char		*idsName = (char *)NULL;
+		char		*pkgnames[2];
+		char		*device = pkgdev.dirname;
+		boolean_t	b;
+
+		echoDebug(DBG_PKGINSTALL_DS_ISFILE, pkgdev.dirname);
+
+		/*
+		 * validate the package source device - return pkgdev info that
+		 * describes the package source device.
+		 */
+
+		if (devtype(device, &pkgdev)) {
+			progerr(ERR_BAD_DEVICE, device);
+			quit(99);
+			/* NOTREACHED */
+		}
+
+		/* generate the list of packages to verify */
+
+		pkgnames[0] = srcinst;
+		pkgnames[1] = (char *)NULL;
+
+		b = open_package_datastream(1, pkgnames, (char *)NULL,
+			pkgdev.dirname, (int *)NULL, &idsName, tmpdir, &pkgdev,
+			1);
+
+		if (b == B_FALSE) {
+			progerr(ERR_CANNOT_OPEN_PKG_STREAM,
+				pkgdev.dirname ? pkgdev.dirname : "?");
+			quit(99);
+			/*NOTREACHED*/
+		}
+
+		/* make sure temporary directory is removed on exit */
+
+		quitSetDstreamTmpdir(pkgdev.dirname);
+
+		/* unpack the package instance from the data stream */
+
+		b = unpack_package_from_stream(idsName, srcinst,
+							pkgdev.dirname);
+		if (b == B_FALSE) {
+			progerr(ERR_CANNOT_UNPACK_PKGSTRM,
+				srcinst ? srcinst : "?",
+				idsName ? idsName : "?",
+				pkgdev.dirname ? pkgdev.dirname : "?");
+			quit(99);
+			/*NOTREACHED*/
+		}
+
+		/* close the datastream - no longer needed */
+
+		echoDebug(DBG_CLOSING_STREAM, idsName, pkgdev.dirname);
+		(void) ds_close(1);
+	}
+
+	if (snprintf(instdir, PATH_MAX, "%s/%s", pkgdev.dirname, srcinst)
+	    >= PATH_MAX) {
+		progerr(ERR_SNPRINTF, instdir);
+		quit(99);
+		/*NOTREACHED*/
+	}
+
+	zoneName = getenv(PKG_ZONENAME_VARIABLE);
+
+	/*
+	 * If the environment has a CLIENT_BASEDIR, that takes precedence
+	 * over anything we will construct. We need to save it here because
+	 * in three lines, the current environment goes away.
+	 */
+	(void) set_env_cbdir();	/* copy over environ */
+
+	getuserlocale();
+
+	/*
+	 * current environment has been read; clear environment out
+	 * so putparam() can be used to populate the new environment
+	 * to be passed to any executables/scripts.
+	 */
+
+	environ = NULL;
+
+	/* write parent condition information to environment */
+
+	putConditionInfo(parentZoneName, parentZoneType);
+
+	putuserlocale();
+
+	if (init_install) {
+		putparam("PKG_INIT_INSTALL", "TRUE");
+	}
+
+	if (is_an_inst_root()) {
+		export_client_env(get_inst_root());
+	}
+
+	if (zoneName != (char *)NULL) {
+		putparam(PKG_ZONENAME_VARIABLE, zoneName);
+	}
+
+	putparam("INST_DATADIR", pkgdev.dirname);
+
+	if (non_abi_scripts) {
+		putparam("NONABI_SCRIPTS", "TRUE");
+	}
+
+	if (nonABI_symlinks()) {
+		putparam("PKG_NONABI_SYMLINKS", "TRUE");
+	}
+
+	if (get_ABI_namelngth()) {
+		putparam("PKG_ABI_NAMELENGTH", "TRUE");
+	}
+
+	/* establish path and oambase */
+
+	if (cmdbin[0] == '\0') {
+		(void) strlcpy(cmdbin, PKGBIN, sizeof (cmdbin));
+	}
+
+	(void) snprintf(path, sizeof (path), "%s:%s", DEFPATH, cmdbin);
+
+	putparam("PATH", path);
+
+	putparam("OAMBASE", OAMBASE);
+
+	(void) snprintf(p_pkginfo, sizeof (p_pkginfo),
+			"%s/%s", instdir, PKGINFO);
+	(void) snprintf(p_pkgmap, sizeof (p_pkgmap),
+			"%s/%s", instdir, PKGMAP);
+
+	/* Read the environment (from pkginfo or '-e') ... */
+	abi_nm_ptr = getenv("PKG_ABI_NAMELENGTH");
+
+	/* Disable the 32 char name limit extension */
+	if (abi_nm_ptr && strncasecmp(abi_nm_ptr, "TRUE", 4) == 0) {
+		(void) set_ABI_namelngth();
+	}
+
+	/*
+	 * This tests the pkginfo and pkgmap files for validity and
+	 * puts all delivered pkginfo variables (except for PATH) into
+	 * our environment. This is where a delivered pkginfo BASEDIR
+	 * would come from. See set_basedirs() below.
+	 */
+
+	if (pkgenv(srcinst, p_pkginfo, p_pkgmap)) {
+		quit(1);
+		/*NOTREACHED*/
+	}
+
+	echo("\n%s(%s) %s", pkgname, pkgarch, pkgvers);
+
+	/*
+	 * If this script was invoked by 'pkgask', just
+	 * execute request script and quit (do_pkgask()).
+	 */
+
+	if (askflag) {
+		do_pkgask(run_request_as_root);
+	}
+
+	/* validate package contents file */
+
+	if (vcfile() == 0) {
+		quit(99);
+	}
+
+	/* if not in dryrun mode aquire packaging lock */
+
+	if (!in_dryrun_mode()) {
+		/* acquire the package lock - at install initialization */
+		if (!lockinst(get_prog_name(), srcinst, "install-initial")) {
+			quit(99);
+			/*NOTREACHED*/
+		}
+	}
+
+	/*
+	 * Now do all the various setups based on ABI compliance
+	 */
+
+	/* Read the environment (from pkginfo or '-o') ... */
+	abi_comp_ptr = getenv("NONABI_SCRIPTS");
+
+	/* Read the environment (from pkginfo or '-y') ... */
+	abi_sym_ptr = getenv("PKG_NONABI_SYMLINKS");
+
+	/* bug id 4244631, not ABI compliant */
+	if (abi_comp_ptr && strncasecmp(abi_comp_ptr, "TRUE", 4) == 0) {
+		script_in = PROC_XSTDIN;
+		non_abi_scripts = 1;
+	}
+
+#ifdef	ALLOW_EXCEPTION_PKG_LIST
+	/*
+	 * *********************************************************************
+	 * this feature is removed starting with Solaris 10 - there is no built
+	 * in list of packages that should be run "the old way"
+	 * *********************************************************************
+	 */
+
+	else if (exception_pkg(srcinst, SCRIPT)) {
+		/*
+		 * Until on1095, set it from exception package names as
+		 * well.
+		 */
+		putparam("NONABI_SCRIPTS", "TRUE");
+		script_in = PROC_XSTDIN;
+		non_abi_scripts = 1;
+	}
+#endif
+
+	/* Set symlinks to be processed the old way */
+	if (abi_sym_ptr && strncasecmp(abi_sym_ptr, "TRUE", 4) == 0) {
+		set_nonABI_symlinks();
+	}
+	/*
+	 * *********************************************************************
+	 * this feature is removed starting with Solaris 10 - there is no built
+	 * in list of packages that should be run "the old way"
+	 * *********************************************************************
+	 */
+
+#ifdef	ALLOW_EXCEPTION_PKG_LIST
+	else if (exception_pkg(srcinst, LINK)) {
+		/* Until 2.9, set it from the execption list */
+		putparam("PKG_NONABI_SYMLINKS", "TRUE");
+		set_nonABI_symlinks();
+	}
+#endif
+	/*
+	 * At this point, script_in, non_abi_scripts & the environment are
+	 * all set correctly for the ABI status of the package.
+	 */
+
+	if (pt = getenv("MAXINST")) {
+		maxinst = atol(pt);
+	}
+
+	/*
+	 * See if were are installing a package that only wants to update
+	 * the database or only install files associated with CAS's. We
+	 * only check the PKG_HOLLOW_VARIABLE variable if told to do so by
+	 * the caller.
+	 */
+
+	if (is_depend_pkginfo_DB()) {
+		pt = getenv(PKG_HOLLOW_VARIABLE);
+		if ((pt != NULL) && (strncasecmp(pt, "true", 4) == 0)) {
+			echoDebug(DBG_PKGREMOVE_HOLLOW_ENABLED);
+			if (disableAttributes) {
+				disable_attribute_check();
+			}
+
+			/*
+			 * this is a hollow package and hollow package support
+			 * is enabled -- override admin settings to suppress
+			 * checks that do not make sense since no scripts will
+			 * be executed and no files will be installed.
+			 */
+
+			setadminSetting("conflict", "nocheck");
+			setadminSetting("setuid", "nocheck");
+			setadminSetting("action", "nocheck");
+			setadminSetting("partial", "nocheck");
+			setadminSetting("space", "nocheck");
+			setadminSetting("authentication", "nocheck");
+		} else {
+			echoDebug(DBG_PKGREMOVE_HOLLOW_DISABLED);
+			set_depend_pkginfo_DB(B_FALSE);
+		}
+	}
+
+	/*
+	 * if performing a fresh install to a non-global zone, and doing
+	 * more than just updating the package database (that is, the
+	 * package to install is NOT "hollow"), then set the global flag
+	 * that directs installation is from partially spooled packages
+	 * (that is, packages installed in the global zone).
+	 */
+
+	if (saveSpoolInstall && (!is_depend_pkginfo_DB())) {
+		set_partial_inst();
+	} else {
+		saveSpoolInstall = 0;
+	}
+
+	/*
+	 * verify that we are not trying to install an
+	 * INTONLY package with no interaction
+	 */
+
+	if (pt = getenv("INTONLY")) {
+		if (askflag || nointeract) {
+			progerr(ERR_INTONLY, pkgabrv ? pkgabrv : "?");
+			quit(1);
+			/*NOTREACHED*/
+		}
+	}
+
+	if (!suppressCopyright && !pkgdev.cdevice) {
+		copyright();
+	}
+
+	/*
+	 * inspect the system to determine if any instances of the
+	 * package being installed already exist on the system
+	 */
+
+	prvinfo = (struct pkginfo *)calloc(MALSIZ, sizeof (struct pkginfo));
+	if (prvinfo == NULL) {
+		progerr(ERR_MEMORY, errno);
+		quit(99);
+		/*NOTREACHED*/
+	}
+
+	for (;;) {
+		if (pkginfo(&prvinfo[npkgs], pkgwild, NULL, NULL)) {
+			if ((errno == ESRCH) || (errno == ENOENT)) {
+				break;
+			}
+			progerr(ERR_SYSINFO, errno);
+			quit(99);
+			/*NOTREACHED*/
+		}
+		if ((++npkgs % MALSIZ) == 0) {
+			prvinfo = (struct pkginfo *)realloc(prvinfo,
+				(npkgs+MALSIZ) * sizeof (struct pkginfo));
+			if (prvinfo == NULL) {
+				progerr(ERR_MEMORY, errno);
+				quit(99);
+				/*NOTREACHED*/
+			}
+		}
+	}
+
+	/*
+	 * Determine the correct package instance based on how many packages are
+	 * already installed. If there are none (npkgs == 0), getinst() just
+	 * returns the package abbreviation. Otherwise, getinst() interacts with
+	 * the user (or reads the admin file) to determine if an instance which
+	 * is already installed should be overwritten, or possibly install a new
+	 * instance of this package
+	 */
+
+	pkginst = getinst(&update, prvinfo, npkgs, preinstallCheck);
+
+	/* set "update flag" if updating an existing instance of this package */
+
+	if (update) {
+		setUpdate();
+	}
+
+	/*
+	 * Need to force UPDATE to be NULL in case a patch has been applied
+	 * before creating a zone. Some pkgs (SUNWcsr) already spooled
+	 * to the zone, check the value of UPDATE in their postinstall script.
+	 * After a pkg has been patched UPDATE exists statically in the
+	 * pkginfo file and this value must be reset when installing a zone.
+	 */
+
+	if (saveSpoolInstall != 0 && !isPatchUpdate() && !isUpdate()) {
+		putparam("UPDATE", "");
+	}
+
+	/* inform quit() if updating existing or installing new instance */
+
+	quitSetUpdatingExisting(update ? B_TRUE : B_FALSE);
+
+	if (respfile) {
+		(void) set_respfile(respfile, pkginst, RESP_RO);
+	}
+
+	(void) snprintf(pkgloc, sizeof (pkgloc),
+			"%s/%s", get_PKGLOC(), pkginst);
+
+	(void) snprintf(pkgbin, sizeof (pkgbin),
+			"%s/install", pkgloc);
+
+	(void) snprintf(pkgsav, sizeof (pkgsav),
+			"%s/save", pkgloc);
+
+	if (snprintf(saveSpoolInstallDir, PATH_MAX, "%s/pspool/%s", pkgsav,
+			pkginst) < 0) {
+		progerr(ERR_SNPRINTF, saveSpoolInstallDir);
+		quit(99);
+		/*NOTREACHED*/
+	}
+
+	(void) snprintf(ilockfile, sizeof (ilockfile),
+			"%s/!I-Lock!", pkgloc);
+	(void) snprintf(rlockfile, sizeof (rlockfile),
+			"%s/!R-Lock!", pkgloc);
+	(void) snprintf(savlog, sizeof (savlog),
+			"%s/logs/%s", get_PKGADM(), pkginst);
+
+	putparam("PKGINST", pkginst);
+	putparam("PKGSAV", pkgsav);
+
+	/*
+	 * Be sure request script has access to PKG_INSTALL_ROOT if there is
+	 * one
+	 */
+
+	put_path_params();
+
+	if (!map_client) {
+		putparam("PKG_NO_UNIFIED", "TRUE");
+	}
+
+	/*
+	 * This maps the client filesystems into the server's space.
+	 */
+
+	if (map_client && !mount_client()) {
+		logerr(MSG_MANMOUNT);
+	}
+
+	/*
+	 * If this is an UPDATE then either this is exactly the same version
+	 * and architecture of an installed package or a different package is
+	 * intended to entirely replace an installed package of the same name
+	 * with a different VERSION or ARCH string.
+	 * Don't merge any databases if only gathering dependencies.
+	 */
+
+	if ((preinstallCheck == B_FALSE) && (update)) {
+		/*
+		 * If this version and architecture is already installed,
+		 * merge the installed and installing parameters and inform
+		 * all procedure scripts by defining UPDATE in the
+		 * environment.
+		 */
+
+		if (is_samepkg()) {
+			/*
+			 * If it's the same ARCH and VERSION, then a merge
+			 * and copy operation is necessary.
+			 */
+
+			if (n = merg_pkginfos(pclass, &mergd_pclass)) {
+				quit(n);
+				/*NOTREACHED*/
+			}
+
+			if (n = cp_pkgdirs()) {
+				quit(n);
+				/*NOTREACHED*/
+			}
+
+		} else {
+			/*
+			 * If it's a different ARCH and/or VERSION then this
+			 * is an "instance=overwrite" situation. The
+			 * installed base needs to be confirmed and the
+			 * package directories renamed.
+			 */
+
+			if (n = ck_instbase()) {
+				quit(n);
+				/*NOTREACHED*/
+			}
+
+			if (n = mv_pkgdirs()) {
+				quit(n);
+				/*NOTREACHED*/
+			}
+		}
+
+		putparam("UPDATE", "yes");
+
+	}
+
+	if (in_dryrun_mode()) {
+		set_dryrun_dir_loc();
+	}
+
+	if (preinstallCheck == B_FALSE) {
+		/*
+		 * Determine if the package has been partially installed on or
+		 * removed from this system.
+		 */
+		ck_w_dryrun(ckpartial, PARTIAL);
+
+		/*
+		 * make sure current runlevel is appropriate
+		 */
+		ck_w_dryrun(ckrunlevel, RUNLEVEL);
+	} else {
+		int	r;
+
+		/*
+		 * Just gathering dependencies - determine if the package has
+		 * been partially installed on or removed from this system and
+		 * output information to stdout
+		 */
+		r = ckpartial();
+		(void) fprintf(stdout, "ckpartialinstall=%d\n", r == 8 ? 1 : 0);
+		(void) fprintf(stdout, "ckpartialremove=%d\n", r == 9 ? 1 : 0);
+
+		/*
+		 * make sure current runlevel is appropriate
+		 */
+		r = ckrunlevel();
+		(void) fprintf(stdout, "ckrunlevel=%d\n", r);
+	}
+
+	if (pkgdev.cdevice) {
+		/* get first volume which contains info files */
+		unpack();
+		if (!suppressCopyright) {
+			copyright();
+		}
+	}
+
+	/* update the lock - at the request script */
+
+	lockupd("request");
+
+	/*
+	 * If no response file has been provided, initialize response file by
+	 * executing any request script provided by this package. Initialize
+	 * the response file if not gathering dependencies only.
+	 */
+
+	if ((!rdonly_respfile()) && (preinstallCheck == B_FALSE)) {
+		(void) snprintf(path, sizeof (path),
+			"%s/%s", instdir, REQUEST_FILE);
+		n = reqexec(update, path, non_abi_scripts,
+			run_request_as_root);
+		if (in_dryrun_mode()) {
+			set_dr_info(REQUESTEXITCODE, n);
+		}
+
+		ckreturn(n, ERR_REQUEST);
+	}
+
+	/*
+	 * Look for all parameters in response file which begin with a
+	 * capital letter, and place them in the environment.
+	 */
+
+	if ((is_a_respfile()) && (preinstallCheck == B_FALSE)) {
+		if (n = merg_respfile()) {
+			quit(n);
+			/*NOTREACHED*/
+		}
+	}
+
+	/*
+	 * Run a checkinstall script if one is provided by the package.
+	 * Don't execute checkinstall script if we are only updating the DB.
+	 * Don't execute checkinstall script if only gathering dependencies.
+	 */
+
+	/* update the lock - at the checkinstall script */
+	lockupd("checkinstall");
+
+	/* Execute checkinstall script if one is provided. */
+	(void) snprintf(script, sizeof (script), "%s/install/checkinstall",
+			instdir);
+	if (access(script, F_OK) != 0) {
+		/* no script present */
+		echoDebug(DBG_PKGINSTALL_COC_NONE, pkginst, script,
+			zoneName ? zoneName : "global");
+	} else if (is_depend_pkginfo_DB()) {
+		/* updating db only: skip checkinstall script */
+		echoDebug(DBG_PKGINSTALL_COC_DBUPD, pkginst, script,
+			zoneName ? zoneName : "global");
+	} else if (preinstallCheck == B_TRUE) {
+		/* only gathering dependencies: skip checkinstall script */
+		echoDebug(DBG_PKGINSTALL_COC_NODEL, pkginst, script,
+			zoneName ? zoneName : "global");
+	} else {
+		/* script present and ok to run: run the script */
+		if (zoneName == (char *)NULL) {
+			echo(MSG_PKGINSTALL_EXECOC_GZ);
+			echoDebug(DBG_PKGINSTALL_EXECOC_GZ, pkginst, script);
+		} else {
+			echo(MSG_PKGINSTALL_EXECOC_LZ, zoneName);
+			echoDebug(DBG_PKGINSTALL_EXECOC_LZ, pkginst, script,
+				zoneName);
+		}
+		n = chkexec(update, script);
+		if (in_dryrun_mode()) {
+			set_dr_info(CHECKEXITCODE, n);
+		}
+
+		if (n == 3) {
+			echo(WRN_CHKINSTALL);
+			ckreturn(4, NULL);
+		} else if (n == 7) {
+			/* access returned error */
+			progerr(ERR_CHKINSTALL_NOSCRIPT, script);
+			ckreturn(4, ERR_CHKINSTALL);
+		} else {
+			ckreturn(n, ERR_CHKINSTALL);
+		}
+	}
+
+	/*
+	 * Now that the internal data structures are initialized, we can
+	 * initialize the dryrun files (which may be the same files).
+	 */
+
+	if (pkgdrtarg) {
+		init_dryrunfile(pkgdrtarg);
+	}
+
+	/*
+	 * Look for all parameters in response file which begin with a
+	 * capital letter, and place them in the environment.
+	 */
+	if (is_a_respfile()) {
+		if (n = merg_respfile()) {
+			quit(n);
+			/*NOTREACHED*/
+		}
+	}
+
+	/* update the lock - doing analysis */
+
+	lockupd("analysis");
+
+	/*
+	 * Determine package base directory and client base directory
+	 * if appropriate. Then encapsulate them for future retrieval.
+	 */
+	if ((err = set_basedirs(isreloc(instdir), adm.basedir, pkginst,
+		nointeract)) != 0) {
+		quit(err);
+		/*NOTREACHED*/
+	}
+
+	/*
+	 * Create the base directory if specified.
+	 * Don't create if we are only updating the DB.
+	 * Don't create if only gathering dependencies.
+	 */
+
+	if (!is_depend_pkginfo_DB() &&
+		!preinstallCheck && is_a_basedir()) {
+		mkbasedir(!nointeract, get_basedir());
+		echo(MSG_BASE_USED, get_basedir());
+	}
+
+	/*
+	 * Store PKG_INSTALL_ROOT, BASEDIR & CLIENT_BASEDIR in our
+	 * environment for later use by procedure scripts.
+	 */
+	put_path_params();
+
+	/*
+	 * the following two checks are done in the corresponding
+	 * ck() routine, but are repeated here to avoid re-processing
+	 * the database if we are administered to not include these
+	 * processes
+	 */
+	if (ADM(setuid, "nochange")) {
+		nosetuid++;	/* Clear setuid/gid bits. */
+	}
+
+	if (ADM(conflict, "nochange")) {
+		nocnflct++;	/* Don't install conflicting files. */
+	}
+
+	/*
+	 * Get the filesystem space information for the filesystem on which
+	 * the "contents" file resides.
+	 */
+
+	svfsb.f_bsize = 8192;
+	svfsb.f_frsize = 1024;
+
+	if (statvfs64(get_PKGADM(), &svfsb) == -1) {
+		int	lerrno = errno;
+		if (!access(get_PKGADM(), F_OK)) {
+			progerr(ERR_PKGINSTALL_STATVFS, get_PKGADM(),
+				strerror(errno));
+			logerr("(errno %d)", lerrno);
+			quit(99);
+			/*NOTREACHED*/
+		}
+	}
+
+	/*
+	 * Get the number of blocks used by the pkgmap, ocfile()
+	 * needs this to properly determine its space requirements.
+	 */
+
+	if (stat(p_pkgmap, &statb) == -1) {
+		progerr(ERR_PKGINSTALL_STATOF, p_pkgmap, strerror(errno));
+		quit(99);
+		/*NOTREACHED*/
+	}
+
+	pkgmap_blks = nblk(statb.st_size, svfsb.f_bsize, svfsb.f_frsize);
+
+	/*
+	 * Merge information in memory with the "contents" file; this creates
+	 * a temporary version of the "contents" file. Note that in dryrun
+	 * mode, we still need to record the contents file data somewhere,
+	 * but we do it in the dryrun directory.
+	 */
+
+	if (in_dryrun_mode()) {
+		if (n = set_cfdir(pkgdrtarg)) {
+			quit(n);
+			/*NOTREACHED*/
+		}
+	} else {
+		if (n = set_cfdir(NULL)) {
+			quit(n);
+			/*NOTREACHED*/
+		}
+	}
+	if (!ocfile(&cfVfp, &cfTmpVfp, pkgmap_blks)) {
+		quit(99);
+		/*NOTREACHED*/
+	}
+
+	/*
+	 * if cpio is being used,  tell pkgdbmerg since attributes will
+	 * have to be check and repaired on all file and directories
+	 */
+	for (np = cpio_names; *np != NULL; np++) {
+		(void) snprintf(path, sizeof (path),
+			"%s/%s", instdir, *np);
+		if (iscpio(path, &is_comp_arch)) {
+			is_WOS_arch();
+			break;
+		}
+	}
+
+	/* Establish the class list and the class attributes. */
+	cl_sets(getenv("CLASSES"));
+	find_CAS(I_ONLY, pkgbin, instdir);
+
+	if (vfpOpen(&pkgmapVfp, p_pkgmap, "r", VFP_NEEDNOW) != 0) {
+		progerr(ERR_PKGMAP, p_pkgmap);
+		quit(99);
+		/*NOTREACHED*/
+	}
+
+	/*
+	 * This modifies the path list entries in memory to reflect
+	 * how they should look after the merg is complete
+	 */
+
+	nparts = sortmap(&extlist, pkgmapVfp, cfVfp, cfTmpVfp, zoneName);
+
+	if ((n = files_installed()) > 0) {
+		if (n > 1) {
+			echo(MSG_INST_MANY, n);
+		} else {
+			echo(MSG_INST_ONE, n);
+		}
+	}
+
+	/*
+	 * Check ulimit requirement (provided in pkginfo). The purpose of
+	 * this limit is to terminate pathological file growth resulting from
+	 * file edits in scripts. It does not apply to files in the pkgmap
+	 * and it does not apply to any database files manipulated by the
+	 * installation service.
+	 */
+	if (pt = getenv("ULIMIT")) {
+		if (assign_ulimit(pt) == -1) {
+			progerr(ERR_BADULIMIT, pt);
+			quit(99);
+			/*NOTREACHED*/
+		}
+		putparam("PKG_ULIMIT", "TRUE");
+	}
+
+	/*
+	 * If only gathering dependencies, check and output status of all
+	 * remaining dependencies and exit.
+	 */
+
+	if (preinstallCheck == B_TRUE) {
+		/* update the lock file - final checking */
+
+		lockupd("preinstallcheck");
+
+		/* verify package information files are not corrupt */
+
+		(void) fprintf(stdout, "ckpkgfiles=%d\n", ckpkgfiles());
+
+		/* verify package dependencies */
+
+		(void) fprintf(stdout, "ckdepend=%d\n", ckdepend());
+
+		/* Check space requirements */
+
+		(void) fprintf(stdout, "ckspace=%d\n", ckspace());
+
+		/*
+		 * Determine if any objects provided by this package conflict
+		 * with the files of previously installed packages.
+		 */
+
+		(void) fprintf(stdout, "ckconflict=%d\n", ckconflct());
+
+		/*
+		 * Determine if any objects provided by this package will be
+		 * installed with setuid or setgid enabled.
+		 */
+
+		(void) fprintf(stdout, "cksetuid=%d\n", cksetuid());
+
+		/*
+		 * Determine if any packaging scripts provided with this package
+		 * will execute as a priviledged user.
+		 */
+
+		(void) fprintf(stdout, "ckpriv=%d\n", ckpriv());
+
+		/* Verify neccessary package installation directories exist */
+
+		(void) fprintf(stdout, "ckpkgdirs=%d\n", ckpkgdirs());
+
+		/*
+		 * ****** preinstall check done - exit ******
+		 */
+
+		echoDebug(DBG_PKGINSTALL_PREINSCHK_OK);
+		quit(0);
+		/*NOTREACHED*/
+	}
+
+	/*
+	 * Not gathering dependencies only, proceed to check dependencies
+	 * and continue with the package installation operation.
+	 */
+
+	/*
+	 * verify package information files are not corrupt
+	 */
+	ck_w_dryrun(ckpkgfiles, PKGFILES);
+
+	/*
+	 * verify package dependencies
+	 */
+	ck_w_dryrun(ckdepend, DEPEND);
+
+	/*
+	 * Check space requirements.
+	 */
+	ck_w_dryrun(ckspace, SPACE);
+
+	/*
+	 * Determine if any objects provided by this package conflict with
+	 * the files of previously installed packages.
+	 */
+	ck_w_dryrun(ckconflct, CONFLICT);
+
+	/*
+	 * Determine if any objects provided by this package will be
+	 * installed with setuid or setgid enabled.
+	 */
+	ck_w_dryrun(cksetuid, SETUID);
+
+	/*
+	 * Determine if any packaging scripts provided with this package will
+	 * execute as a priviledged user.
+	 */
+	ck_w_dryrun(ckpriv, PRIV);
+
+	/*
+	 * Verify neccessary package installation directories exist.
+	 */
+	ck_w_dryrun(ckpkgdirs, PKGDIRS);
+
+	/*
+	 * If we have assumed that we were installing setuid or conflicting
+	 * files, and the user chose to do otherwise, we need to read in the
+	 * package map again and re-merg with the "contents" file
+	 */
+
+	if (rprcflag) {
+		nparts = sortmap(&extlist, pkgmapVfp, cfVfp,
+				cfTmpVfp, zoneName);
+	}
+
+	(void) vfpClose(&pkgmapVfp);
+
+	/* BEGIN INSTALLATION PHASE */
+	if (in_dryrun_mode()) {
+		echo(MSG_PKGINSTALL_DRYRUN, pkgname, pkginst);
+	} else if (zoneName == (char *)NULL) {
+		echo(MSG_PKGINSTALL_INSIN_GZ, pkgname, pkginst);
+	} else {
+		echo(MSG_PKGINSTALL_INSIN_LZ, pkgname, pkginst, zoneName);
+	}
+
+	/* inform quit that the install has started */
+
+	quitSetInstallStarted(B_TRUE);
+
+	/*
+	 * This replaces the contents file with recently created temp version
+	 * which contains information about the objects being installed.
+	 * Under old lock protocol it closes both files and releases the
+	 * locks. Beginning in Solaris 2.7, this lock method should be
+	 * reviewed.
+	 */
+
+	n = swapcfile(&cfVfp, &cfTmpVfp, pkginst, dbchg);
+	if (n == RESULT_WRN) {
+		warnflag++;
+	} else if (n == RESULT_ERR) {
+		quit(99);
+		/*NOTREACHED*/
+	}
+
+	/*
+	 * Create install-specific lockfile to indicate start of
+	 * installation. This is really just an information file. If the
+	 * process dies, the initial lockfile (from lockinst(), is
+	 * relinquished by the kernel, but this one remains in support of the
+	 * post-mortem.
+	 */
+
+	if (access(ilockfile, F_OK) == 0) {
+		(void) remove(ilockfile);
+	}
+
+	if (open(ilockfile, O_WRONLY|O_CREAT|O_TRUNC|O_EXCL, 0644) < 0) {
+		progerr(ERR_LOCKFILE, ilockfile);
+		quit(99);
+		/*NOTREACHED*/
+	}
+
+	(void) time(&clock);
+	/* LINTED warning: do not use cftime(); ... */
+	(void) cftime(cbuf, "%b %d \045Y \045H:\045M", &clock);
+	putparam("INSTDATE", qstrdup(cbuf));
+
+	/*
+	 * Store information about package being installed;
+	 * modify installation parameters as neccessary and
+	 * copy contents of 'install' directory into $pkgloc
+	 */
+	merginfo(mergd_pclass, saveSpoolInstall);
+
+	/* If this was just a dryrun, then quit() will write out that file. */
+	if (in_dryrun_mode()) {
+		quit(0);
+		/*NOTREACHED*/
+	}
+
+	if (opresvr4) {
+		/*
+		 * we are overwriting a pre-svr4 package, so remove the file
+		 * in /usr/options now
+		 */
+		(void) snprintf(path, sizeof (path),
+			"%s/%s.name", get_PKGOLD(), pkginst);
+		if (remove(path) && (errno != ENOENT)) {
+			progerr(ERR_OPRESVR4, path);
+			warnflag++;
+		}
+	}
+
+	/*
+	 * Execute preinstall script, if one was provided with the
+	 * package. We check the package to avoid running an old
+	 * preinstall script if one was provided with a prior instance.
+	 * Don't execute preinstall script if we are only updating the DB.
+	 */
+
+	/* update the lock - at the preinstall altscript */
+	lockupd("preinstall");
+
+	/* preinstall script in the media (package source) */
+	(void) snprintf(altscript, sizeof (altscript), "%s/install/preinstall",
+			instdir);
+
+	/* preinstall script in the pkgbin instead of media */
+	(void) snprintf(script, sizeof (script), "%s/preinstall", pkgbin);
+
+	if (access(altscript, F_OK) != 0) {
+		/* no script present */
+		echoDebug(DBG_PKGINSTALL_POCALT_NONE, pkginst, altscript,
+			zoneName ? zoneName : "global");
+	} else if (access(script, F_OK) != 0) {
+		/* no script present */
+		echoDebug(DBG_PKGINSTALL_POC_NONE, pkginst, script,
+			zoneName ? zoneName : "global");
+	} else if (is_depend_pkginfo_DB()) {
+		/* updating db only: skip preinstall script */
+		echoDebug(DBG_PKGINSTALL_POC_DBUPD, pkginst, script,
+			zoneName ? zoneName : "global");
+	} else {
+		/* script present and ok to run: run the script */
+		assert(preinstallCheck == B_FALSE);
+
+		set_ulimit("preinstall", ERR_PREINSTALL);
+		if (zoneName == (char *)NULL) {
+			echo(MSG_PKGINSTALL_EXEPOC_GZ);
+			echoDebug(DBG_PKGINSTALL_EXEPOC_GZ, pkginst, script);
+		} else {
+			echo(MSG_PKGINSTALL_EXEPOC_LZ, zoneName);
+			echoDebug(DBG_PKGINSTALL_EXEPOC_LZ, pkginst, script,
+				zoneName);
+		}
+		putparam("PKG_PROC_script", "preinstall");
+		if (pkgverbose) {
+			ckreturn(pkgexecl(script_in, PROC_STDOUT,
+				PROC_USER, PROC_GRP, SHELL, "-x",
+				script, NULL), ERR_PREINSTALL);
+		} else {
+			ckreturn(pkgexecl(script_in, PROC_STDOUT,
+				PROC_USER, PROC_GRP, SHELL, script,
+				NULL), ERR_PREINSTALL);
+		}
+
+		clr_ulimit();
+		(void) remove(script);	/* no longer needed. */
+	}
+
+	/*
+	 * Check delivered package for a postinstall script while
+	 * we're still on volume 1.
+	 */
+
+	(void) snprintf(script, sizeof (script),
+			"%s/install/postinstall", instdir);
+	if (access(script, F_OK) == 0) {
+		(void) snprintf(script, sizeof (script),
+					"%s/postinstall", pkgbin);
+	} else {
+		script[0] = '\0';
+	}
+
+	/* update the lock - at the install phase */
+
+	lockupd("install");
+
+	/*
+	 * install package one part (volume) at a time
+	 */
+
+	part = 1;
+	while (part <= nparts) {
+		if ((part > 1) && pkgdev.cdevice) {
+			unpack();
+		}
+
+		instvol(extlist, srcinst, part, nparts,
+			&cfVfp, &cfTmpVfp, &updated,
+			&skipped, zoneName);
+
+		if (part++ >= nparts) {
+			break;
+		}
+	}
+
+	z_destroyMountTable();
+
+	/*
+	 * Now that all install class action scripts have been used, we
+	 * delete them from the package directory.
+	 */
+	rm_icas(pkgbin);
+
+	if ((globalZoneOnly) && (!patchPkgInstall) && (!patchPkgRemoval)) {
+		boolean_t   b;
+		b = pkgAddPackageToGzonlyList(pkginst, get_inst_root());
+		if (b == B_FALSE) {
+			progerr(ERR_PKGINSTALL_GZONLY_ADD, pkginst);
+			ckreturn(1, NULL);
+		}
+	}
+
+	/*
+	 * Execute postinstall script, if any
+	 * Don't execute postinstall script if we are only updating the DB.
+	 */
+
+	echoDebug(DBG_PKGINSTALL_INSDONE, is_depend_pkginfo_DB(),
+		is_depend_pkginfo_DB(), saveSpoolInstall,
+		updated ? updated : "",
+		skipped ? skipped : "",
+		script ? script : "",
+		script ? access(script, F_OK) : -1);
+
+	/* update the lock - at the postinstall script */
+	lockupd("postinstall");
+
+	if ((script == (char *)NULL) || (*script == '\0')) {
+		echoDebug(DBG_PKGINSTALL_POIS_NOPATH, pkginst,
+			zoneName ? zoneName : "global");
+	} else if (access(script, F_OK) != 0) {
+		echoDebug(DBG_PKGINSTALL_POIS_NONE, pkginst, script,
+			zoneName ? zoneName : "global");
+	} else if (is_depend_pkginfo_DB()) {
+		echoDebug(DBG_PKGINSTALL_POIS_DBUPD, pkginst, script,
+			zoneName ? zoneName : "global");
+	} else if ((saveSpoolInstall != 0) && (updated == (char *)NULL) &&
+				(skipped != (char *)NULL)) {
+		/*
+		 * fresh installing into non-global zone, no object was
+		 * updated (installed/verified in non-inherited area),
+		 * and and at least one object was skipped (verified in
+		 * inherited area) - this means all objects were skipped
+		 * so do not run the postinstall script.
+		 */
+		echoDebug(DBG_PKGINSTALL_POIS_SKIPPING,
+			zoneName ? zoneName : "global", pkginst, script);
+	} else {
+		/* script present and ok to run: run the script */
+		set_ulimit("postinstall", ERR_POSTINSTALL);
+		if (zoneName == (char *)NULL) {
+			echo(MSG_PKGINSTALL_EXEPIC_GZ);
+			echoDebug(DBG_PKGINSTALL_EXEPIC_GZ, pkginst, script);
+		} else {
+			echo(MSG_PKGINSTALL_EXEPIC_LZ, zoneName);
+			echoDebug(DBG_PKGINSTALL_EXEPIC_LZ, pkginst, script,
+				zoneName);
+		}
+		putparam("PKG_PROC_SCRIPT", "postinstall");
+		putparam("TMPDIR", tmpdir);
+		if (pkgverbose) {
+			ckreturn(pkgexecl(script_in, PROC_STDOUT,
+				PROC_USER, PROC_GRP, SHELL, "-x",
+				script, NULL), ERR_POSTINSTALL);
+		} else {
+			ckreturn(pkgexecl(script_in, PROC_STDOUT,
+				PROC_USER, PROC_GRP, SHELL, script,
+				NULL), ERR_POSTINSTALL);
+		}
+
+		clr_ulimit();
+		(void) remove(script);	/* no longer needed */
+	}
+
+	if (!warnflag && !failflag) {
+		if (pt = getenv("PREDEPEND"))
+			predepend(pt);
+		(void) remove(rlockfile);
+		(void) remove(ilockfile);
+		(void) remove(savlog);
+	}
+
+	/* release the generic package lock */
+
+	(void) unlockinst();
+
+	quit(0);
+	/* LINTED: no return */
+}
+
+/*
+ * This function merges the environment data in the response file with the
+ * current environment.
+ */
+static int
+merg_respfile()
+{
+	int retcode = 0;
+	char *resppath = get_respfile();
+	char *locbasedir;
+	char param[MAX_PKG_PARAM_LENGTH], *value;
+	FILE *fp;
+
+	if ((fp = fopen(resppath, "r")) == NULL) {
+		progerr(ERR_RESPONSE, resppath);
+		return (99);
+	}
+
+	param[0] = '\0';
+
+	while (value = fpkgparam(fp, param)) {
+		if (!isupper(param[0])) {
+			param[0] = '\0';
+			continue;
+		}
+
+		if (rdonly(param)) {
+			progerr(ERR_RDONLY, param);
+			param[0] = '\0';
+			continue;
+		}
+
+		/*
+		 * If this is an update, and the response file
+		 * specifies the BASEDIR, make sure it matches the
+		 * existing installation base. If it doesn't, we have
+		 * to quit.
+		 */
+		if (update && strcmp("BASEDIR", param) == 0) {
+			locbasedir = getenv("BASEDIR");
+			if (locbasedir && strcmp(value, locbasedir) != 0) {
+				char *dotptr;
+				/* Get srcinst down to a name. */
+				if (dotptr = strchr(srcinst, '.'))
+					*dotptr = '\000';
+				progerr(ERR_NEWBD, srcinst,
+					locbasedir, value);
+				retcode = 99;
+			}
+		}
+
+		putparam(param, value);
+		param[0] = '\0';
+	}
+	(void) fclose(fp);
+
+	return (retcode);
+}
+
+/*
+ * This scans the installed pkginfo file for the current BASEDIR. If this
+ * BASEDIR is different from the current BASEDIR, there will definitely be
+ * problems.
+ */
+static int
+ck_instbase(void)
+{
+	int retcode = 0;
+	char param[MAX_PKG_PARAM_LENGTH], *value;
+	char pkginfo_path[PATH_MAX];
+	FILE *fp;
+
+	/* Open the old pkginfo file. */
+	(void) snprintf(pkginfo_path, sizeof (pkginfo_path),
+			"%s/%s", pkgloc, PKGINFO);
+	if ((fp = fopen(pkginfo_path, "r")) == NULL) {
+		progerr(ERR_PKGINFO, pkginfo_path);
+		return (99);
+	}
+
+	param[0] = '\000';
+
+	while (value = fpkgparam(fp, param)) {
+		if (strcmp("BASEDIR", param) == 0) {
+			if (adm.basedir && *(adm.basedir) &&
+				strchr("/$", *(adm.basedir))) {
+				char *dotptr;
+
+				/*
+				 * Get srcinst down to a name.
+				 */
+				if (dotptr = strchr(srcinst, '.'))
+					*dotptr = '\000';
+				if (strcmp(value,
+					adm.basedir) != 0) {
+					progerr(ERR_ADMBD, srcinst,
+						value, adm.basedir);
+					retcode = 4;
+					break;
+				}
+			} else if (ADM(basedir, "ask"))
+				/*
+				 * If it's going to ask later, let it know
+				 * that it *must* agree with the BASEDIR we
+				 * just picked up.
+				 */
+				adm.basedir = "update";
+
+			putparam(param, value);
+			break;
+		}
+
+		param[0] = '\0';
+	}
+	(void) fclose(fp);
+
+	return (retcode);
+}
+
+/*
+ * Since this is an overwrite of a different version of the package, none of
+ * the old files should remain, so we rename them.
+ */
+static int
+mv_pkgdirs(void)
+{
+	/*
+	 * If we're not in dryrun mode and we can find an old set of package
+	 * files over which the new ones will be written, do the rename.
+	 */
+	if (!in_dryrun_mode() && pkgloc[0] && !access(pkgloc, F_OK)) {
+		(void) snprintf(pkgloc_sav, sizeof (pkgloc_sav),
+			"%s/.save.%s", get_PKGLOC(),
+			pkginst);
+		if (pkgloc_sav[0] && !access(pkgloc_sav, F_OK)) {
+			(void) rrmdir(pkgloc_sav);
+		}
+
+		if (rename(pkgloc, pkgloc_sav) == -1) {
+			progerr(ERR_PKGBINREN, pkgloc, pkgloc_sav);
+			return (99);
+		}
+	}
+
+	return (0);
+}
+
+/*
+ * Name:	merg_pkginfos
+ * Description:	This function scans the installed pkginfo and merges that
+ *		environment with the installing environment according to
+ *		the following rules:
+ *
+ *		1. CLASSES is a union of the installed and installing CLASSES
+ *			lists.
+ *		2. The installed BASEDIR takes precedence. If it doesn't agree
+ *		   with an administratively imposed BASEDIR, an ERROR is issued.
+ *		3. All other installing parameters are preserved.
+ *		4. All installed parameters are added if they do not overwrite
+ *		   an existing installing parameter.
+ *
+ *		The current environment contains the pkginfo settings for the
+ *		new package to be installed or to be updated.
+ *
+ * Arguments:	pclass - returned list of current classes involved in install
+ *		mpclass - pointer to returned list of current install classes
+ * Returns:	int
+ *		== 0 - all OK
+ *		!= 0 - an error code if a fatal error occurred
+ */
+
+static int
+merg_pkginfos(struct cl_attr **pclass, struct cl_attr ***mpclass)
+{
+	FILE	*fp;
+	char	SUNW_PKG_ALLZONES[MAX_PKG_PARAM_LENGTH] = {'\0'};
+	char	SUNW_PKG_HOLLOW[MAX_PKG_PARAM_LENGTH] = {'\0'};
+	char	SUNW_PKG_THISZONE[MAX_PKG_PARAM_LENGTH] = {'\0'};
+	char	*newValue;
+	char	*oldValue;
+	char	*pkgName;
+	char	*pkgVersion;
+	char	param[MAX_PKG_PARAM_LENGTH];
+	char	pkginfo_path[PATH_MAX];
+	int	retcode = 0;
+
+	/* obtain the name of the package (for error messages) */
+
+	pkgName = getenv("PKG");
+	if (pkgName == NULL) {
+		pkgName = "*current*";	/* default name */
+	}
+
+	/* obtain the version of the package (for error messages) */
+
+	pkgVersion = getenv("VERSION");
+	if (pkgVersion == NULL) {
+		pkgVersion = "*current*";	/* default version */
+	}
+
+	/* open installed package pkginfo file */
+
+	(void) snprintf(pkginfo_path, sizeof (pkginfo_path),
+			"%s/%s", pkgloc, PKGINFO);
+	if ((fp = fopen(pkginfo_path, "r")) == NULL) {
+		progerr(ERR_PKGINFO, pkginfo_path);
+		return (99);
+	}
+
+	/* entry debugging info */
+
+	echoDebug(DBG_MERGINFOS_ENTRY, pkginfo_path);
+
+	/*
+	 * cycle through the currently installed package's pkginfo parameters
+	 * and let the currently installed package's settings survive if the
+	 * update to the package does not provide an overriding value
+	 */
+
+	for (param[0] = '\0'; (oldValue = fpkgparam(fp, param)) != NULL;
+		param[0] = '\0') {
+
+		boolean_t	setZoneAttribute = B_FALSE;
+
+		/* debug info - attribute currently set to value */
+
+		echoDebug(DBG_MERGINFOS_SET_TO, param, oldValue);
+
+		/*
+		 * if zone package attribute is present in the currently
+		 * installed package, then remember the value for the
+		 * specific zone package attribute, and set the flag that
+		 * indicates a zone package attribute is being processed.
+		 */
+
+		if (strcmp(param, PKG_THISZONE_VARIABLE) == 0) {
+			/* SUNW_PKG_THISZONE currently set */
+			setZoneAttribute = B_TRUE;
+			(void) strlcpy(SUNW_PKG_THISZONE, oldValue,
+					sizeof (SUNW_PKG_THISZONE));
+		} else if (strcmp(param, PKG_ALLZONES_VARIABLE) == 0) {
+			/* SUNW_PKG_ALLZONES currently set */
+			setZoneAttribute = B_TRUE;
+			(void) strlcpy(SUNW_PKG_ALLZONES, oldValue,
+					sizeof (SUNW_PKG_ALLZONES));
+		} else if (strcmp(param, PKG_HOLLOW_VARIABLE) == 0) {
+			/* SUNW_PKG_THISZONE currently set */
+			setZoneAttribute = B_TRUE;
+			(void) strlcpy(SUNW_PKG_HOLLOW, oldValue,
+					sizeof (SUNW_PKG_HOLLOW));
+		}
+
+		/* handle CLASSES currently being set */
+
+		if (strcmp(param, "CLASSES") == 0) {
+			echoDebug(DBG_MERGINFOS_SET_CLASSES, oldValue);
+			/* create a list of the current classes */
+			(void) setlist(&pclass, qstrdup(oldValue));
+			/* set pointer to list of current classes */
+			*mpclass = pclass;
+			continue;
+		}
+
+		/* handle BASEDIR currently being set */
+
+		if (strcmp("BASEDIR", param) == 0) {
+			if (adm.basedir && *(adm.basedir) &&
+				strchr("/$", *(adm.basedir))) {
+				char *dotptr;
+
+				/* Get srcinst down to a* name */
+
+				if (dotptr = strchr(srcinst, '.')) {
+					*dotptr = '\000';
+				}
+				if (strcmp(oldValue, adm.basedir) != 0) {
+					progerr(ERR_ADMBD, srcinst,
+						oldValue, adm.basedir);
+					/* administration */
+					retcode = 4;
+					break;
+				}
+			} else if (ADM(basedir, "ask")) {
+				/*
+				 * If it's going to ask
+				 * later, let it know that it
+				 * *must* agree with the
+				 * BASEDIR we just picked up.
+				 */
+				adm.basedir = "update";
+				echoDebug(DBG_MERGINFOS_ASK_BASEDIR);
+			}
+
+			echoDebug(DBG_MERGINFOS_SET_BASEDIR, oldValue);
+			putparam(param, oldValue);
+			continue;
+		}
+
+		/*
+		 * determine if there is a new value for this attribute.
+		 */
+
+		newValue = getenv(param);
+
+		/*
+		 * If zone attributes of patch packages haven't been verified
+		 * by pdo, if there is no new value, and a zone attribute
+		 * is being changed, it is the same as setting the zone package
+		 * attribute to 'false' - make sure current setting is 'false'.
+		 */
+
+		if ((patchPkgInstall == B_FALSE) && (newValue == NULL) &&
+		    (setZoneAttribute == B_TRUE) &&
+		    (strcasecmp(oldValue, "false") != 0)) {
+
+			/* unset existing non-"false" zone pkg attr */
+			progerr(ERR_MERGINFOS_UNSET_ZONEATTR,
+				pkgName, pkgVersion, param, oldValue);
+			retcode = 1;
+			break;
+		}
+
+		/* retain old value if no new value specified */
+
+		if (newValue == NULL) {
+			/* no new value - retain the old value */
+			echoDebug(DBG_MERGINFOS_RETAIN_OLD, param, oldValue);
+			putparam(param, oldValue);
+			continue;
+		}
+
+		/* note if the old and new values are the same */
+
+		if (strcmp(newValue, oldValue) == 0) {
+			/* set existing package parameter to same value */
+			echoDebug(DBG_MERGINFOS_SET_DUPLICATE, param, oldValue);
+			continue;
+		}
+
+		/*
+		 * If zone attributes of patch packages haven't been verified
+		 * by pdo, check if old and new values differ.
+		 * Error if zone parameter
+		 */
+
+		if ((patchPkgInstall == B_FALSE) &&
+		    (setZoneAttribute == B_TRUE)) {
+			/* illegal change to zone attribute */
+
+			progerr(ERR_MERGINFOS_CHANGE_ZONEATTR, pkgName,
+				pkgVersion, param, oldValue, newValue);
+
+			/* set return code to "fatal error" */
+			retcode = 1;
+			break;
+		}
+
+		/* note valid change to existing package parameter */
+
+		echoDebug(DBG_MERGINFOS_SET_CHANGE, param,
+				oldValue, newValue);
+	}
+
+	/* close handle on currently installed package's pkginfo file */
+
+	(void) fclose(fp);
+
+	/* return error if not successful up to this point */
+
+	if (retcode != 0) {
+		echoDebug(DBG_MERGINFOS_EXIT, pkginfo_path, retcode);
+
+		return (retcode);
+	}
+
+	/*
+	 * Skip this if() section, if zone attributes of patch packages
+	 * have been verified by pdo.
+	 */
+
+	if (patchPkgInstall == B_FALSE) {
+
+		/*
+		 * verify that no zone attribute has been
+		 * set to an invalid value
+		 */
+
+		/* SUNW_PKG_ALLZONES */
+
+		newValue = getenv(PKG_ALLZONES_VARIABLE);
+
+		/*
+		 * complain if setting SUNW_PKG_ALLZONES to other than "false"
+		 */
+
+
+		if ((newValue != NULL) && (*SUNW_PKG_ALLZONES == '\0') &&
+		    (strcasecmp(newValue, "false") != 0)) {
+			/* change ALLZONES from "true" to "false" (unset) */
+			progerr(ERR_MERGINFOS_SET_ZONEATTR, pkgName,
+			    pkgVersion, PKG_ALLZONES_VARIABLE, newValue);
+			return (1);
+		}
+
+		/* SUNW_PKG_THISZONE */
+
+		newValue = getenv(PKG_THISZONE_VARIABLE);
+
+		/*
+		 * complain if setting SUNW_PKG_THISZONE to other than "false"
+		 */
+
+		if ((newValue != NULL) && (*SUNW_PKG_THISZONE == '\0') &&
+		    (strcasecmp(newValue, "false") != 0)) {
+			/* change THISZONE from "true" to "false" (unset) */
+			progerr(ERR_MERGINFOS_SET_ZONEATTR, pkgName,
+			    pkgVersion, PKG_THISZONE_VARIABLE, newValue);
+			return (1);
+		}
+
+		/* SUNW_PKG_HOLLOW */
+
+		newValue = getenv(PKG_HOLLOW_VARIABLE);
+
+		/* complain if setting SUNW_PKG_HOLLOW to other than "false" */
+
+		if ((newValue != NULL) && (*SUNW_PKG_HOLLOW == '\0') &&
+		    (strcasecmp(newValue, "false") != 0)) {
+			/* change HOLLOW from "true" to 'false" (unset) */
+			progerr(ERR_MERGINFOS_SET_ZONEATTR, pkgName,
+			    pkgVersion, PKG_HOLLOW_VARIABLE, newValue);
+			return (1);
+		}
+
+	}
+
+	/* return */
+
+	echoDebug(DBG_MERGINFOS_EXIT, pkginfo_path, 0);
+
+	return (0);
+}
+
+static void
+set_dryrun_dir_loc(void)
+{
+	/* Set pkg location to the dryrun directory */
+	set_PKGLOC(pkgdrtarg);
+	(void) snprintf(pkgloc, sizeof (pkgloc),
+			"%s/%s", get_PKGLOC(), pkginst);
+	(void) snprintf(pkgbin, sizeof (pkgbin),
+			"%s/install", pkgloc);
+	(void) snprintf(pkgsav, sizeof (pkgsav),
+			"%s/save", pkgloc);
+	(void) snprintf(ilockfile, sizeof (ilockfile),
+			"%s/!I-Lock!", pkgloc);
+	(void) snprintf(rlockfile, sizeof (rlockfile),
+			"%s/!R-Lock!", pkgloc);
+	(void) snprintf(savlog, sizeof (savlog),
+			"%s/logs/%s", get_PKGADM(), pkginst);
+}
+
+/*
+ * If we are updating a pkg, then we need to copy the "old" pkgloc so that
+ * any scripts that got removed in the new version aren't left around.  So we
+ * copy it here to .save.pkgloc, then in quit() we can restore our state, or
+ * remove it.
+ */
+static int
+cp_pkgdirs(void)
+{
+	if (in_dryrun_mode()) {
+		set_dryrun_dir_loc();
+	}
+
+	/*
+	 * If we're not in dryrun mode and we can find an old set of package
+	 * files over which the new ones will be written, do the copy.
+	 */
+	if (!in_dryrun_mode() && pkgloc[0] && !access(pkgloc, F_OK)) {
+		int status;
+		int r;
+
+		(void) snprintf(pkgloc_sav, sizeof (pkgloc_sav), "%s/.save.%s",
+			get_PKGLOC(), pkginst);
+
+		/*
+		 * Even though it takes a while, we use a recursive copy here
+		 * because if the current pkgadd fails for any reason, we
+		 * don't want to lose this data.
+		 */
+		r = e_ExecCmdList(&status, (char **)NULL, (char *)NULL,
+			"/usr/bin/cp", "cp", "-r", pkgloc, pkgloc_sav,
+			(char *)NULL);
+
+		if ((r != 0) || (status == -1) || (WEXITSTATUS(status) != 0)) {
+			progerr(ERR_PKGBINCP, pkgloc, pkgloc_sav);
+			return (99);
+		}
+	}
+
+	return (0);
+}
+
+/*
+ * This implements the pkgask function. It just executes the request script
+ * and stores the results in a response file.
+ */
+static void
+do_pkgask(boolean_t a_run_request_as_root)
+{
+	if (pkgdev.cdevice) {
+		unpack();
+		if (!suppressCopyright) {
+			copyright();
+		}
+	}
+	(void) snprintf(path, sizeof (path), "%s/%s", instdir, REQUEST_FILE);
+	if (access(path, F_OK)) {
+		progerr(ERR_NOREQUEST);
+		quit(1);
+		/*NOTREACHED*/
+	}
+
+	(void) set_respfile(respfile, srcinst, RESP_WR);
+
+	if (is_a_respfile()) {
+		ckreturn(reqexec(update, path, non_abi_scripts,
+			a_run_request_as_root), ERR_REQUEST);
+	} else {
+		failflag++;
+	}
+
+	if (warnflag || failflag) {
+		(void) remove(respfile);
+		echo("\nResponse file <%s> was not created.",
+			get_respfile());
+	} else {
+		echo("\nResponse file <%s> was created.",
+			get_respfile());
+	}
+
+	quit(0);
+	/*NOTREACHED*/
+}
+
+/*
+ * This function runs a check utility and acts appropriately based upon the
+ * return code. It deals appropriately with the dryrun file if it is present.
+ */
+static void
+ck_w_dryrun(int (*func)(), int type)
+{
+	int n;
+
+	n = func();
+	if (in_dryrun_mode())
+		set_dr_info(type, !n);
+
+	if (n) {
+		quit(n);
+		/*NOTREACHED*/
+	}
+}
+
+/*
+ * This function deletes all install class action scripts from the package
+ * directory on the root filesystem.
+ */
+static void
+rm_icas(char *cas_dir)
+{
+	DIR	*pdirfp;
+	struct	dirent *dp;
+	char path[PATH_MAX];
+
+	if ((pdirfp = opendir(cas_dir)) == NULL)
+		return;
+
+	while ((dp = readdir(pdirfp)) != NULL) {
+		if (dp->d_name[0] == '.')
+			continue;
+
+		if (dp->d_name[0] == 'i' && dp->d_name[1] == '.') {
+			(void) snprintf(path, sizeof (path),
+				"%s/%s", cas_dir, dp->d_name);
+			(void) remove(path);
+		}
+	}
+	(void) closedir(pdirfp);
+}
+
+void
+ckreturn(int retcode, char *msg)
+{
+	switch (retcode) {
+		case 2:
+		case 12:
+		case 22:
+		warnflag++;
+		if (msg) {
+			progerr("%s", msg);
+		}
+		/*FALLTHRU*/
+		case 10:
+		case 20:
+		if (retcode >= 10 && retcode < 20) {
+			dreboot++;
+		}
+		if (retcode >= 20) {
+			ireboot++;
+		}
+		/*FALLTHRU*/
+		case 0:
+		break; /* okay */
+
+		case -1:
+		retcode = 99;
+		/*FALLTHRU*/
+		case 99:
+		case 1:
+		case 11:
+		case 21:
+		case 4:
+		case 14:
+		case 24:
+		case 5:
+		case 15:
+		case 25:
+		if (msg) {
+			progerr("%s", msg);
+		}
+		/*FALLTHRU*/
+		case 3:
+		case 13:
+		case 23:
+		quit(retcode);
+		/*NOTREACHED*/
+		default:
+		if (msg) {
+			progerr("%s", msg);
+		}
+		quit(1);
+		/*NOTREACHED*/
+	}
+}
+
+static void
+copyright(void)
+{
+	FILE	*fp;
+	char	line[LSIZE];
+	char	path[PATH_MAX];
+
+	/* Compose full path for copyright file */
+	(void) snprintf(path, sizeof (path), "%s/%s", instdir, COPYRIGHT_FILE);
+
+	if ((fp = fopen(path, "r")) == NULL) {
+		if (getenv("VENDOR") != NULL)
+			echo(getenv("VENDOR"));
+	} else {
+		while (fgets(line, LSIZE, fp))
+			(void) fprintf(stdout, "%s", line); /* bug #1083713 */
+		(void) fclose(fp);
+	}
+}
+
+static int
+rdonly(char *p)
+{
+	int	i;
+
+	for (i = 0; ro_params[i]; i++) {
+		if (strcmp(p, ro_params[i]) == 0)
+			return (1);
+	}
+	return (0);
+}
+
+static void
+unpack(void)
+{
+	/*
+	 * read in next part from stream, even if we decide
+	 * later that we don't need it
+	 */
+	if (dparts < 1) {
+		progerr(ERR_DSTREAMCNT);
+		quit(99);
+		/*NOTREACHED*/
+	}
+	if ((access(instdir, F_OK) == 0) && rrmdir(instdir)) {
+		progerr(ERR_RMDIR, instdir);
+		quit(99);
+		/*NOTREACHED*/
+	}
+	if (mkdir(instdir, 0755)) {
+		progerr(ERR_MKDIR, instdir);
+		quit(99);
+		/*NOTREACHED*/
+	}
+	if (chdir(instdir)) {
+		progerr(ERR_CHDIR, instdir);
+		quit(99);
+		/*NOTREACHED*/
+	}
+	if (!ds_fd_open()) {
+		dparts = ds_findpkg(pkgdev.cdevice, srcinst);
+		if (dparts < 1) {
+			progerr(ERR_DSARCH, srcinst);
+			quit(99);
+			/*NOTREACHED*/
+		}
+	}
+
+	dparts--;
+
+	if (ds_next(pkgdev.cdevice, instdir)) {
+		progerr(ERR_DSTREAM);
+		quit(99);
+		/*NOTREACHED*/
+	}
+	if (chdir(get_PKGADM())) {
+		progerr(ERR_CHDIR, get_PKGADM());
+		quit(99);
+		/*NOTREACHED*/
+	}
+	ds_close(1);
+}
+
+static void
+usage(void)
+{
+	(void) fprintf(stderr, ERR_USAGE_PKGINSTALL);
+	exit(1);
+	/*NOTREACHED*/
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/usr/src/cmd/svr4pkg/pkginstall/merginfo.c	Fri Jun 05 10:28:40 2009 -0400
@@ -0,0 +1,621 @@
+/*
+ * 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
+ * or http://www.opensolaris.org/os/licensing.
+ * 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 2007 Sun Microsystems, Inc.  All rights reserved.
+ * Use is subject to license terms.
+ */
+
+/* Copyright (c) 1984, 1986, 1987, 1988, 1989 AT&T */
+/* All Rights Reserved */
+
+
+#include <stdio.h>
+#include <string.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <dirent.h>
+#include <locale.h>
+#include <libintl.h>
+#include <errno.h>
+#include "pkglib.h"
+#include "install.h"
+#include "libadm.h"
+#include "libinst.h"
+#include "pkginstall.h"
+#include "messages.h"
+
+extern char	instdir[], pkgbin[], pkgloc[], savlog[], *pkginst, **environ;
+extern char	saveSpoolInstallDir[];
+extern char	pkgsav[];	/* pkginstall/main.c */
+static char 	*infoloc;
+
+/*
+ * flag definitions for each entry in table
+ */
+
+typedef unsigned int TBL_FLAG_T;
+
+/* no flag set */
+#define	FLAG_NONE	((TBL_FLAG_T)0x0000)
+
+/* exclude this attribute if found */
+#define	FLAG_EXCLUDE	((TBL_FLAG_T)0x0001)
+
+/* this attribute must not change if found */
+#define	FLAG_IDENTICAL	((TBL_FLAG_T)0x0002)
+
+/*
+ * macro to generate an entry in the table:
+ *	TBL_ENTRY("PKGINFO_ATTRIBUTE=", FLAG_XXX)
+ * where:
+ *	"PKGINFO_ATTRIBUTE=" is the attribute to look for
+ *	FLAG_XXX is the action to perform when the attribute is found
+ */
+
+#define	TBL_ENTRY(_Y_, _F_)	{ (_Y_), ((sizeof ((_Y_)))-1), (_F_) }
+
+/*
+ * table containing attributes that require special handling
+ */
+
+struct _namelist {
+	char		*_nlName;	/* attribute name */
+	int		_nlLen;		/* attribute length */
+	TBL_FLAG_T	_nlFlag;	/* attribute disposition flag */
+};
+
+typedef struct _namelist NAMELIST_T;
+
+/*
+ * These are attributes to be acted on in some way when a pkginfo file is
+ * merged. This table MUST be in alphabetical order because it is searched
+ * using a binary search algorithm.
+ */
+
+static NAMELIST_T attrTbl[] = {
+	TBL_ENTRY("BASEDIR=",			FLAG_EXCLUDE),
+	TBL_ENTRY("CLASSES=",			FLAG_EXCLUDE),
+	TBL_ENTRY("CLIENT_BASEDIR=",		FLAG_EXCLUDE),
+	TBL_ENTRY("INST_DATADIR=",		FLAG_EXCLUDE),
+	TBL_ENTRY("PKG_CAS_PASSRELATIVE=",	FLAG_EXCLUDE),
+	TBL_ENTRY("PKG_DST_QKVERIFY=",		FLAG_EXCLUDE),
+	TBL_ENTRY("PKG_INIT_INSTALL=",		FLAG_EXCLUDE),
+	TBL_ENTRY("PKG_INSTALL_ROOT=",		FLAG_EXCLUDE),
+	TBL_ENTRY("PKG_SRC_NOVERIFY=",		FLAG_EXCLUDE),
+	TBL_ENTRY("SUNW_PKGCOND_GLOBAL_DATA=",	FLAG_EXCLUDE),
+	TBL_ENTRY("SUNW_PKG_ALLZONES=",		FLAG_IDENTICAL),
+	TBL_ENTRY("SUNW_PKG_DIR=",		FLAG_EXCLUDE),
+	TBL_ENTRY("SUNW_PKG_HOLLOW=",		FLAG_IDENTICAL),
+	TBL_ENTRY("SUNW_PKG_INSTALL_ZONENAME=",	FLAG_EXCLUDE),
+	TBL_ENTRY("SUNW_PKG_THISZONE=",		FLAG_IDENTICAL),
+};
+
+#define	ATTRTBL_SIZE	(sizeof (attrTbl) / sizeof (NAMELIST_T))
+
+/*
+ * While pkgsav has to be set up with reference to the server for package
+ * scripts, it has to be client-relative in the pkginfo file. This function
+ * is used to set the client-relative value for use in the pkginfo file.
+ */
+void
+set_infoloc(char *path)
+{
+	if (path && *path) {
+		if (is_an_inst_root()) {
+			/* Strip the server portion of the path. */
+			infoloc = orig_path(path);
+		} else {
+			infoloc = strdup(path);
+		}
+	}
+}
+
+void
+merginfo(struct cl_attr **pclass, int install_from_pspool)
+{
+	DIR		*pdirfp;
+	FILE		*fp;
+	FILE		*pkginfoFP;
+	char		path[PATH_MAX];
+	char		cmd[PATH_MAX];
+	char		pkginfoPath[PATH_MAX];
+	char		temp[PATH_MAX];
+	int		i;
+	int		nc;
+	int		out;
+
+	/* remove savelog from previous attempts */
+
+	(void) unlink(savlog);
+
+	/*
+	 * create path to appropriate pkginfo file for the package that is
+	 * already installed - is_spool_create() will be set (!= 0) if the
+	 * -t option is presented to pkginstall - the -t option is used to
+	 * disable save spool area creation; do not spool any partial package
+	 * contents, that is, suppress the creation and population of the
+	 * package save spool area (var/sadm/pkg/PKG/save/pspool/PKG). This
+	 * option is set only when a non-global zone is being created.
+	 */
+
+	if (is_spool_create() == 0) {
+		/*
+		 * normal package install (not a non-global zone install);
+		 * use the standard installed pkginfo file for this package:
+		 * --> /var/sadm/pkg/PKGINST/pkginfo
+		 * as the source pkginfo file to scan.
+		 */
+		i = snprintf(pkginfoPath, sizeof (pkginfoPath),
+			"%s/var/sadm/pkg/%s/%s",
+			((get_inst_root()) &&
+			(strcmp(get_inst_root(), "/") != 0)) ?
+			get_inst_root() : "", pkginst,
+			PKGINFO);
+		if (i > sizeof (pkginfoPath)) {
+			progerr(ERR_CREATE_PATH_2,
+				((get_inst_root()) &&
+				(strcmp(get_inst_root(), "/") != 0)) ?
+				get_inst_root() : "/",
+				pkginst);
+			quit(1);
+		}
+	} else {
+		/*
+		 * non-global zone installation - use the "saved" pspool
+		 * pkginfo file in the global zone for this package:
+		 * --> /var/sadm/install/PKG/save/pspool/PKG/pkginfo
+		 * as the source pkginfo file to scan.
+		 */
+		i = snprintf(pkginfoPath, sizeof (pkginfoPath), "%s/%s",
+			saveSpoolInstallDir, PKGINFO);
+		if (i > sizeof (pkginfoPath)) {
+			progerr(ERR_CREATE_PATH_2,
+				saveSpoolInstallDir, PKGINFO);
+			quit(1);
+		}
+	}
+
+	i = snprintf(path, PATH_MAX, "%s/%s", pkgloc, PKGINFO);
+	if (i > PATH_MAX) {
+		progerr(ERR_CREATE_PATH_2, pkgloc, PKGINFO);
+		quit(1);
+	}
+
+	/* entry debugging info */
+
+	echoDebug(DBG_MERGINFO_ENTRY,
+		instdir ? instdir : "??",
+		((get_inst_root()) &&
+		(strcmp(get_inst_root(), "/") != 0)) ?
+		get_inst_root() : "??",
+		saveSpoolInstallDir ? saveSpoolInstallDir : "??",
+		pkgloc ? pkgloc : "??",	is_spool_create(),
+		get_info_basedir() ? get_info_basedir() : "??",
+		pkginfoPath, path);
+
+	/*
+	 * open the pkginfo file:
+	 * if the source pkginfo file to check is the same as the merged one
+	 * (e.g. /var/sadm/pkg/PKGINST/pkginfo) then do not open the source
+	 * pkginfo file to "verify"
+	 */
+
+	if (strcmp(pkginfoPath, path) == 0) {
+		pkginfoFP = (FILE *)NULL;
+		echoDebug(DBG_MERGINFO_SAME, path);
+	} else {
+		echoDebug(DBG_MERGINFO_DIFFERENT, pkginfoPath, path);
+		pkginfoFP = fopen(pkginfoPath, "r");
+
+		if (pkginfoFP == (FILE *)NULL) {
+			echoDebug(ERR_NO_PKG_INFOFILE, pkginst, pkginfoPath,
+				strerror(errno));
+		}
+	}
+
+	/*
+	 * output packaging environment to create a pkginfo file in pkgloc[]
+	 */
+
+	if ((fp = fopen(path, "w")) == NULL) {
+		progerr(ERR_CANNOT_OPEN_FOR_WRITING, path, strerror(errno));
+		quit(99);
+	}
+
+	/*
+	 * output CLASSES attribute
+	 */
+
+	out = 0;
+	(void) fputs("CLASSES=", fp);
+	if (pclass) {
+		(void) fputs(pclass[0]->name, fp);
+		out++;
+		for (i = 1; pclass[i]; i++) {
+			(void) putc(' ', fp);
+			(void) fputs(pclass[i]->name, fp);
+			out++;
+		}
+	}
+	nc = cl_getn();
+	for (i = 0; i < nc; i++) {
+		int found = 0;
+
+		if (pclass) {
+			int	j;
+
+			for (j = 0; pclass[j]; ++j) {
+				if (cl_nam(i) != NULL &&
+					strcmp(cl_nam(i),
+					pclass[j]->name) == 0) {
+					found++;
+					break;
+				}
+			}
+		}
+		if (!found) {
+			if (out > 0) {
+				(void) putc(' ', fp);
+			}
+			(void) fputs(cl_nam(i), fp);
+			out++;
+		}
+	}
+	(void) putc('\n', fp);
+
+	/*
+	 * NOTE : BASEDIR below is relative to the machine that
+	 * *runs* the package. If there's an install root, this
+	 * is actually the CLIENT_BASEDIR wrt the machine
+	 * doing the pkgadd'ing here. -- JST
+	 */
+
+	if (is_a_basedir()) {
+		static char	*txs1 = "BASEDIR=";
+
+		(void) fputs(txs1, fp);
+		(void) fputs(get_info_basedir(), fp);
+		(void) putc('\n', fp);
+	} else {
+		(void) fputs("BASEDIR=/", fp);
+		(void) putc('\n', fp);
+	}
+
+	/*
+	 * output all other environment attributes except those which
+	 * are relevant only to install.
+	 */
+
+	for (i = 0; environ[i] != (char *)NULL; i++) {
+		char	*ep = environ[i];
+		int	attrPos = -1;
+		int	incr = (ATTRTBL_SIZE >> 1)+1;	/* searches possible */
+		int	pos = ATTRTBL_SIZE >> 1;	/* start in middle */
+		NAMELIST_T	*pp = (NAMELIST_T *)NULL;
+
+		/*
+		 * find this attribute in the table - accept the attribute if it
+		 * is outside of the bounds of the table; otherwise, do a binary
+		 * search looking for this attribute.
+		 */
+
+		if (strncmp(ep, attrTbl[0]._nlName, attrTbl[0]._nlLen) < 0) {
+
+			/* entry < first entry in attribute table */
+
+			echoDebug(DBG_MERGINFO_LESS_THAN, ep,
+				attrTbl[0]._nlName);
+
+		} else if (strncmp(ep, attrTbl[ATTRTBL_SIZE-1]._nlName,
+				attrTbl[ATTRTBL_SIZE-1]._nlLen) > 0) {
+
+			/* entry > last entry in attribute table */
+
+			echoDebug(DBG_MERGINFO_GREATER_THAN, ep,
+				attrTbl[ATTRTBL_SIZE-1]._nlName);
+
+		} else {
+			/* first entry < entry < last entry in table: search */
+
+			echoDebug(DBG_MERGINFO_SEARCHING, ep,
+				attrTbl[0]._nlName,
+				attrTbl[ATTRTBL_SIZE-1]._nlName);
+
+			while (incr > 0) {	/* while possible to divide */
+				int	r;
+
+				pp = &attrTbl[pos];
+
+				/* compare current attr with this table entry */
+				r = strncmp(pp->_nlName, ep, pp->_nlLen);
+
+				/* break out of loop if match */
+				if (r == 0) {
+					/* save location/break if match found */
+					attrPos = pos;
+					break;
+				}
+
+				/* no match search to next/prev half */
+				incr = incr >> 1;
+				pos += (r < 0) ? incr : -incr;
+				continue;
+			}
+		}
+
+		/* handle excluded attribute found */
+
+		if ((attrPos >= 0) && (pp->_nlFlag == FLAG_EXCLUDE)) {
+			/* attribute is excluded */
+			echoDebug(DBG_MERGINFO_EXCLUDING, ep);
+			continue;
+		}
+
+		/* handle fixed attribute found */
+
+		if ((pkginfoFP != (FILE *)NULL) && (attrPos >= 0) &&
+			(pp->_nlFlag == FLAG_IDENTICAL)) {
+			/* attribute must not change */
+
+			char	*src = ep+pp->_nlLen;
+			char	*trg;
+			char	theAttr[PATH_MAX+1];
+
+			/* isolate attribute name only without '=' at end */
+
+			(void) strncpy(theAttr, pp->_nlName, pp->_nlLen-1);
+			theAttr[pp->_nlLen-1] = '\0';
+
+			/* lookup attribute in installed package pkginfo file */
+
+			rewind(pkginfoFP);
+			trg = fpkgparam(pkginfoFP, theAttr);
+
+			echoDebug(DBG_MERGINFO_ATTRCOMP, theAttr,
+				trg ? trg : "");
+
+			/* if target not found attribute is being added */
+
+			if (trg == (char *)NULL) {
+				progerr(ERR_PKGINFO_ATTR_ADDED, pkginst, ep);
+				quit(1);
+			}
+
+			/* error if two values are not the same */
+
+			if (strcmp(src, trg) != 0) {
+				progerr(ERR_PKGINFO_ATTR_CHANGED, pkginst,
+					theAttr, src, trg);
+				quit(1);
+			}
+		}
+
+		/* attribute not excluded/has not changed - process */
+
+		if ((strncmp(ep, "PKGSAV=", 7) == 0)) {
+			(void) fputs("PKGSAV=", fp);
+			(void) fputs(infoloc, fp);
+			(void) putc('/', fp);
+			(void) fputs(pkginst, fp);
+			(void) fputs("/save\n", fp);
+			continue;
+		}
+
+		if ((strncmp(ep, "UPDATE=", 7) == 0) &&
+				install_from_pspool != 0 &&
+				!isPatchUpdate() &&
+				!isUpdate()) {
+			continue;
+		}
+
+		echoDebug(DBG_MERGINFO_FINAL, ep);
+
+		(void) fputs(ep, fp);
+		(void) putc('\n', fp);
+	}
+
+	(void) fclose(fp);
+	(void) fclose(pkginfoFP);
+
+	/*
+	 * copy all packaging scripts to appropriate directory
+	 */
+
+	i = snprintf(path, PATH_MAX, "%s/install", instdir);
+	if (i > PATH_MAX) {
+		progerr(ERR_CREATE_PATH_2, instdir, "/install");
+		quit(1);
+	}
+
+	if ((pdirfp = opendir(path)) != NULL) {
+		struct dirent	*dp;
+
+		while ((dp = readdir(pdirfp)) != NULL) {
+			if (dp->d_name[0] == '.')
+				continue;
+
+			i = snprintf(path, PATH_MAX, "%s/install/%s",
+					instdir, dp->d_name);
+			if (i > PATH_MAX) {
+				progerr(ERR_CREATE_PATH_3, instdir, "/install/",
+					dp->d_name);
+				quit(1);
+			}
+
+			i = snprintf(temp, PATH_MAX, "%s/%s", pkgbin,
+					dp->d_name);
+			if (i > PATH_MAX) {
+				progerr(ERR_CREATE_PATH_2, pkgbin, dp->d_name);
+				quit(1);
+			}
+
+			if (cppath(MODE_SRC|DIR_DISPLAY, path, temp, 0644)) {
+			    progerr(ERR_CANNOT_COPY, dp->d_name, pkgbin);
+				quit(99);
+			}
+		}
+		(void) closedir(pdirfp);
+	}
+
+	/*
+	 * copy all packaging scripts to the partial spool directory
+	 */
+
+	if (!is_spool_create()) {
+		/* packages are being spooled to ../save/pspool/.. */
+		i = snprintf(path, PATH_MAX, "%s/install", instdir);
+		if (i > PATH_MAX) {
+			progerr(ERR_CREATE_PATH_2, instdir, "/install");
+			quit(1);
+		}
+
+		if (((pdirfp = opendir(path)) != NULL) &&
+			!isPatchUpdate()) {
+			struct dirent	*dp;
+
+
+			while ((dp = readdir(pdirfp)) != NULL) {
+				if (dp->d_name[0] == '.')
+					continue;
+				/*
+				 * Don't copy i.none since if it exists it
+				 * contains Class Archive Format procedure
+				 * for installing archives. Only Directory
+				 * Format packages can exist
+				 * in a global spooled area.
+				 */
+				if (strcmp(dp->d_name, "i.none") == 0)
+					continue;
+
+				i = snprintf(path, PATH_MAX, "%s/install/%s",
+						instdir, dp->d_name);
+
+				if (i > PATH_MAX) {
+					progerr(ERR_CREATE_PATH_3, instdir,
+						"/install/", dp->d_name);
+					quit(1);
+				}
+
+				i = snprintf(temp, PATH_MAX, "%s/install/%s",
+						saveSpoolInstallDir,
+						dp->d_name);
+
+				if (i > PATH_MAX) {
+					progerr(ERR_CREATE_PATH_3,
+						saveSpoolInstallDir,
+						"/install/", dp->d_name);
+					quit(1);
+				}
+
+				if (cppath(MODE_SRC, path, temp, 0644)) {
+					progerr(ERR_CANNOT_COPY, path, temp);
+					(void) closedir(pdirfp);
+					quit(99);
+				}
+			}
+			(void) closedir(pdirfp);
+		}
+
+		/*
+		 * Now copy the original pkginfo and pkgmap files from the
+		 * installing package to the spooled directory.
+		 */
+
+		i = snprintf(path, sizeof (path), "%s/%s", instdir, PKGINFO);
+		if (i > sizeof (path)) {
+			progerr(ERR_CREATE_PATH_2, instdir, PKGINFO);
+			quit(1);
+		}
+
+		i = snprintf(temp, sizeof (temp), "%s/%s",
+				saveSpoolInstallDir, PKGINFO);
+		if (i > sizeof (temp)) {
+			progerr(ERR_CREATE_PATH_2, saveSpoolInstallDir,
+				PKGINFO);
+			quit(1);
+		}
+
+		if (cppath(MODE_SRC, path, temp, 0644)) {
+			progerr(ERR_CANNOT_COPY, path, temp);
+			quit(99);
+		}
+
+		/*
+		 * Only want to copy the FCS pkgmap if this is not a
+		 * patch installation.
+		 */
+
+		if (!isPatchUpdate()) {
+			i = snprintf(path, sizeof (path), "%s/pkgmap", instdir);
+			if (i > sizeof (path)) {
+				progerr(ERR_CREATE_PATH_2, instdir, "pkgmap");
+				quit(1);
+			}
+
+			i = snprintf(temp, sizeof (temp), "%s/pkgmap",
+				saveSpoolInstallDir);
+			if (i > sizeof (path)) {
+				progerr(ERR_CREATE_PATH_2, saveSpoolInstallDir,
+					"pkgmap");
+				quit(1);
+			}
+
+			if (cppath(MODE_SRC, path, temp, 0644)) {
+				progerr(ERR_CANNOT_COPY, path, temp);
+				quit(99);
+			}
+		}
+	}
+
+	/*
+	 * If we are installing from a spool directory
+	 * copy the save directory from it, it may have
+	 * been patched. Duplicate it only if this
+	 * installation isn't an update and is not to
+	 * an alternate root.
+	 */
+	if (strstr(instdir, "pspool") != NULL) {
+		struct stat status;
+
+		i = snprintf(path, sizeof (path), "%s/save", instdir);
+		if (i > sizeof (path)) {
+			progerr(ERR_CREATE_PATH_2, instdir, "save");
+			quit(1);
+		}
+
+		if ((stat(path, &status) == 0) &&
+				(status.st_mode & S_IFDIR) &&
+				!isPatchUpdate()) {
+			i = snprintf(cmd, sizeof (cmd), "cp -pr %s/* %s",
+					path, pkgsav);
+			if (i > sizeof (cmd)) {
+				progerr(ERR_SNPRINTF, "cp -pr %s/* %s");
+				quit(1);
+			}
+
+			if (system(cmd)) {
+				progerr(ERR_PKGBINCP, path, pkgsav);
+				quit(99);
+			}
+		}
+	}
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/usr/src/cmd/svr4pkg/pkginstall/pkgenv.c	Fri Jun 05 10:28:40 2009 -0400
@@ -0,0 +1,126 @@
+/*
+ * 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
+ * or http://www.opensolaris.org/os/licensing.
+ * 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 2006 Sun Microsystems, Inc.  All rights reserved.
+ * Use is subject to license terms.
+ */
+
+/* Copyright (c) 1984, 1986, 1987, 1988, 1989 AT&T */
+/* All Rights Reserved */
+
+
+#include <stdio.h>
+#include <limits.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <sys/types.h>
+#include <pkgstrct.h>
+#include <string.h>
+#include <locale.h>
+#include <libintl.h>
+#include <pkglib.h>
+#include "install.h"
+#include "libadm.h"
+#include "libinst.h"
+
+#define	ERR_PKGINFO	"unable to access pkginfo file <%s>"
+#define	ERR_PKGMAP	"unable to access pkgmap file <%s>"
+#define	ERR_NOPARAM	"%s parameter is not defined in <%s>"
+#define	ERR_PKGBAD	"PKG parameter is invalid <%s>"
+#define	ERR_PKGMTCH	"PKG parameter <%s> does not match instance <%s>"
+
+char	*pkgarch;
+char	*pkgvers;
+char	*pkgabrv;
+char	*pkgname;
+char	pkgwild[PKGSIZ+1];
+
+/*
+ * This function confirms the presence of pkgmap and pkginfo and verifies
+ * that the mandatory parameters are available in the environment.
+ */
+int
+pkgenv(char *pkginst, char *p_pkginfo, char *p_pkgmap)
+{
+	FILE	*fp;
+	char 	*value,
+		path[PATH_MAX],
+		param[MAX_PKG_PARAM_LENGTH];
+	int	errflg;
+
+	errflg = 0;
+	if (access(p_pkgmap, 0)) {
+		progerr(gettext(ERR_PKGMAP), p_pkgmap);
+		return (1);
+	}
+	if ((fp = fopen(p_pkginfo, "r")) == NULL) {
+		progerr(gettext(ERR_PKGINFO), p_pkginfo);
+		return (1);
+	}
+	param[0] = '\0';
+	while (value = fpkgparam(fp, param)) {
+		if (strcmp("PATH", param))
+			putparam(param, value);
+		free(value);
+		param[0] = '\0';
+	}
+	(void) fclose(fp);
+	/*
+	 * verify that required parameters are now present in
+	 * the environment
+	 */
+	if ((pkgabrv = getenv("PKG")) == NULL) {
+		progerr(gettext(ERR_NOPARAM), "PKG", path);
+		errflg++;
+	}
+	if (pkgnmchk(pkgabrv, NULL, 0) || strchr(pkgabrv, '.')) {
+		progerr(gettext(ERR_PKGBAD), pkgabrv);
+		errflg++;
+	}
+	(void) snprintf(pkgwild, sizeof (pkgwild), "%s.*", pkgabrv);
+	if ((pkgname = getenv("NAME")) == NULL) {
+		progerr(gettext(ERR_NOPARAM), "NAME", path);
+		errflg++;
+	}
+	if ((pkgarch = getenv("ARCH")) == NULL) {
+		progerr(gettext(ERR_NOPARAM), "ARCH", path);
+		errflg++;
+	}
+	if ((pkgvers = getenv("VERSION")) == NULL) {
+		progerr(gettext(ERR_NOPARAM), "VERSION", path);
+		errflg++;
+	}
+	if (getenv("CATEGORY") == NULL) {
+		progerr(gettext(ERR_NOPARAM), "CATEGORY", path);
+		errflg++;
+	}
+	/*
+	 * verify consistency between PKG parameter and pkginst that
+	 * was determined from the directory structure
+	 */
+	(void) snprintf(param, sizeof (param), "%s.*", pkgabrv);
+	if (pkgnmchk(pkginst, param, 0)) {
+		progerr(gettext(ERR_PKGMTCH), pkgabrv, pkginst);
+		errflg++;
+	}
+	return (errflg);
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/usr/src/cmd/svr4pkg/pkginstall/pkginstall.h	Fri Jun 05 10:28:40 2009 -0400
@@ -0,0 +1,107 @@
+/*
+ * 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
+ * or http://www.opensolaris.org/os/licensing.
+ * 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 2005 Sun Microsystems, Inc.  All rights reserved.
+ * Use is subject to license terms.
+ */
+
+#ifndef __PKG_PKGINSTALL_H__
+#define	__PKG_PKGINSTALL_H__
+
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/* cppath() variables */
+#define	DIR_DISPLAY	0x0001	/* display implied directories created */
+#define	MODE_SRC	0x0002	/* set mode to mode of source file */
+#define	MODE_SET	0x0004	/* set mode to mode passed in as argument */
+#define	MODE_0666	0x0008	/* force mode to 0666 */
+
+/* special stdin for request scripts */
+#define	REQ_STDIN	"/dev/tty"
+
+/* response file writability status */
+#define	RESP_WR		0	/* Response file is writable. */
+#define	RESP_RO		1	/* Read only. */
+
+#ifdef __STDC__
+#ifndef __P
+#define	__P(x)	x
+#endif
+#else
+#ifndef __P
+#define	__P(x)	()
+#endif
+#endif /* __STDC__ */
+
+extern int	cppath __P((int ctrl, char *f1, char *f2, mode_t mode));
+extern void	backup __P((char *path, int mode));
+extern void	pkgvolume __P((struct pkgdev *devp, char *pkg, int part,
+		    int nparts));
+extern void	quit __P((int exitval));
+extern void	ckreturn __P((int retcode, char *msg));
+extern int	sortmap __P((struct cfextra ***extlist, VFP_T *pkgmapVfp,
+			VFP_T *mapvfp, VFP_T *tmpvfp, char *a_zoneName));
+extern void merginfo __P((struct cl_attr **pclass, int install_from_pspool));
+extern void	set_infoloc __P((char *real_pkgsav));
+extern int	pkgenv __P((char *pkginst, char *p_pkginfo, char *p_pkgmap));
+extern void	instvol __P((struct cfextra **extlist, char *srcinst, int part,
+			int nparts, VFP_T **a_cfVfp, VFP_T **a_cfTmpVfp,
+			char **r_updated, char **r_skipped,
+			char *a_zoneName));
+extern int	reqexec __P((int update, char *script, int non_abi_scripts,
+			boolean_t enable_root_user));
+extern int	chkexec __P((int update, char *script));
+extern int	rdonly_respfile __P((void));
+extern int	is_a_respfile __P((void));
+extern char	*get_respfile __P((void));
+extern int	set_respfile __P((char *respfile, char *pkginst,
+		    int resp_stat));
+extern void	predepend __P((char *oldpkg));
+extern void	cksetPreinstallCheck __P((boolean_t a_preinstallCheck));
+extern void	cksetZoneName __P((char *a_zoneName));
+extern int	cksetuid __P((void));
+extern int	ckconflct __P((void));
+extern int	ckpkgdirs __P((void));
+extern int	ckspace __P((void));
+extern int	ckdepend __P((void));
+extern int	ckrunlevel __P((void));
+extern int	ckpartial __P((void));
+extern int	ckpkgfiles __P((void));
+extern int	ckpriv __P((void));
+extern void	is_WOS_arch __P((void));
+extern void	ckdirs __P((void));
+extern char	*getinst __P((int *updatingExisting, struct pkginfo *info,
+			int npkgs, boolean_t a_preinstallCheck));
+extern int	is_samepkg __P((void));
+extern int	dockspace __P((char *spacefile));
+
+extern int	special_contents_add(int, struct cfextra **, const char *);
+extern boolean_t	rm_all_pkg_entries(char *, char *);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif	/* __PKG_PKGINSTALL_H__ */
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/usr/src/cmd/svr4pkg/pkginstall/pkgvolume.c	Fri Jun 05 10:28:40 2009 -0400
@@ -0,0 +1,89 @@
+/*
+ * 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
+ * or http://www.opensolaris.org/os/licensing.
+ * 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 2006 Sun Microsystems, Inc.  All rights reserved.
+ * Use is subject to license terms.
+ */
+
+/* Copyright (c) 1984, 1986, 1987, 1988, 1989 AT&T */
+/* All Rights Reserved */
+
+
+#include <stdio.h>
+#include <limits.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <string.h>
+#include <sys/mount.h>
+#include <pkgdev.h>
+#include <locale.h>
+#include <libintl.h>
+#include <pkglib.h>
+#include <libadm.h>
+#include <libinst.h>
+
+extern char	instdir[], pkgbin[];
+
+void
+pkgvolume(struct pkgdev *devp, char *pkg, int part, int nparts)
+{
+	static int	cpart = 0;
+	char	path[PATH_MAX];
+	int	n;
+
+	if (devp->cdevice)
+		return;
+	if (cpart == part)
+		return;
+	cpart = part;
+
+	if (part == 1) {
+		if (ckvolseq(instdir, 1, nparts)) {
+			progerr(gettext("corrupt directory structure"));
+			quit(99);
+		}
+		cpart = 1;
+		return;
+	}
+
+	if (devp->mount == NULL) {
+		if (ckvolseq(instdir, part, nparts)) {
+			progerr(gettext("corrupt directory structure"));
+			quit(99);
+		}
+		return;
+	}
+
+	for (;;) {
+		(void) chdir("/");
+		if (n = pkgumount(devp)) {
+			progerr(gettext("attempt to unmount <%s> failed (%d)"),
+				devp->bdevice, n);
+			quit(99);
+		}
+		if (n = pkgmount(devp, pkg, part, nparts, 1))
+			quit(n);
+		(void) sprintf(path, "%s/%s", devp->dirname, pkg);
+		if (ckvolseq(path, part, nparts) == 0)
+			break;
+	}
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/usr/src/cmd/svr4pkg/pkginstall/predepend.c	Fri Jun 05 10:28:40 2009 -0400
@@ -0,0 +1,89 @@
+/*
+ * 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
+ * or http://www.opensolaris.org/os/licensing.
+ * 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 1993 Sun Microsystems, Inc.  All rights reserved.
+ * Use is subject to license terms.
+ */
+
+/* Copyright (c) 1984, 1986, 1987, 1988, 1989 AT&T */
+/* All Rights Reserved */
+
+
+#include <stdio.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <string.h>
+#include <limits.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <pkglocs.h>
+#include <locale.h>
+#include <libintl.h>
+#include <pkglib.h>
+#include "libadm.h"
+
+extern char	*pkgname, pkgloc[];
+extern int	warnflag;
+
+#define	ERR_RMLINK	"unable to remove options file <%s>"
+#define	ERR_SYMLINK	"unable to create symbloic link from <%s> to <%s>"
+#define	ERR_PREDEPEND	"unable to create predepend file <%s>"
+
+void
+predepend(char *oldpkg)
+{
+	FILE	*fp;
+	char	path[PATH_MAX];
+	char	spath[PATH_MAX];
+	struct stat statbuf;
+
+	oldpkg = strtok(oldpkg, " \t\n");
+	if (oldpkg == NULL)
+		return;
+
+	(void) sprintf(path, "%s/predepend", pkgloc);
+	if ((fp = fopen(path, "w")) == NULL) {
+		progerr(gettext(ERR_PREDEPEND), path);
+		warnflag++;
+		return;
+	}
+	(void) fprintf(fp, "%s\n", pkgname);
+	(void) fclose(fp);
+
+	do {
+		(void) sprintf(spath, "%s/%s.name", get_PKGOLD(), oldpkg);
+		if (lstat(spath, &statbuf) == 0) {
+			/* options file already exists */
+			if (statbuf.st_mode & S_IFLNK) {
+				/* remove current link */
+				if (unlink(spath)) {
+					progerr(gettext(ERR_RMLINK), spath);
+					warnflag++;
+				}
+			}
+		}
+		if (symlink(path, spath)) {
+			progerr(gettext(ERR_SYMLINK), path, spath);
+			warnflag++;
+		}
+	} while (oldpkg = strtok(NULL, " \t\n"));
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/usr/src/cmd/svr4pkg/pkginstall/quit.c	Fri Jun 05 10:28:40 2009 -0400
@@ -0,0 +1,521 @@
+/*
+ * 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
+ * or http://www.opensolaris.org/os/licensing.
+ * 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 2005 Sun Microsystems, Inc.  All rights reserved.
+ * Use is subject to license terms.
+ */
+
+/* Copyright (c) 1984, 1986, 1987, 1988, 1989 AT&T */
+/* All Rights Reserved */
+
+
+#include <stdio.h>
+#include <string.h>
+#include <signal.h>
+#include <sys/utsname.h>
+#include <limits.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <pkgdev.h>
+#include <pkglocs.h>
+#include <locale.h>
+#include <libintl.h>
+#include <errno.h>
+#include <pkglib.h>
+#include "install.h"
+#include "dryrun.h"
+#include "libadm.h"
+#include "libinst.h"
+#include "pkginstall.h"
+#include "messages.h"
+
+/* main.c */
+extern char		*pkgdrtarg;
+extern struct cfextra	**extlist;
+
+extern struct	admin adm;
+extern struct	pkgdev pkgdev;	/* holds info about the installation device */
+
+extern int	dparts;
+extern int	dreboot;	/* != 0 if reboot required after installation */
+extern int	failflag;	/* != 0 if fatal error has occurred (1) */
+extern int	ireboot;	/* != 0 if immediate reboot required */
+extern int	warnflag;	/* != 0 if non-fatal error has occurred (2) */
+
+extern char	tmpdir[];
+extern char	pkgloc[];
+extern char	pkgloc_sav[];
+extern char	*msgtext;
+extern char	*pkginst;
+extern char	*pkgname;
+extern char	saveSpoolInstallDir[]; /* pkginstall/main.c */
+
+/*
+ * exported functions
+ */
+
+void		quit(int retcode);
+void		quitSetZoneName(char *a_zoneName);
+sighdlrFunc_t	*quitGetTrapHandler(void);
+
+/*
+ * forward declarations
+ */
+
+static void		trap(int signo);
+static void		mailmsg(int retcode);
+static void		quitmsg(int retcode);
+
+static boolean_t	silentExit = B_FALSE;
+static boolean_t	pkgaskFlag = B_FALSE;
+static boolean_t	installStarted = B_FALSE;
+static boolean_t	updatingExistingPackage = B_FALSE;
+
+static char		*dstreamTempDir = (char *)NULL;
+static char		*zoneName = (char *)NULL;
+static int		includeZonename = 0;
+static int		trapEntered = 0;
+
+/*
+ * *****************************************************************************
+ * global external (public) functions
+ * *****************************************************************************
+ */
+
+/*
+ * Name:	quitGetTrapHandler
+ * Description:	return address of this modules "signal trap" handler
+ * Arguments:	void
+ * Returns:	sighdlrFunc_t
+ *			The address of the trap handler that can be passed to
+ *			the signal() type system calls
+ */
+
+sighdlrFunc_t *
+quitGetTrapHandler(void)
+{
+	return (&trap);
+}
+
+/*
+ * Name:	quitSetZoneName
+ * Description:	set the zone name the program is running in
+ * Arguments:	a_zoneName - pointer to string representing the name of the zone
+ *			that the program is running in
+ * Returns:	void
+ */
+
+void
+quitSetZoneName(char *a_zoneName)
+{
+	zoneName = a_zoneName;
+	if ((zoneName == (char *)NULL || *zoneName == '\0')) {
+		includeZonename = 0;
+	} else {
+		includeZonename = 1;
+	}
+}
+
+/*
+ * Name:	quitSetDstreamTmpdir
+ * Description:	set the name of a temporary directory that contains package
+ *		streams to be removed when quit() is called
+ * Arguments:	a_dstreamTempDir - pointer to string representing the path
+ *			to the temporary directory to remove when quit()
+ *			is called
+ * Returns:	void
+ */
+
+void
+quitSetDstreamTmpdir(char *a_dstreamTempDir)
+{
+	dstreamTempDir = a_dstreamTempDir;
+}
+
+/*
+ * Name:	quitSetUpdatingExisting
+ * Description:	set the "updating existing" flag - used in conjunction
+ *		with the "install started" flag to determine the type
+ *		of cleanup to be done when quit() is called
+ * Arguments:	a_updatingExistingPackage - indicates whether or not existing
+ *			packages are being updated (B_TRUE) or new packages
+ *			are being installed (B_FALSE)
+ * Returns:	void
+ */
+
+void
+quitSetUpdatingExisting(boolean_t a_updatingExistingPackage)
+{
+	updatingExistingPackage = a_updatingExistingPackage;
+}
+
+/*
+ * Name:	quitSetInstallStarted
+ * Description:	set the "install started" flag - used in conjunction
+ *		with the "updating existing" flag to determine the type
+ *		of cleanup to be done when quit() is called, and the
+ *		type of message to be output for the "reason" why quit()
+ *		was called
+ * Arguments:	a_installStarted - indicates whether or not installation
+ *			has started
+ * Returns:	void
+ */
+
+void
+quitSetInstallStarted(boolean_t a_installStarted)
+{
+	installStarted = a_installStarted;
+}
+
+/*
+ * Name:	quitSetPkgask
+ * Description:	set the "pkgask is being run" flag - used to determine
+ *		the type of message to be output for the "reason" why
+ *		quit() was called
+ * Arguments:	a_pkgaskflag - indicates whether or not pkgask is being run
+ * Returns:	void
+ */
+
+void
+quitSetPkgask(boolean_t a_pkgaskFlag)
+{
+	pkgaskFlag = a_pkgaskFlag;
+}
+
+/*
+ * Name:	quitSetSilentExit
+ * Description:	set the "silent exit" flag - if silent exit is TRUE, then
+ *		no messages are output by quit() when it is called
+ * Arguments:	a_silentExit - indicates whether or not silent exit is set
+ * Returns:	void
+ */
+
+void
+quitSetSilentExit(boolean_t a_silentExit)
+{
+	silentExit = a_silentExit;
+}
+
+/*
+ * Name:	quit
+ * Description:	cleanup and exit
+ * Arguments:	a_retcode - the code to use to determine final exit status;
+ *			if this is NOT "99" and if a "ckreturnFunc" is
+ *			set, then that function is called with a_retcode
+ *			to set the final exit status.
+ *		Valid values are:
+ *		0 - success
+ *		1 - package operation failed (fatal error)
+ *		2 - non-fatal error (warning)
+ *		3 - user selected quit (operation interrupted)
+ *		4 - admin settings prevented operation
+ *		5 - interaction required and -n (non-interactive) specified
+ *		"10" is added to indicate "immediate reboot required"
+ *		"20" is be added to indicate "reboot after install required"
+ *		99 - do not interpret the code - just exit "99"
+ * Returns:	<<this function does not return - calls exit()>>
+ */
+
+void
+quit(int retcode)
+{
+	char		orig_pkginfo_path[PATH_MAX];
+	char		pkginfo_path[PATH_MAX];
+
+	/* disable interrupts */
+
+	(void) signal(SIGINT, SIG_IGN);
+	(void) signal(SIGHUP, SIG_IGN);
+
+	/* process return code if not quit(99) */
+
+	if (retcode != 99) {
+		if ((retcode % 10) == 0) {
+			if (failflag) {
+				retcode += 1;
+			} else if (warnflag) {
+				retcode += 2;
+			}
+		}
+
+		if (ireboot) {
+			retcode = (retcode % 10) + 20;
+		}
+		if (dreboot) {
+			retcode = (retcode % 10) + 10;
+		}
+	}
+
+	/* if set remove dstream temporary directory */
+
+	if (dstreamTempDir != (char *)NULL) {
+		echoDebug(DBG_REMOVING_DSTREAM_TMPDIR, dstreamTempDir);
+		(void) rrmdir(dstreamTempDir);
+		dstreamTempDir = (char *)NULL;
+	}
+
+	/* If we're in dryrun mode, write out the dryrun file(s). */
+	if (in_dryrun_mode()) {
+		char exit_msg[200];
+		set_dr_info(EXITCODE, retcode);
+		if (failflag || warnflag) {
+			set_dr_exitmsg(msgtext);
+		} else {
+			/* LINTED variable format specified */
+			(void) snprintf(exit_msg, sizeof (exit_msg),
+				qreason(1, retcode, installStarted,
+					includeZonename),
+					(pkginst ? pkginst : "unknown"),
+					zoneName);
+			set_dr_exitmsg(exit_msg);
+		}
+
+		write_dryrun_file(extlist);
+		ptext(stderr, MSG_DRYRUN_DONE);
+		ptext(stderr, MSG_NOCHANGE);
+
+		if (tmpdir[0] != NULL)
+			(void) rrmdir(tmpdir);
+
+	} else {
+		/* fix bug #1082589 that deletes root file */
+		if (tmpdir[0] != NULL) {
+			(void) rrmdir(tmpdir);
+		}
+
+		/* send mail to appropriate user list */
+		mailmsg(retcode);
+
+		/* display message about this installation */
+		quitmsg(retcode);
+	}
+
+	/*
+	 * In the event that this quit() was called prior to completion of
+	 * the task, do an unlockinst() just in case.
+	 */
+	unlockinst();
+
+	/* Unmount anything that's our responsibility. */
+	(void) unmount_client();
+
+	/*
+	 * No need to umount device since calling process
+	 * was responsible for original mount
+	 */
+
+	if (!updatingExistingPackage) {
+		if (!installStarted && pkgloc[0]) {
+			/*
+			 * install not yet started; if package install
+			 * location is defined, remove the package.
+			 */
+			echoDebug(DBG_QUIT_REMOVING_PKGDIR, pkgloc);
+
+			(void) chdir("/");
+			if (pkgloc[0]) {
+				(void) rrmdir(pkgloc);
+			}
+		}
+	} else {
+		if (!installStarted) {
+			/*
+			 * If we haven't started, but have already done
+			 * the <PKGINST>/install directory rename, then
+			 * remove the new <PKGINST>/install directory
+			 * and rename <PKGINST>/install.save back to
+			 * <PKGINST>/install.
+			 */
+			if (pkgloc_sav[0] && !access(pkgloc_sav, F_OK)) {
+				if (pkgloc[0] && !access(pkgloc, F_OK))
+					(void) rrmdir(pkgloc);
+				if (rename(pkgloc_sav, pkgloc) == -1) {
+					progerr(ERR_PACKAGEBINREN,
+						pkgloc_sav, pkgloc);
+				}
+			}
+		} else {
+			if (pkgloc_sav[0] && !access(pkgloc_sav, F_OK)) {
+				echoDebug(DBG_QUIT_REMOVING_PKGSAV, pkgloc_sav);
+				(void) rrmdir(pkgloc_sav);
+			}
+		}
+
+		if (isPatchUpdate()) {
+			if (pkgloc[0] && !access(pkgloc, F_OK) &&
+				!access(saveSpoolInstallDir, F_OK)) {
+				/*
+				 * Copy the pkginfo file to the pspool
+				 * directory. This propagates patch
+				 * info to the patched pkg in the local
+				 * zone.
+				 */
+				(void) snprintf(orig_pkginfo_path,
+					sizeof (orig_pkginfo_path),
+					"%s/%s/%s", get_PKGLOC(),
+					pkginst, PKGINFO);
+
+				(void) snprintf(pkginfo_path,
+					sizeof (pkginfo_path), "%s/%s",
+					saveSpoolInstallDir, PKGINFO);
+
+				if (cppath(MODE_SET|DIR_DISPLAY,
+					orig_pkginfo_path, pkginfo_path,
+					0644)) {
+					progerr(ERR_PKGINFO_COPY,
+							orig_pkginfo_path,
+							pkginfo_path);
+				}
+			}
+		}
+	}
+
+	/*
+	 * pkginst can be null if an administration setting doesn't all
+	 * the package to be installed. Make sure pkginst exeists before
+	 * updating the DB
+	 */
+
+	if (dparts > 0)
+		ds_skiptoend(pkgdev.cdevice);
+	(void) ds_close(1);
+
+	/* Free the filesystem table. */
+	fs_tab_free();
+
+	/* Free the package information lists. */
+	pinfo_free();
+
+	/* Free all stragglers. */
+	bl_free(BL_ALL);
+	(void) pathdup(NULL);
+
+	/* Free regfiles. */
+	regfiles_free();
+
+	/* final exit debugging message */
+
+	echoDebug(DBG_EXIT_WITH_CODE, retcode);
+
+	exit(retcode);
+	/*NOTREACHED*/
+}
+
+/*
+ * *****************************************************************************
+ * static internal (private) functions
+ * *****************************************************************************
+ */
+
+static void
+quitmsg(int retcode)
+{
+	if (silentExit == B_TRUE) {
+		return;
+	}
+
+	(void) putc('\n', stderr);
+	if (pkgaskFlag) {
+		ptext(stderr, qreason(0, retcode, installStarted,
+			includeZonename), zoneName);
+	} else if (pkginst) {
+		ptext(stderr, qreason(1, retcode, installStarted,
+			includeZonename), pkginst, zoneName);
+	}
+
+	if (retcode && !installStarted) {
+		ptext(stderr, MSG_NOCHANGE);
+	}
+}
+
+static void
+mailmsg(int retcode)
+{
+	struct utsname utsbuf;
+	FILE	*pp;
+	char	*cmd;
+	size_t	len;
+
+	if (silentExit == B_TRUE) {
+		return;
+	}
+
+	if (!installStarted || pkgaskFlag || (adm.mail == NULL)) {
+		return;
+	}
+
+	len = strlen(adm.mail) + sizeof (MAILCMD) + 2;
+	cmd = calloc(len, sizeof (char));
+	if (cmd == NULL) {
+		logerr(WRN_NOMAIL);
+		return;
+	}
+
+	(void) snprintf(cmd, len, "%s %s", MAILCMD, adm.mail);
+	if ((pp = popen(cmd, "w")) == NULL) {
+		logerr(WRN_NOMAIL);
+		return;
+	}
+
+	if (msgtext)
+		ptext(pp, msgtext);
+
+	(void) strcpy(utsbuf.nodename, MSG_NODENAME);
+	(void) uname(&utsbuf);
+
+	ptext(pp, qreason(2, retcode, installStarted, includeZonename),
+		pkgname, utsbuf.nodename, pkginst, zoneName);
+
+	if (pclose(pp)) {
+		logerr(WRN_FLMAIL);
+	}
+}
+
+/*
+ * Name:	trap
+ * Description:	signal handler connected via quitGetTrapHandler()
+ * Arguments:	signo - [RO, *RO] - (int)
+ *			Integer representing the signal that caused the trap
+ *			to this function to occur
+ * Returns:	<< NONE >>
+ * NOTE:	This function exits the program after doing mandatory cleanup.
+ * NOTE:	Even though quit() should NOT return, there is a call to _exit()
+ *		put after each call to quit() just in case quit() ever returned
+ *		by mistake.
+ */
+
+static void
+trap(int signo)
+{
+	/* prevent reentrance */
+
+	if (trapEntered++ != 0) {
+		return;
+	}
+
+	if ((signo == SIGINT) || (signo == SIGHUP)) {
+		quit(3);
+		_exit(3);
+	}
+	quit(1);
+	_exit(1);
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/usr/src/cmd/svr4pkg/pkginstall/reqexec.c	Fri Jun 05 10:28:40 2009 -0400
@@ -0,0 +1,392 @@
+/*
+ * 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
+ * or http://www.opensolaris.org/os/licensing.
+ * 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 2005 Sun Microsystems, Inc.  All rights reserved.
+ * Use is subject to license terms.
+ */
+
+/* Copyright (c) 1984, 1986, 1987, 1988, 1989 AT&T */
+/* All Rights Reserved */
+
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <sys/wait.h>
+#include <unistd.h>
+#include <string.h>
+#include <fcntl.h>	/* creat() declaration */
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <pwd.h>
+#include <grp.h>
+#include <locale.h>
+#include <libintl.h>
+#include <pkglib.h>
+#include "install.h"
+#include "libadm.h"
+#include "libinst.h"
+#include "pkginstall.h"
+#include "messages.h"
+
+extern char	tmpdir[], instdir[];
+extern int	pkgverbose;
+
+static int	do_exec(int update, char *script, char *output,
+			char *inport, char *alt_user);
+static char	path[PATH_MAX];
+static char	*resppath = NULL;
+static int	fd;
+static int	respfile_defined = 0;
+static int	respfile_ro = 0;	/* read only resp file */
+
+/*
+ * This informs the calling routine if a read-only response file has been
+ * provided on the command line.
+ */
+int
+rdonly_respfile(void)
+{
+	return (respfile_ro);
+}
+
+int
+is_a_respfile(void)
+{
+	return (respfile_defined);
+}
+
+/*
+ * This function creates a working copy of the checkinstall script.
+ * This is needed in situations where the packages parent directories modes
+ * are set too restrictively, i.e. 700.
+ *
+ * Returns: A pointer to the location of the copied checkinstall
+ * script or NULL
+ */
+
+char *
+dup_chkinstall(char *script)
+{
+	char	*dstpath;
+	size_t	dstpathLen;
+	int	r;
+static	char	*tmpname = "checkinstallXXXXXX";
+
+	/* determine length for destination script path */
+
+	dstpathLen = strlen(tmpdir) + strlen(tmpname) + 3;
+
+	/* allocate storage to hold destination script path */
+
+	dstpath = (char *)malloc(dstpathLen);
+	if (dstpath == (char *)NULL) {
+		return ((char *)NULL);
+	}
+
+	/* create destination script path */
+
+	(void) snprintf(dstpath, dstpathLen, "%s/%s", tmpdir, tmpname);
+
+	if (mktemp(dstpath) == NULL) {
+		progerr(ERR_TMPFILE_CHK);
+		(void) free(dstpath);
+		return (NULL);
+	}
+
+	/* make copy of script */
+
+	r = copyf(script, dstpath, (time_t)0);
+	if (r != 0) {
+		progerr(ERR_CANNOT_COPY, script, dstpath);
+		return (NULL);
+	}
+
+	/* Make the copy of the script readable by all */
+
+	if (chmod(dstpath, 0444) != 0) {
+		progerr(ERR_CHMOD_CHK);
+		(void) free(dstpath);
+		return (NULL);
+	}
+
+	return (dstpath);
+}
+
+/*
+ * This function creates a temporary working copy of a read-only response
+ * file. It changes the resppath pointer to point to the working copy.
+ */
+static int
+dup_respfile(void)
+{
+	char	tpath[PATH_MAX];
+	int	r;
+
+	(void) strlcpy(tpath, path, sizeof (tpath));
+
+	(void) snprintf(path, sizeof (path), "%s/respXXXXXX", tmpdir);
+
+	resppath = mktemp(path);
+	if (resppath == NULL) {
+		progerr(ERR_TMPRESP);
+		return (99);
+	}
+
+	/* Copy the contents of the user's response file to the working copy. */
+
+	r = copyf(tpath, resppath, (time_t)0);
+	if (r != 0) {
+		progerr(ERR_NORESPCOPY, tpath, resppath);
+		return (99);
+	}
+
+	/*
+	 * Make it writable by the non-privileged installation user-id,
+	 * but readable by the world.
+	 */
+
+	if (chmod(resppath, 0644) != 0) {
+		progerr(ERR_CHMOD, resppath);
+		return (99);
+	}
+
+	respfile_ro = 0;
+
+	return (0);
+}
+
+/*
+ * This function establishes the response file passed on the command line if
+ * it's called with a valid string. If called with NULL, it checks to see if
+ * there's a response file already. If there isn't, it creates a temporary.
+ */
+int
+set_respfile(char *respfile, char *pkginst, int resp_stat)
+{
+	if (respfile == NULL && !respfile_defined) {
+		/* A temporary response file needs to be constructed. */
+		(void) snprintf(path, sizeof (path), "%s/respXXXXXX", tmpdir);
+		resppath = mktemp(path);
+		if (resppath == NULL) {
+			progerr(ERR_TMPRESP);
+			return (99);
+		}
+	} else {
+		/* OK, we're being passed a response file or directory. */
+		if (isdir(respfile) == 0) {
+			(void) snprintf(path, sizeof (path),
+				"%s/%s", respfile, pkginst);
+		} else {
+			(void) strlcpy(path, respfile, sizeof (path));
+		}
+
+		resppath = path;
+		respfile_ro = resp_stat;
+	}
+
+	respfile_defined++;
+
+	return (0);
+}
+
+/* This exposes the working response file. */
+char *
+get_respfile(void)
+{
+	return (resppath);
+}
+
+/*
+ * Execute the request script if present assuming the response file
+ * isn't read only.
+ */
+int
+reqexec(int update, char *script, int non_abi_scripts,
+	boolean_t enable_root_user)
+{
+	char	*req_user;
+
+	/*
+	 * determine which alternative user to execute the request script as
+	 * if the default user "install" is not defined.
+	 */
+
+	if (enable_root_user == B_TRUE) {
+		/* use the root user */
+		req_user = CHK_USER_ROOT;
+	} else if (non_abi_scripts != 0) {
+		/* non-compliant package user */
+		req_user = CHK_USER_NON;
+	} else {
+		/* standard non-privileged user */
+		req_user = CHK_USER_ALT;
+	}
+
+	/*
+	 * If we can't get to the the script or the response file, skip this.
+	 */
+	if (access(script, F_OK) != 0 || respfile_ro)
+		return (0);
+
+	/* No interact means no interact. */
+	if (echoGetFlag() == B_FALSE) {
+		ptext(stderr, ERR_INTR);
+		return (5);
+	}
+
+	/* If there's no response file, create one. */
+	if (!respfile_defined)
+		if (set_respfile(NULL, NULL, 0))
+			return (99);
+
+	/* Clear out the old response file (if there is one). */
+	if ((access(resppath, F_OK) == 0) && unlink(resppath)) {
+		progerr(ERR_RMRESP, resppath);
+		return (99);
+	}
+
+	/*
+	 * Create a zero length response file which is only writable
+	 * by the non-privileged installation user-id, but is readable
+	 * by the world
+	 */
+	if ((fd = open(resppath, O_WRONLY|O_CREAT|O_TRUNC|O_EXCL, 0644)) < 0) {
+		progerr(ERR_CRERESP, resppath);
+		return (99);
+	}
+	(void) close(fd);
+
+	return (do_exec(update, script, resppath, REQ_STDIN, req_user));
+}
+
+int
+chkexec(int update, char *script)
+{
+	/*
+	 * If we're up against a read-only response file from the command
+	 * line. Create a working copy.
+	 */
+	if (respfile_ro) {
+		if (dup_respfile())
+
+			return (99);
+
+		/* Make sure we can get to it. */
+		if ((access(resppath, F_OK) != 0)) {
+			progerr(ERR_ACCRESP, resppath);
+			return (7);
+		}
+	}
+
+	/* If there's no response file, create a fresh one. */
+	else if (!respfile_defined) {
+		if (set_respfile(NULL, NULL, 0))
+			return (99);
+
+		/*
+		 * create a zero length response file which is only writable
+		 * by the non-priveledged installation user-id, but is readable
+		 * by the world
+		 */
+		fd = open(resppath, O_WRONLY|O_CREAT|O_TRUNC|O_EXCL, 0644);
+		if (fd < 0) {
+			progerr(ERR_CRERESP, resppath);
+			return (99);
+		}
+		(void) close(fd);
+	}
+
+	return (do_exec(update, script, resppath, CHK_STDIN, CHK_USER_ALT));
+}
+
+static int
+do_exec(int update, char *script, char *output, char *inport, char *alt_user)
+{
+	char		*gname;
+	char		*tmp_script;
+	char		*uname;
+	gid_t		instgid;
+	int		retcode = 0;
+	struct group	*grp;
+	struct passwd	*pwp;
+	uid_t		instuid;
+
+	/*
+	 * Determine which user to run the request script as:
+	 * - if CHK_USER is a valid user, run the script as CHK_USER
+	 * - otherwise, if alt_user is a valid user, run the script
+	 * -- as alt_user
+	 * - otherwise, output an error message and return failure
+	 */
+
+	if ((pwp = getpwnam(CHK_USER)) != (struct passwd *)NULL) {
+		instuid = pwp->pw_uid;
+		uname = CHK_USER;
+	} else if ((pwp = getpwnam(alt_user)) != (struct passwd *)NULL) {
+		instuid = pwp->pw_uid;
+		uname = alt_user;
+	} else {
+		ptext(stderr, ERR_BADUSER, CHK_USER, CHK_USER_ALT);
+		return (1);
+	}
+
+	/*
+	 * Determine which group to run the request script as:
+	 * - If CHK_GRP is a valid group, run the script as CHK_GRP
+	 * - otherwise, assume group "1" user "other"
+	 */
+
+	if ((grp = getgrnam(CHK_GRP)) != (struct group *)NULL) {
+		instgid = grp->gr_gid;
+		gname = CHK_GRP;
+	} else {
+		instgid = (gid_t)1;	/* "other" group id */
+		gname = "other";	/* "other" group name */
+	}
+
+	echoDebug(DBG_DO_EXEC_REQUEST_USER, script, output, uname, instuid,
+		gname, instgid);
+
+	(void) chown(output, instuid, instgid);
+
+	/*
+	 * Copy the checkinstall script to tmpdir in case parent directories
+	 * are restrictive, i.e. 700. Only do this for non updates, i.e.
+	 * package installs and not patch package installs.
+	 */
+	if (update) {
+		tmp_script = strdup(script);
+	} else if ((tmp_script = dup_chkinstall(script)) == NULL) {
+		/* Use the original checkinstall script */
+		tmp_script = strdup(script);
+	}
+
+	if (pkgverbose)
+		retcode = pkgexecl(inport, CHK_STDOUT, uname, CHK_GRP, SHELL,
+		    "-x", tmp_script, output, NULL);
+	else
+		retcode = pkgexecl(inport, CHK_STDOUT, uname, CHK_GRP, SHELL,
+		    tmp_script, output, NULL);
+
+	free(tmp_script);
+	return (retcode);
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/usr/src/cmd/svr4pkg/pkginstall/sortmap.c	Fri Jun 05 10:28:40 2009 -0400
@@ -0,0 +1,170 @@
+/*
+ * 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
+ * or http://www.opensolaris.org/os/licensing.
+ * 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 2006 Sun Microsystems, Inc.  All rights reserved.
+ * Use is subject to license terms.
+ */
+
+/* Copyright (c) 1984, 1986, 1987, 1988, 1989 AT&T */
+/* All Rights Reserved */
+
+
+/*
+ * This module constructs a list of entries from the pkgmap associated
+ * with this package. When finished, this list is sorted in alphabetical
+ * order and an accompanying structure list, mergstat, provides
+ * information about how these new files merge with existing files
+ * already on the system.
+ */
+
+#include <stdio.h>
+#include <string.h>
+#include <limits.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <sys/types.h>
+#include <pkgstrct.h>
+#include <pkglocs.h>
+#include <locale.h>
+#include <libintl.h>
+#include <install.h>
+#include <pkglib.h>
+#include <libadm.h>
+#include <libinst.h>
+
+/* libinst/ocfile.c */
+extern int	dbchg;
+
+static int	client_refer(struct cfextra **ext);
+static int	server_refer(struct cfextra **ext);
+
+int
+sortmap(struct cfextra ***extlist, VFP_T *pkgmapVfp,
+    VFP_T *mapvfp, VFP_T *tmpvfp, char *a_zoneName)
+{
+	int	i, n, nparts;
+	char *db_mrg = "unable to merge package and system information";
+
+	if (a_zoneName == (char *)NULL) {
+		echo(gettext("## Processing package information."));
+	} else {
+		echo(gettext("## Processing package information in zone <%s>."),
+			a_zoneName);
+	}
+
+	/*
+	 * The following instruction puts the client-relative basedir
+	 * into the environment iff it's a relocatable package and
+	 * we're installing to a client. Otherwise, it uses the regular
+	 * basedir. The only reason for this is so that mappath() upon
+	 * finding $BASEDIR in a path will properly resolve it to the
+	 * client-relative path. This way eval_path() can properly
+	 * construct the server-relative path.
+	 */
+	if (is_relocatable() && is_an_inst_root())
+		putparam("BASEDIR", get_info_basedir());
+
+	/*
+	 * read the pkgmap provided by this package into
+	 * memory; map parameters specified in the pathname
+	 * and sort in memory by pathname
+	 */
+
+	vfpRewind(pkgmapVfp);		/* rewind input file */
+
+	*extlist = pkgobjmap(pkgmapVfp, 2, NULL);
+
+	if (*extlist == NULL) {
+		progerr(gettext("unable to process pkgmap"));
+		quit(99);
+	}
+
+	/* Make all paths client-relative if necessary. */
+	if (is_an_inst_root()) {
+		(void) client_refer(*extlist);
+	}
+
+	if (a_zoneName == (char *)NULL) {
+		echo(gettext("## Processing system information."));
+	} else {
+		echo(gettext("## Processing system information in zone <%s>."),
+			a_zoneName);
+	}
+
+	/*
+	 * calculate the number of parts in this package
+	 * by locating the entry with the largest "volno"
+	 * associated with it
+	 */
+	nparts = 0;
+	if (is_depend_pkginfo_DB() == B_FALSE) {
+		for (i = 0; (*extlist)[i]; i++) {
+			n = (*extlist)[i]->cf_ent.volno;
+			if (n > nparts)
+				nparts = n;
+		}
+
+		vfpTruncate(tmpvfp);
+
+		dbchg = pkgdbmerg(mapvfp, tmpvfp, *extlist, 60);
+		if (dbchg < 0) {
+			progerr(gettext(db_mrg));
+			quit(99);
+		}
+	}
+
+	/* Restore the original BASEDIR. */
+	if (is_relocatable() && is_an_inst_root())
+		putparam("BASEDIR", get_basedir());
+
+	if (is_an_inst_root()) {
+		(void) server_refer(*extlist);
+	}
+
+	return (nparts);
+}
+
+static int
+client_refer(struct cfextra **ext)
+{
+	int count;
+
+	for (count = 0; ext[count] != (struct cfextra *)NULL; count++) {
+		ext[count]->cf_ent.path = ext[count]->client_path;
+		ext[count]->cf_ent.ainfo.local = ext[count]->client_local;
+	}
+
+	return (1);
+}
+
+static int
+server_refer(struct cfextra **ext)
+{
+	int count;
+
+	for (count = 0; ext[count] != (struct cfextra *)NULL; count++) {
+		ext[count]->cf_ent.path = ext[count]->server_path;
+		ext[count]->cf_ent.ainfo.local = ext[count]->server_local;
+	}
+
+	return (1);
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/usr/src/cmd/svr4pkg/pkgmk/Makefile	Fri Jun 05 10:28:40 2009 -0400
@@ -0,0 +1,45 @@
+#
+# 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
+# or http://www.opensolaris.org/os/licensing.
+# 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 2009 Sun Microsystems, Inc.  All rights reserved.
+# Use is subject to license terms.
+#
+
+PROG=		pkgmk
+
+OBJS=		main.o		\
+		mkpkgmap.o	\
+		quit.o		\
+		splpkgmap.o
+
+include $(SRC)/cmd/svr4pkg/Makefile.svr4pkg
+
+LDLIBS +=	-ll -lpkg -linstzones -ladm
+
+
+.KEEP_STATE:
+
+all:		$(PROG)
+
+install:        all $(ROOTPROG)
+
+include $(SRC)/cmd/svr4pkg/Makefile.svr4pkg.targ
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/usr/src/cmd/svr4pkg/pkgmk/main.c	Fri Jun 05 10:28:40 2009 -0400
@@ -0,0 +1,985 @@
+/*
+ * 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
+ * or http://www.opensolaris.org/os/licensing.
+ * 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 2009 Sun Microsystems, Inc.  All rights reserved.
+ * Use is subject to license terms.
+ */
+
+/* Copyright (c) 1984, 1986, 1987, 1988, 1989 AT&T */
+/* All Rights Reserved */
+
+
+#include <stdio.h>
+#include <string.h>
+#include <signal.h>
+#include <errno.h>
+#include <malloc.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <time.h>
+#include <fcntl.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <sys/param.h>
+#include <ctype.h>
+#include <sys/mman.h>
+#include <sys/sysmacros.h>
+#include <strings.h>
+#include <pkgstrct.h>
+#include <pkgdev.h>
+#include <pkginfo.h>
+#include <pkglocs.h>
+#include <locale.h>
+#include <libintl.h>
+#include <sys/statvfs.h>
+#include <sys/utsname.h>
+#include <instzones_api.h>
+#include <pkglib.h>
+#include <libadm.h>
+#include <libinst.h>
+
+extern char	**environ, *pkgdir;
+
+/* mkpkgmap.c */
+extern int	mkpkgmap(char *outfile, char *protofile, char **cmdparam);
+/* splpkgmap.c */
+extern int	splpkgmap(struct cfent **eptlist, unsigned int eptnum,
+    char *order[], ulong_t bsize, ulong_t frsize, fsblkcnt_t *plimit,
+    fsfilcnt_t *pilimit, fsblkcnt_t *pllimit);
+/* scriptvfy.c */
+extern int	checkscripts(char *inst_dir, int silent);
+
+/* libpkg/gpkgmap.c */
+extern void	setmapmode(int mode_no);
+
+static boolean_t valid_zone_attr(struct cfent **eptlist);
+
+#define	MALSIZ	16
+#define	NROOT	8
+#define	SPOOLDEV	"spool"
+
+#define	MSG_PROTOTYPE	"## Building pkgmap from package prototype file.\n"
+#define	MSG_PKGINFO	"## Processing pkginfo file.\n"
+#define	MSG_VOLUMIZE	"## Attempting to volumize %d entries in pkgmap.\n"
+#define	MSG_PACKAGE1	"## Packaging one part.\n"
+#define	MSG_PACKAGEM	"## Packaging %d parts.\n"
+#define	MSG_VALSCRIPTS	"## Validating control scripts.\n"
+
+/* Other problems */
+#define	ERR_MEMORY	"memory allocation failure, errno=%d"
+#define	ERR_NROOT	"too many paths listed with -r option, limit is %d"
+#define	ERR_PKGINST	"invalid package instance identifier <%s>"
+#define	ERR_PKGABRV	"invalid package abbreviation <%s>"
+#define	ERR_BADDEV	"unknown or invalid device specified <%s>"
+#define	ERR_TEMP	"unable to obtain temporary file resources, errno=%d"
+#define	ERR_DSTREAM	"invalid device specified (datastream) <%s>"
+#define	ERR_SPLIT	"unable to volumize package"
+#define	ERR_MKDIR	"unable to make directory <%s>"
+#define	ERR_SYMLINK	"unable to create symbolic link for <%s>"
+#define	ERR_OVERWRITE	"must use -o option to overwrite <%s>"
+#define	ERR_UMOUNT	"unable to unmount device <%s>"
+#define	ERR_NOPKGINFO	"required pkginfo file is not specified in prototype " \
+			"file"
+#define	ERR_RDPKGINFO	"unable to process pkginfo file <%s>"
+#define	ERR_PROTOTYPE	"unable to locate prototype file"
+#define	ERR_STATVFS	"unable to stat filesystem <%s>"
+#define	ERR_WHATVFS	"unable to determine or access output filesystem for " \
+			"device <%s>"
+#define	ERR_DEVICE	"unable to find info for device <%s>"
+#define	ERR_BUILD	"unable to build pkgmap from prototype file"
+#define	ERR_ONEVOL	"other packages found - package must fit on a single " \
+			"volume"
+#define	ERR_NOPARAM	"parameter <%s> is not defined in <%s>"
+#define	ERR_PKGMTCH	"PKG parameter <%s> does not match instance <%s>"
+#define	ERR_NO_PKG_INFOFILE	"unable to open pkginfo file <%s>: %s"
+#define	ERR_ALLZONES_AND_THISZONE	"The package <%s> has <%s> = true " \
+					"and <%s> = true: the package may " \
+					"set either parameter to true, but " \
+					"may not set both parameters to " \
+					"true. NOTE: if the package " \
+					"contains a request script, it is " \
+					"treated as though it has " \
+					"<SUNW_PKG_THISZONE> = true"
+#define	ERR_NO_ALLZONES_AND_HOLLOW	"The package <%s> has <%s> = false " \
+					"and <%s> = true: a hollow package " \
+					"must also be set to install in all " \
+					"zones"
+#define	ERR_PKGINFO_INVALID_OPTION_COMB	"Invalid combinations of zone " \
+					"parameters in pkginfo file"
+
+#define	ERR_USAGE	"usage: %s [options] [VAR=value [VAR=value]] " \
+			"[pkginst]\n" \
+			"   where options may include:\n" \
+			"\t-o\n" \
+			"\t-a arch\n" \
+			"\t-v version\n" \
+			"\t-p pstamp\n" \
+			"\t-l limit\n" \
+			"\t-r rootpath\n" \
+			"\t-b basedir\n" \
+			"\t-d device\n" \
+			"\t-f protofile\n"
+#define	WRN_MISSINGDIR	"WARNING: missing directory entry for <%s>"
+#define	WRN_SETPARAM	"WARNING: parameter <%s> set to \"%s\""
+#define	WRN_CLASSES	"WARNING: unreferenced class <%s> in prototype file"
+
+#define	LINK    1
+
+struct pkgdev pkgdev; 	/* holds info about the installation device */
+int	started;
+char	pkgloc[PATH_MAX];
+char	*basedir;
+char	*root;
+char	*rootlist[NROOT];
+char	*t_pkgmap;
+char	*t_pkginfo;
+
+static struct cfent *svept;
+static char	*protofile,
+		*device;
+static fsblkcnt_t limit = 0;
+static fsblkcnt_t llimit = 0;
+static fsfilcnt_t ilimit = 0;
+static int	overwrite,
+		nflag,
+		sflag;
+static void	ckmissing(char *path, char type);
+static void	outvol(struct cfent **eptlist, unsigned int eptnum, int part,
+			int nparts);
+static void	trap(int n);
+static void	usage(void);
+
+static int	slinkf(char *from, char *to);
+
+int
+main(int argc, char *argv[])
+{
+	struct utsname utsbuf;
+	struct statvfs64 svfsb;
+	struct cfent	**eptlist;
+	FILE	*fp;
+	VFP_T	*vfp;
+	int	c, n, found;
+	int	part, nparts, npkgs, objects;
+	char	buf[MAX_PKG_PARAM_LENGTH];
+	char	temp[MAX_PKG_PARAM_LENGTH];
+	char	param[MAX_PKG_PARAM_LENGTH];
+	char	*pt, *value, *pkginst, *tmpdir, *abi_sym_ptr,
+		**cmdparam;
+	char	*pkgname;
+	char	*pkgvers;
+	char	*pkgarch;
+	char	*pkgcat;
+	void	(*func)();
+	time_t	clock;
+	ulong_t	bsize = 0;
+	ulong_t	frsize = 0;
+	struct cl_attr	**allclass = NULL;
+	struct cl_attr	**order;
+	unsigned int eptnum, i;
+
+	/* initialize locale environment */
+
+	(void) setlocale(LC_ALL, "");
+
+#if !defined(TEXT_DOMAIN)	/* Should be defined by cc -D */
+#define	TEXT_DOMAIN "SYS_TEST"
+#endif
+	(void) textdomain(TEXT_DOMAIN);
+
+	/* initialize program name */
+
+	(void) set_prog_name(argv[0]);
+
+	/* tell spmi zones interface how to access package output functions */
+
+	z_set_output_functions(echo, echoDebug, progerr);
+
+	func = sigset(SIGINT, trap);
+	if (func != SIG_DFL)
+		func = sigset(SIGINT, func);
+	func = sigset(SIGHUP, trap);
+	setmapmode(MAPBUILD);	/* variable binding */
+	if (func != SIG_DFL)
+		func = sigset(SIGHUP, func);
+
+	environ = NULL;
+	while ((c = getopt(argc, argv, "osnp:l:r:b:d:f:a:v:?")) != EOF) {
+		switch (c) {
+		    case 'n':
+			nflag++;
+			break;
+
+		    case 's':
+			sflag++;
+			break;
+
+		    case 'o':
+			overwrite++;
+			break;
+
+		    case 'p':
+			putparam("PSTAMP", optarg);
+			break;
+
+		    case 'l':
+			llimit = strtoull(optarg, NULL, 10);
+			break;
+
+		    case 'r':
+			pt = strtok(optarg, " \t\n, ");
+			n = 0;
+			do {
+				rootlist[n++] = flex_device(pt, 0);
+				if (n >= NROOT) {
+					progerr(gettext(ERR_NROOT), NROOT);
+					quit(1);
+				}
+			} while (pt = strtok(NULL, " \t\n, "));
+			rootlist[n] = NULL;
+			break;
+
+		    case 'b':
+			basedir = optarg;
+			break;
+
+		    case 'f':
+			protofile = optarg;
+			break;
+
+		    case 'd':
+			device = flex_device(optarg, 1);
+			break;
+
+		    case 'a':
+			putparam("ARCH", optarg);
+			break;
+
+		    case 'v':
+			putparam("VERSION", optarg);
+			break;
+
+		    default:
+			usage();
+			/*NOTREACHED*/
+			/*
+			 * Although usage() calls a noreturn function,
+			 * needed to add return (1);  so that main() would
+			 * pass compilation checks. The statement below
+			 * should never be executed.
+			 */
+			return (1);
+		}
+	}
+
+	/*
+	 * Store command line variable assignments for later
+	 * incorporation into the environment.
+	 */
+	cmdparam = &argv[optind];
+
+	/* Skip past equates. */
+	while (argv[optind] && strchr(argv[optind], '='))
+		optind++;
+
+	/* Confirm that the instance name is valid */
+	if ((pkginst = argv[optind]) != NULL) {
+		if (pkgnmchk(pkginst, "all", 0)) {
+			progerr(gettext(ERR_PKGINST), pkginst);
+			quit(1);
+		}
+		argv[optind++] = NULL;
+	}
+	if (optind != argc)
+		usage();
+
+	tmpdir = getenv("TMPDIR");
+	if (tmpdir == NULL)
+		tmpdir = P_tmpdir;
+
+	/* bug id 4244631, not ABI compliant */
+	abi_sym_ptr = getenv("PKG_NONABI_SYMLINKS");
+	if (abi_sym_ptr && (strncasecmp(abi_sym_ptr, "TRUE", 4) == 0)) {
+		set_nonABI_symlinks();
+	}
+
+	if (device == NULL) {
+		device = devattr(SPOOLDEV, "pathname");
+		if (device == NULL) {
+			progerr(gettext(ERR_DEVICE), SPOOLDEV);
+			exit(99);
+		}
+	}
+
+	if (protofile == NULL) {
+		if (access("prototype", 0) == 0)
+			protofile = "prototype";
+		else if (access("Prototype", 0) == 0)
+			protofile = "Prototype";
+		else {
+			progerr(gettext(ERR_PROTOTYPE));
+			quit(1);
+		}
+	}
+
+	if (devtype(device, &pkgdev)) {
+		progerr(gettext(ERR_BADDEV), device);
+		quit(1);
+	}
+	if (pkgdev.norewind) {
+		/* initialize datastream */
+		progerr(gettext(ERR_DSTREAM), device);
+		quit(1);
+	}
+	if (pkgdev.mount) {
+		if (n = pkgmount(&pkgdev, NULL, 0, 0, 1))
+			quit(n);
+	}
+
+	/*
+	 * convert prototype file to a pkgmap, while locating
+	 * package objects in the current environment
+	 */
+	t_pkgmap = tempnam(tmpdir, "tmpmap");
+	if (t_pkgmap == NULL) {
+		progerr(gettext(ERR_TEMP), errno);
+		exit(99);
+	}
+
+	(void) fprintf(stderr, gettext(MSG_PROTOTYPE));
+	if (n = mkpkgmap(t_pkgmap, protofile, cmdparam)) {
+		progerr(gettext(ERR_BUILD));
+		quit(1);
+	}
+
+	setmapmode(MAPNONE);	/* All appropriate variables are now bound */
+
+	if (vfpOpen(&vfp, t_pkgmap, "r", VFP_NEEDNOW) != 0) {
+		progerr(gettext(ERR_TEMP), errno);
+		quit(99);
+	}
+
+	eptlist = procmap(vfp, 0, NULL);
+
+	if (eptlist == NULL) {
+		quit(1);
+	}
+
+	(void) vfpClose(&vfp);
+
+	/* Validate the zone attributes in pkginfo, before creation */
+	if (!valid_zone_attr(eptlist)) {
+		progerr(ERR_PKGINFO_INVALID_OPTION_COMB);
+		quit(1);
+	}
+
+	(void) fprintf(stderr, gettext(MSG_PKGINFO));
+	pt = NULL;
+	for (i = 0; eptlist[i]; i++) {
+		ckmissing(eptlist[i]->path, eptlist[i]->ftype);
+		if (eptlist[i]->ftype != 'i')
+			continue;
+		if (strcmp(eptlist[i]->path, "pkginfo") == 0)
+			svept = eptlist[i];
+	}
+	if (svept == NULL) {
+		progerr(gettext(ERR_NOPKGINFO));
+		quit(99);
+	}
+	eptnum = i;
+
+	/*
+	 * process all parameters from the pkginfo file
+	 * and place them in the execution environment
+	 */
+
+	if ((fp = fopen(svept->ainfo.local, "r")) == NULL) {
+		progerr(gettext(ERR_RDPKGINFO), svept->ainfo.local);
+		quit(99);
+	}
+	param[0] = '\0';
+	while (value = fpkgparam(fp, param)) {
+		if (getenv(param) == NULL)
+			putparam(param, value);
+		free((void *)value);
+		param[0] = '\0';
+	}
+	(void) fclose(fp);
+
+	/* add command line variables */
+	while (*cmdparam && (value = strchr(*cmdparam, '=')) != NULL) {
+		*value = NULL;	/* terminate the parameter */
+		value++;	/* value is now the value (not '=') */
+		putparam(*cmdparam++, value);  /* store it in environ */
+	}
+
+	/* make sure parameters are valid */
+	(void) time(&clock);
+	if (pt = getenv("PKG")) {
+		if (pkgnmchk(pt, NULL, 0) || strchr(pt, '.')) {
+			progerr(gettext(ERR_PKGABRV), pt);
+			quit(1);
+		}
+		if (pkginst == NULL)
+			pkginst = pt;
+	} else {
+		progerr(gettext(ERR_NOPARAM), "PKG", svept->path);
+		quit(1);
+	}
+	/*
+	 * verify consistency between PKG parameter and pkginst
+	 */
+	(void) snprintf(param, sizeof (param), "%s.*", pt);
+	if (pkgnmchk(pkginst, param, 0)) {
+		progerr(gettext(ERR_PKGMTCH), pt, pkginst);
+		quit(1);
+	}
+
+	/*
+	 * *********************************************************************
+	 * this feature is removed starting with Solaris 10 - there is no built
+	 * in list of packages that should be run "the old way"
+	 * *********************************************************************
+	 */
+
+#ifdef	ALLOW_EXCEPTION_PKG_LIST
+	/* Until 2.9, set it from the execption list */
+	if (exception_pkg(pkginst, LINK))
+		set_nonABI_symlinks();
+#endif
+
+	if ((pkgname = getenv("NAME")) == NULL) {
+		progerr(gettext(ERR_NOPARAM), "NAME", svept->path);
+		quit(1);
+	}
+	if (ckparam("NAME", pkgname))
+		quit(1);
+	if ((pkgvers = getenv("VERSION")) == NULL) {
+		/* XXX - I18n */
+		/* LINTED do not use cftime(); use strftime instead */
+		(void) cftime(buf, "\045m/\045d/\045Y", &clock);
+		(void) snprintf(temp, sizeof (temp),
+			gettext("Dev Release %s"), buf);
+		putparam("VERSION", temp);
+		pkgvers = getenv("VERSION");
+		logerr(gettext(WRN_SETPARAM), "VERSION", temp);
+	}
+	if (ckparam("VERSION", pkgvers))
+		quit(1);
+	if ((pkgarch = getenv("ARCH")) == NULL) {
+		(void) uname(&utsbuf);
+		putparam("ARCH", utsbuf.machine);
+		pkgarch = getenv("ARCH");
+		logerr(gettext(WRN_SETPARAM), "ARCH", utsbuf.machine);
+	}
+	if (ckparam("ARCH", pkgarch))
+		quit(1);
+	if (getenv("PSTAMP") == NULL) {
+		/* use octal value of '%' to fight sccs expansion */
+		/* XXX - I18n */
+		/* LINTED do not use cftime(); use strftime instead */
+		(void) cftime(buf, "\045Y\045m\045d\045H\045M\045S", &clock);
+		(void) uname(&utsbuf);
+		(void) snprintf(temp, sizeof (temp), "%s%s",
+			utsbuf.nodename, buf);
+		putparam("PSTAMP", temp);
+		logerr(gettext(WRN_SETPARAM), "PSTAMP", temp);
+	}
+	if ((pkgcat = getenv("CATEGORY")) == NULL) {
+		progerr(gettext(ERR_NOPARAM), "CATEGORY", svept->path);
+		quit(1);
+	}
+	if (ckparam("CATEGORY", pkgcat))
+		quit(1);
+
+	/*
+	 * warn user of classes listed in package which do
+	 * not appear in CLASSES variable in pkginfo file
+	 */
+	objects = 0;
+	for (i = 0; eptlist[i]; i++) {
+		if (eptlist[i]->ftype != 'i') {
+			objects++;
+			addlist(&allclass, eptlist[i]->pkg_class);
+		}
+	}
+
+	if ((pt = getenv("CLASSES")) == NULL) {
+		if (allclass && *allclass) {
+			cl_setl(allclass);
+			cl_putl("CLASSES", allclass);
+			logerr(gettext(WRN_SETPARAM), "CLASSES",
+			    getenv("CLASSES"));
+		}
+	} else {
+		cl_sets(qstrdup(pt));
+		if (allclass && *allclass) {
+			for (i = 0; allclass[i]; i++) {
+				found = 0;
+				if (cl_idx(allclass[i]->name) != -1) {
+					found++;
+					break;
+				}
+				if (!found) {
+					logerr(gettext(WRN_CLASSES),
+					    (char *)allclass[i]);
+				}
+			}
+		}
+	}
+
+	(void) fprintf(stderr, gettext(MSG_VOLUMIZE), objects);
+	order = (struct cl_attr **)0;
+	if (pt = getenv("ORDER")) {
+		pt = qstrdup(pt);
+		(void) setlist(&order, pt);
+		cl_putl("ORDER", order);
+	}
+
+	/* stat the intended output filesystem to get blocking information */
+	if (pkgdev.dirname == NULL) {
+		progerr(gettext(ERR_WHATVFS), device);
+		quit(99);
+	}
+	if (statvfs64(pkgdev.dirname, &svfsb)) {
+		progerr(gettext(ERR_STATVFS), pkgdev.dirname);
+		quit(99);
+	}
+
+	if (bsize == 0) {
+		bsize = svfsb.f_bsize;
+	}
+	if (frsize == 0) {
+		frsize = svfsb.f_frsize;
+	}
+
+	if (limit == 0)
+		/*
+		 * bavail is in terms of fragment size blocks - change
+		 * to 512 byte blocks
+		 */
+		limit = (fsblkcnt_t)(((fsblkcnt_t)frsize > 0) ?
+			howmany(frsize, DEV_BSIZE) :
+			howmany(bsize, DEV_BSIZE)) * svfsb.f_bavail;
+
+	if (ilimit == 0) {
+		ilimit = (svfsb.f_favail > 0) ?
+		    svfsb.f_favail : svfsb.f_ffree;
+	}
+
+	nparts = splpkgmap(eptlist, eptnum, (char **)order, bsize, frsize,
+	    &limit, &ilimit, &llimit);
+
+	if (nparts <= 0) {
+		progerr(gettext(ERR_SPLIT));
+		quit(1);
+	}
+
+	if (nflag) {
+		for (i = 0; eptlist[i]; i++)
+			(void) ppkgmap(eptlist[i], stdout);
+		exit(0);
+		/*NOTREACHED*/
+	}
+
+	(void) snprintf(pkgloc, sizeof (pkgloc), "%s/%s",
+			pkgdev.dirname, pkginst);
+	if (!isdir(pkgloc) && !overwrite) {
+		progerr(gettext(ERR_OVERWRITE), pkgloc);
+		quit(1);
+	}
+
+	/* output all environment install parameters */
+	t_pkginfo = tempnam(tmpdir, "pkginfo");
+	if ((fp = fopen(t_pkginfo, "w")) == NULL) {
+		progerr(gettext(ERR_TEMP), errno);
+		exit(99);
+	}
+	for (i = 0; environ[i]; i++) {
+		if (isupper(*environ[i])) {
+			(void) fputs(environ[i], fp);
+			(void) fputc('\n', fp);
+		}
+	}
+	(void) fclose(fp);
+
+	started++;
+	(void) rrmdir(pkgloc);
+	if (mkdir(pkgloc, 0755)) {
+		progerr(gettext(ERR_MKDIR), pkgloc);
+		quit(1);
+	}
+
+	/* determine how many packages already reside on the medium */
+	pkgdir = pkgdev.dirname;
+	npkgs = 0;
+	while (pt = fpkginst("all", NULL, NULL))
+		npkgs++;
+	(void) fpkginst(NULL); /* free resource usage */
+
+	if (nparts > 1) {
+		if (pkgdev.mount && npkgs) {
+			progerr(gettext(ERR_ONEVOL));
+			quit(1);
+		}
+	}
+
+	/*
+	 *  update pkgmap entry for pkginfo file, since it may
+	 *  have changed due to command line or failure to
+	 *  specify all neccessary parameters
+	 */
+	for (i = 0; eptlist[i]; i++) {
+		if (eptlist[i]->ftype != 'i')
+			continue;
+		if (strcmp(eptlist[i]->path, "pkginfo") == 0) {
+			svept = eptlist[i];
+			svept->ftype = '?';
+			svept->ainfo.local = t_pkginfo;
+			(void) cverify(0, &svept->ftype, t_pkginfo,
+				&svept->cinfo, 1);
+			svept->ftype = 'i';
+			break;
+		}
+	}
+
+	if (nparts > 1)
+		(void) fprintf(stderr, gettext(MSG_PACKAGEM), nparts);
+	else
+		(void) fprintf(stderr, gettext(MSG_PACKAGE1));
+
+	for (part = 1; part <= nparts; part++) {
+		if ((part > 1) && pkgdev.mount) {
+			if (pkgumount(&pkgdev)) {
+				progerr(gettext(ERR_UMOUNT), pkgdev.mount);
+				quit(99);
+			}
+			if (n = pkgmount(&pkgdev, NULL, part, nparts, 1))
+				quit(n);
+			(void) rrmdir(pkgloc);
+			if (mkdir(pkgloc, 0555)) {
+				progerr(gettext(ERR_MKDIR), pkgloc);
+				quit(99);
+			}
+		}
+		outvol(eptlist, eptnum, part, nparts);
+
+		/* Validate (as much as possible) the control scripts. */
+		if (part == 1) {
+			char inst_path[PATH_MAX];
+
+			(void) fprintf(stderr, gettext(MSG_VALSCRIPTS));
+			(void) snprintf(inst_path, sizeof (inst_path),
+					"%s/install", pkgloc);
+			checkscripts(inst_path, 0);
+		}
+	}
+
+	quit(0);
+	/* LINTED: no return */
+}
+
+static void
+trap(int n)
+{
+	(void) signal(SIGINT, SIG_IGN);
+	(void) signal(SIGHUP, SIG_IGN);
+
+	if (n == SIGINT)
+		quit(3);
+	else {
+		(void) fprintf(stderr, gettext("%s terminated (signal %d).\n"),
+				get_prog_name(), n);
+		quit(99);
+	}
+}
+
+static void
+outvol(struct cfent **eptlist, unsigned int eptnum, int part, int nparts)
+{
+	FILE	*fp;
+	char	*svpt, *path, temp[PATH_MAX];
+	unsigned int	i;
+
+
+	if (nparts > 1)
+		(void) fprintf(stderr, gettext(" -- part %2d:\n"), part);
+	if (part == 1) {
+		/* re-write pkgmap, but exclude local pathnames */
+		(void) snprintf(temp, sizeof (temp), "%s/pkgmap", pkgloc);
+		if ((fp = fopen(temp, "w")) == NULL) {
+			progerr(gettext(ERR_TEMP), errno);
+			quit(99);
+		}
+		(void) fprintf(fp, ": %d %ld\n", nparts, limit);
+		for (i = 0; eptlist[i]; i++) {
+			svpt = eptlist[i]->ainfo.local;
+			if (!strchr("sl", eptlist[i]->ftype))
+				eptlist[i]->ainfo.local = NULL;
+			if (ppkgmap(eptlist[i], fp)) {
+				progerr(gettext(ERR_TEMP), errno);
+				quit(99);
+			}
+			eptlist[i]->ainfo.local = svpt;
+		}
+		(void) fclose(fp);
+		(void) fprintf(stderr, "%s\n", temp);
+	}
+
+	(void) snprintf(temp, sizeof (temp), "%s/pkginfo", pkgloc);
+	if (copyf(svept->ainfo.local, temp, svept->cinfo.modtime))
+		quit(1);
+	(void) fprintf(stderr, "%s\n", temp);
+
+	for (i = 0; i < eptnum; i++) {
+		if (eptlist[i]->volno != part)
+			continue;
+		if (strchr("dxslcbp", eptlist[i]->ftype))
+			continue;
+		if (eptlist[i]->ftype == 'i') {
+			if (eptlist[i] == svept)
+				continue; /* don't copy pkginfo file */
+			(void) snprintf(temp, sizeof (temp),
+				"%s/install/%s", pkgloc,
+				eptlist[i]->path);
+			path = temp;
+		} else
+			path = srcpath(pkgloc, eptlist[i]->path, part, nparts);
+		if (sflag) {
+			if (slinkf(eptlist[i]->ainfo.local, path))
+				quit(1);
+		} else if (copyf(eptlist[i]->ainfo.local, path,
+				eptlist[i]->cinfo.modtime)) {
+			quit(1);
+		}
+
+		/*
+		 * If the package file attributes can be sync'd up with
+		 * the pkgmap, we fix the attributes here.
+		 */
+		if (*(eptlist[i]->ainfo.owner) != '$' &&
+		    *(eptlist[i]->ainfo.group) != '$' && getuid() == 0) {
+			/* Clear dangerous bits. */
+			eptlist[i]->ainfo.mode=
+			    (eptlist[i]->ainfo.mode & S_IAMB);
+			/*
+			 * Make sure it can be read by the world and written
+			 * by root.
+			 */
+			eptlist[i]->ainfo.mode |= 0644;
+			if (!strchr("in", eptlist[i]->ftype)) {
+				/* Set the safe attributes. */
+				averify(1, &(eptlist[i]->ftype),
+				    path, &(eptlist[i]->ainfo));
+			}
+		}
+
+		(void) fprintf(stderr, "%s\n", path);
+	}
+}
+
+static void
+ckmissing(char *path, char type)
+{
+	static char	**dir;
+	static int	ndir;
+	char	*pt;
+	int	i, found;
+
+	if (dir == NULL) {
+		dir = (char **)calloc(MALSIZ, sizeof (char *));
+		if (dir == NULL) {
+			progerr(gettext(ERR_MEMORY), errno);
+			quit(99);
+		}
+	}
+
+	if (strchr("dx", type)) {
+		dir[ndir] = path;
+		if ((++ndir % MALSIZ) == 0) {
+			dir = (char **)realloc((void *)dir,
+				(ndir+MALSIZ)*sizeof (char *));
+			if (dir == NULL) {
+				progerr(gettext(ERR_MEMORY), errno);
+				quit(99);
+			}
+		}
+		dir[ndir] = (char *)NULL;
+	}
+
+	pt = path;
+	if (*pt == '/')
+		pt++;
+	while (pt = strchr(pt, '/')) {
+		*pt = '\0';
+		found = 0;
+		for (i = 0; i < ndir; i++) {
+			if (strcmp(path, dir[i]) == 0) {
+				found++;
+				break;
+			}
+		}
+		if (!found) {
+			logerr(gettext(WRN_MISSINGDIR), path);
+			ckmissing(qstrdup(path), 'd');
+		}
+		*pt++ = '/';
+	}
+}
+
+static int
+slinkf(char *from, char *to)
+{
+	char	*pt;
+
+	pt = to;
+	while (pt = strchr(pt+1, '/')) {
+		*pt = '\0';
+		if (isdir(to) && mkdir(to, 0755)) {
+			progerr(gettext(ERR_MKDIR), to);
+			*pt = '/';
+			return (-1);
+		}
+		*pt = '/';
+	}
+	if (symlink(from, to)) {
+		progerr(gettext(ERR_SYMLINK), to);
+		return (-1);
+	}
+	return (0);
+}
+
+static void
+usage(void)
+{
+	(void) fprintf(stderr, gettext(ERR_USAGE), get_prog_name());
+	exit(1);
+	/*NOTREACHED*/
+}
+
+/*
+ * valid_zone_attr:	Validates the zone attributes specified in
+ *			pkginfo file for this package. The package
+ *			can not be created with certain combinations
+ *			of the attributes.
+ */
+static boolean_t
+valid_zone_attr(struct cfent **eptlist)
+{
+	FILE		*pkginfoFP;
+	boolean_t	all_zones;	/* pkg is "all zones" only */
+	boolean_t	is_hollow;	/* pkg is "hollow" */
+	boolean_t	this_zone;	/* pkg is "this zone" only */
+	char 		pkginfoPath[PATH_MAX];	/* pkginfo file path */
+	char		*pkgInst;
+	int i;
+
+	/* Path to pkginfo file within the package to be installed */
+
+	this_zone = B_FALSE;
+	for (i = 0; eptlist[i]; i++) {
+		if (eptlist[i]->ftype != 'i')
+			continue;
+		if (strcmp(eptlist[i]->path, "pkginfo") == 0)
+			(void) strcpy(pkginfoPath, eptlist[i]->ainfo.local);
+
+		/*
+		 * Check to see if this package has a request script. If this
+		 * package does have a request script, then mark the package
+		 * for installation in this zone only. Any package with a
+		 * request script cannot be installed outside of the zone the
+		 * pkgadd command is being run in, nor can such a package be
+		 * installed as part of a new zone install. A new zone install
+		 * must be non-interactive, which is required by all packages
+		 * integrated into the Solaris WOS.
+		 * If request file is set in prototype, then this_zone is TRUE.
+		 */
+		if (strcmp(eptlist[i]->path, "request") == 0)
+			this_zone = B_TRUE;
+	}
+
+	/* Gather information from the pkginfo file */
+
+	pkginfoFP = fopen(pkginfoPath, "r");
+
+	if (pkginfoFP == NULL) {
+		progerr(ERR_NO_PKG_INFOFILE, pkginfoPath, strerror(errno));
+		return (B_FALSE);
+	}
+
+	if ((pkgInst = fpkgparam(pkginfoFP, "PKG")) == NULL) {
+		progerr(gettext(ERR_NOPARAM), "PKG", pkginfoPath);
+		return (B_FALSE);
+	}
+
+
+	/* Determine "HOLLOW" setting for this package */
+	is_hollow = pkginfoParamTruth(pkginfoFP, PKG_HOLLOW_VARIABLE,
+			"true", B_FALSE);
+
+	/* Determine "ALLZONES" setting for this package */
+	all_zones = pkginfoParamTruth(pkginfoFP, PKG_ALLZONES_VARIABLE,
+			"true", B_FALSE);
+
+	/* Determine "THISZONE" setting for this package, if no request file */
+	if (!this_zone)
+		this_zone = pkginfoParamTruth(pkginfoFP, PKG_THISZONE_VARIABLE,
+			"true", B_FALSE);
+
+	/* Close pkginfo file */
+	(void) fclose(pkginfoFP);
+
+	/*
+	 * Validate zone attributes based on information gathered,
+	 * and validate the three SUNW_PKG_ options:
+	 *
+	 * -----------------------------|---------------|
+	 * <ALLZONES><HOLLOW><THISZONE> |  If Allowed   |
+	 * ----1------------------------|---------------|
+	 *		F F F		|	OK	|
+	 *		F F T		|	OK	|
+	 *		F T *		|	NO	|
+	 * ----2------------------------|---------------|
+	 *		T F F		|	OK	|
+	 *		T T F		|	OK	|
+	 *		T * T		|	NO	|
+	 * -----------------------------|---------------|
+	 */
+
+	/* pkg "all zones" && "this zone" (#2) */
+
+	if (all_zones && this_zone) {
+		progerr(ERR_ALLZONES_AND_THISZONE, pkgInst,
+		    PKG_ALLZONES_VARIABLE, PKG_THISZONE_VARIABLE);
+		return (B_FALSE);
+	}
+
+	/* pkg "!all zones" && "hollow" (#1) */
+
+	if ((!all_zones) && is_hollow) {
+		progerr(ERR_NO_ALLZONES_AND_HOLLOW, pkgInst,
+		    PKG_ALLZONES_VARIABLE, PKG_HOLLOW_VARIABLE);
+		return (B_FALSE);
+	}
+
+	return (B_TRUE);
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/usr/src/cmd/svr4pkg/pkgmk/mkpkgmap.c	Fri Jun 05 10:28:40 2009 -0400
@@ -0,0 +1,827 @@
+/*
+ * 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
+ * or http://www.opensolaris.org/os/licensing.
+ * 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 2006 Sun Microsystems, Inc.  All rights reserved.
+ * Use is subject to license terms.
+ */
+
+/* Copyright (c) 1984, 1986, 1987, 1988, 1989 AT&T */
+/* All Rights Reserved */
+
+
+#include <stdio.h>
+#include <string.h>
+#include <errno.h>
+#include <ctype.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <limits.h>
+#include <pkgstrct.h>
+#include <pkginfo.h>
+#include <locale.h>
+#include <libintl.h>
+#include <unistd.h>
+#include <stdlib.h>
+#include <pkglib.h>
+#include <install.h>
+#include <libadm.h>
+#include <libinst.h>
+
+extern char	*basedir, *root, *rootlist[], **environ;
+
+/*
+ * IMPORTANT NOTE: PLEASE SEE THE DEFINITION OF temp[] BELOW BEFORE
+ * CHANGING THE DEFINITION OF PATH_LGTH!!!!
+ */
+
+#define	PATH_LGTH 4096
+
+#define	MAXPARAMS 256
+#define	NRECURS 20
+
+#define	MSG_BPARAMC	"parametric class specification for <%s> not allowed"
+#define	MSG_SRCHLOC	"no object for <%s> found in local path"
+#define	MSG_SRCHSRCH	"no object for <%s> found in search path"
+#define	MSG_SRCHROOT	"no object for <%s> found in root directory"
+#define	MSG_CONTENTS	"unable to process contents of object <%s>"
+#define	MSG_WRITE	"write of entry failed, errno=%d"
+#define	MSG_GARBDEFLT	"garbled default settings: %s"
+#define	MSG_BANG	"unknown directive: %s"
+#define	MSG_CHDIR	"unable to change directory to <%s>"
+#define	MSG_INCOMPLETE	"processing of <%s> may be incomplete"
+#define	MSG_NRECURS	"too many levels of include (limit is %d)"
+#define	MSG_RDINCLUDE	"unable to process include file <%s>, errno=%d"
+#define	MSG_IGNINCLUDE	"ignoring include file <%s>"
+#define	MSG_NODEVICE	"device numbers cannot be determined for <%s>"
+
+#define	WRN_BADATTR	"WARNING: attributes set to %04o %s %s for <%s>"
+#define	WRN_BADATTRM	"WARNING: attributes set to %s %s %s for <%s>"
+#define	WRN_FAKEBD	"WARNING: parametric paths may ignore BASEDIR"
+
+#define	ERR_TEMP	"unable to obtain temporary file resources, errno=%d"
+#define	ERR_ENVBUILD	"unable to build parameter environment, errno=%d"
+#define	ERR_MAXPARAMS	"too many parameter definitions (limit is %d)"
+#define	ERR_GETCWD	"unable to get current directory, errno=%d"
+#define	ERR_PATHVAR	"cannot resolve all build parameters associated with " \
+			    "path <%s>."
+
+static struct cfent entry;
+static FILE	*fp,
+		*sfp[20];
+static char	*dname[NRECURS],
+		*params[256],
+		*proto[NRECURS],
+		*rootp[NRECURS][16],
+		*srchp[NRECURS][16],
+		*d_own[NRECURS],
+		*d_grp[NRECURS],
+		*rdonly[256];
+static mode_t	d_mod[NRECURS];
+static int	nfp = (-1);
+static int	nrdonly = 0;
+static int	errflg = 0;
+static char	*separ = " \t\n, ";
+
+/* libpkg/gpkgmap.c */
+extern void	attrpreset(int mode, char *owner, char *group);
+extern void	attrdefault();
+static char	*findfile(char *path, char *local);
+static char	*srchroot(char *path, char *copy);
+
+static int	popenv(void);
+
+static int	doattrib(void);
+static void	doinclude(void);
+static void	dorsearch(void);
+static void	dosearch(void);
+static void	error(int flag);
+static void	lputenv(char *s);
+static void	pushenv(char *file);
+static void	translate(register char *pt, register char *copy);
+
+int
+mkpkgmap(char *outfile, char *protofile, char **envparam)
+{
+	FILE	*tmpfp;
+	char	*pt, *path, mybuff[PATH_LGTH];
+	char	**envsave;
+	int	c, fakebasedir;
+	int	i, n;
+
+	/*
+	 * NOTE: THE SIZE OF temp IS HARD CODED INTO CALLS TO fscanf.
+	 * YOU *MUST* MAKE SURE TO CHANGE THOSE CALLS IF THE SIZE OF temp
+	 * IS EVER CHANGED!!!!!!
+	 */
+	char	temp[PATH_LGTH];
+
+	if ((tmpfp = fopen(outfile, "w")) == NULL) {
+		progerr(gettext(ERR_TEMP), errno);
+		quit(99);
+	}
+	envsave = environ;
+	environ = params; /* use only local environ */
+	attrdefault();	/* assume no default attributes */
+
+	/*
+	 * Environment parameters are optional, so variable
+	 * (envparam[i]) could be NULL.
+	 */
+	for (i = 0; (envparam[i] != NULL) &&
+	    (pt = strchr(envparam[i], '=')); i++) {
+		*pt = '\0';
+		rdonly[nrdonly++] = qstrdup(envparam[i]);
+		*pt = '=';
+		if (putenv(qstrdup(envparam[i]))) { /* bugid 1090920 */
+			progerr(gettext(ERR_ENVBUILD), errno);
+			quit(99);
+		}
+		if (nrdonly >= MAXPARAMS) {
+			progerr(gettext(ERR_MAXPARAMS), MAXPARAMS);
+			quit(1);
+		}
+	}
+
+	pushenv(protofile);
+	errflg = 0;
+again:
+	fakebasedir = 0;
+	while (!feof(fp)) {
+		c = getc(fp);
+		while (isspace(c))
+			c = getc(fp);
+
+		if (c == '#') {
+			do c = getc(fp); while ((c != EOF) && (c != '\n'));
+			continue;
+		}
+		if (c == EOF)
+			break;
+
+		if (c == '!') {
+			/*
+			 * IMPORTANT NOTE: THE SIZE OF temp IS HARD CODED INTO
+			 * the FOLLOWING CALL TO fscanf -- YOU MUST CHANGE THIS
+			 * LINE IF THE SIZE OF fscanf IS EVER CHANGED!!!
+			 */
+			(void) fscanf(fp, "%4096s", temp);
+
+			if (strcmp(temp, "include") == 0)
+				doinclude();
+			else if (strcmp(temp, "rsearch") == 0)
+				dorsearch();
+			else if (strcmp(temp, "search") == 0)
+				dosearch();
+			else if (strcmp(temp, "default") == 0) {
+				if (doattrib())
+					break;
+			} else if (strchr(temp, '=')) {
+				translate(temp, mybuff);
+				/* put this into the local environment */
+				lputenv(mybuff);
+				(void) fscanf(fp, "%*[^\n]"); /* rest of line */
+				(void) fscanf(fp, "\n"); /* rest of line */
+			} else {
+				error(1);
+				logerr(gettext(MSG_BANG), temp);
+				(void) fscanf(fp, "%*[^\n]"); /* read of line */
+				(void) fscanf(fp, "\n"); /* read of line */
+			}
+			continue;
+		}
+		(void) ungetc(c, fp);
+
+		if ((n = gpkgmap(&entry, fp)) < 0) {
+			char	*errstr;
+
+			error(1);
+			errstr = getErrstr();
+			logerr(gettext("garbled entry"));
+			logerr(gettext("- pathname: %s"),
+			    (entry.path && *entry.path) ? entry.path :
+			    "Unknown");
+			logerr(gettext("- problem: %s"),
+			    (errstr && *errstr) ? errstr : "Unknown");
+			break;
+		}
+		if (n == 0)
+			break; /* done with file */
+
+		/* don't allow classname to be parametric */
+		if (entry.ftype != 'i') {
+			if (entry.pkg_class[0] == '$') {
+				error(1);
+				logerr(gettext(MSG_BPARAMC), entry.path);
+			}
+		}
+
+		if (strchr("dxlscbp", entry.ftype)) {
+			/*
+			 * We don't need to search for things without any
+			 * contents in them.
+			 */
+			if (strchr("cb", entry.ftype)) {
+				if (entry.ainfo.major == BADMAJOR ||
+				    entry.ainfo.minor == BADMINOR) {
+					error(1);
+					logerr(gettext(MSG_NODEVICE),
+					    entry.path);
+				}
+			}
+			path = NULL;
+		} else {
+			path = findfile(entry.path, entry.ainfo.local);
+			if (!path)
+				continue;
+
+			entry.ainfo.local = path;
+			if (strchr("fevin?", entry.ftype)) {
+				if (cverify(0, &entry.ftype, path,
+				    &entry.cinfo, 1)) {
+					error(1);
+					logerr(gettext(MSG_CONTENTS), path);
+				}
+			}
+		}
+
+		/* Warn if attributes are not set correctly. */
+		if (!strchr("isl", entry.ftype)) {
+			int dowarning = 0;
+			int hasbadmode = 0;
+
+			if (entry.ainfo.mode == NOMODE) {
+				entry.ainfo.mode = CURMODE;
+				dowarning = 1;
+				hasbadmode = 1;
+			}
+
+			if (strcmp(entry.ainfo.owner, NOOWNER) == 0) {
+				(void) strlcpy(entry.ainfo.owner, CUROWNER,
+						sizeof (entry.ainfo.owner));
+				dowarning = 1;
+			}
+
+			if (strcmp(entry.ainfo.group, NOGROUP) == 0) {
+				(void) strlcpy(entry.ainfo.group, CURGROUP,
+						sizeof (entry.ainfo.group));
+				dowarning = 1;
+			}
+
+
+			if (dowarning) {
+				if (hasbadmode)
+					logerr(gettext(WRN_BADATTRM),
+						"?",
+					    entry.ainfo.owner,
+					    entry.ainfo.group,
+					    entry.path);
+				else
+					logerr(gettext(WRN_BADATTR),
+						(int)entry.ainfo.mode,
+						entry.ainfo.owner,
+						entry.ainfo.group,
+						entry.path);
+			}
+		}
+
+		/*
+		 * Resolve build parameters (initial lower case) in
+		 * the link and target paths.
+		 */
+		if (strchr("ls", entry.ftype)) {
+			if (!RELATIVE(entry.ainfo.local) ||
+					PARAMETRIC(entry.ainfo.local)) {
+				if (mappath(1, entry.ainfo.local)) {
+					error(1);
+					logerr(gettext(ERR_PATHVAR),
+					    entry.ainfo.local);
+					break;
+				}
+
+				canonize(entry.ainfo.local);
+			}
+		}
+
+		/*
+		 * Warn if top level file or directory is an install
+		 * parameter
+		 */
+		if (entry.ftype != 'i') {
+			if (entry.path[0] == '$' && isupper(entry.path[1]))
+				fakebasedir = 1;
+		}
+
+		if (mappath(1, entry.path)) {
+			error(1);
+			logerr(gettext(ERR_PATHVAR), entry.path);
+			break;
+		}
+
+		canonize(entry.path);
+		if (ppkgmap(&entry, tmpfp)) {
+			error(1);
+			logerr(gettext(MSG_WRITE), errno);
+			break;
+		}
+	}
+
+	if (fakebasedir) {
+		logerr(gettext(WRN_FAKEBD));
+		fakebasedir = 0;
+	}
+
+	if (popenv())
+		goto again;
+
+	(void) fclose(tmpfp);
+	environ = envsave; /* restore environment */
+
+	return (errflg ? 1 : 0);
+}
+
+static char *
+findfile(char *path, char *local)
+{
+	struct stat statbuf;
+	static char host[PATH_MAX];
+	register char *pt;
+	char	temp[PATH_MAX], *basename;
+	int	i;
+
+	/*
+	 * map any parameters specified in path to their corresponding values
+	 * and make sure the path is in its canonical form; any parmeters for
+	 * which a value is not defined will be left unexpanded. Since this
+	 * is an actual search for a real file (which will not end up in the
+	 * package) - we map ALL variables (both build and Install).
+	 */
+	(void) strlcpy(temp, (local && local[0] ? local : path), sizeof (temp));
+	mappath(0, temp);
+	canonize(temp);
+
+	*host = '\0';
+	if (rootlist[0] || (basedir && (*temp != '/'))) {
+		/*
+		 * search for path in the pseudo-root/basedir directory; note
+		 * that package information files should NOT be included in
+		 * this list
+		 */
+		if (entry.ftype != 'i')
+			return (srchroot(temp, host));
+	}
+
+	/* looking for local object file  */
+	if (local && *local) {
+		basepath(temp, dname[nfp], NULL);
+		/*
+		 * If it equals "/dev/null", that just means it's an empty
+		 * file. Otherwise, we'll really be writing stuff, so we need
+		 * to verify the source.
+		 */
+		if (strcmp(temp, "/dev/null") != 0) {
+			if (stat(temp, &statbuf) ||
+			    !(statbuf.st_mode & S_IFREG)) {
+				error(1);
+				logerr(gettext(MSG_SRCHLOC), path);
+				return (NULL);
+			}
+		}
+		(void) strlcpy(host, temp, sizeof (host));
+		return (host);
+	}
+
+	for (i = 0; rootp[nfp][i]; i++) {
+		(void) snprintf(host, sizeof (host), "%s/%s", rootp[nfp][i],
+		    temp + (*temp == '/' ? 1 : 0));
+		if ((stat(host, &statbuf) == 0) &&
+		    (statbuf.st_mode & S_IFREG)) {
+			return (host);
+		}
+	}
+
+	pt = strrchr(temp, '/');
+	if (!pt++)
+		pt = temp;
+
+	basename = pt;
+
+	for (i = 0; srchp[nfp][i]; i++) {
+		(void) snprintf(host, sizeof (host), "%s/%s",
+			srchp[nfp][i], basename);
+		if ((stat(host, &statbuf) == 0) &&
+		    (statbuf.st_mode & S_IFREG)) {
+			return (host);
+		}
+	}
+
+	/* check current directory as a last resort */
+	(void) snprintf(host, sizeof (host), "%s/%s", dname[nfp], basename);
+	if ((stat(host, &statbuf) == 0) && (statbuf.st_mode & S_IFREG))
+		return (host);
+
+	error(1);
+	logerr(gettext(MSG_SRCHSRCH), path);
+	return (NULL);
+}
+
+static void
+dosearch(void)
+{
+	char temp[PATH_MAX], lookpath[PATH_MAX], *pt;
+	int n;
+
+	(void) fgets(temp, PATH_MAX, fp);
+	translate(temp, lookpath);
+
+	for (n = 0; srchp[nfp][n]; n++)
+		free(srchp[nfp][n]);
+
+	n = 0;
+	pt = strtok(lookpath, separ);
+	if (pt && *pt) {
+		do {
+			if (*pt != '/') {
+				/* make relative path an absolute directory */
+				(void) snprintf(temp, sizeof (temp),
+						"%s/%s", dname[nfp], pt);
+				pt = temp;
+			}
+			canonize(pt);
+			srchp[nfp][n++] = qstrdup(pt);
+		} while (pt = strtok(NULL, separ));
+		srchp[nfp][n] = NULL;
+	}
+}
+
+static void
+dorsearch(void)
+{
+	char temp[PATH_MAX], lookpath[PATH_MAX], *pt;
+	int n;
+
+	(void) fgets(temp, PATH_MAX, fp);
+	translate(temp, lookpath);
+
+	for (n = 0; rootp[nfp][n]; n++)
+		free(rootp[nfp][n]);
+
+	n = 0;
+	pt = strtok(lookpath, separ);
+	do {
+		if (*pt != '/') {
+			/* make relative path an absolute directory */
+			(void) snprintf(temp, sizeof (temp),
+					"%s/%s", dname[nfp], pt);
+			pt = temp;
+		}
+		canonize(pt);
+		rootp[nfp][n++] = qstrdup(pt);
+	} while (pt = strtok(NULL, separ));
+	rootp[nfp][n] = NULL;
+}
+
+/*
+ * This function reads the default mode, owner and group from the prototype
+ * file and makes that available.
+ */
+static int
+doattrib(void)
+{
+	char *pt, attrib[PATH_MAX], *mode_ptr, *owner_ptr, *group_ptr, *eol;
+	int mode;
+	char owner[ATRSIZ+1], group[ATRSIZ+1], attrib_save[(4*ATRSIZ)];
+
+	(void) fgets(attrib, PATH_MAX, fp);
+
+	(void) strlcpy(attrib_save, attrib, sizeof (attrib_save));
+
+	/*
+	 * Now resolve any variables that may be present. Start on group and
+	 * move backward since that keeps the resolved string from
+	 * overwriting any of the other entries. This is required since
+	 * mapvar() writes the resolved string over the string provided.
+	 */
+	mode_ptr = strtok(attrib, " \t");
+	owner_ptr = strtok(NULL, " \t");
+	group_ptr = strtok(NULL, " \t\n");
+	eol = strtok(NULL, " \t\n");
+	if (strtok(NULL, " \t\n")) {
+		/* extra tokens on the line */
+		error(1);
+		logerr(gettext(MSG_GARBDEFLT), (eol) ? eol :
+		    gettext("unreadable at end of line"));
+		return (1);
+	}
+
+	if (group_ptr && mapvar(1, group_ptr) == 0)
+		(void) strncpy(group, group_ptr, ATRSIZ);
+	else {
+		error(1);
+		logerr(gettext(MSG_GARBDEFLT), (attrib_save) ?
+		    ((attrib_save[0]) ? attrib_save : gettext("none")) :
+		    gettext("unreadable at group"));
+		return (1);
+	}
+
+	if (owner_ptr && mapvar(1, owner_ptr) == 0)
+		(void) strncpy(owner, owner_ptr, ATRSIZ);
+	else {
+		error(1);
+		logerr(gettext(MSG_GARBDEFLT), (attrib_save) ?
+		    ((attrib_save[0]) ? attrib_save : gettext("none")) :
+		    gettext("unreadable at owner"));
+		return (1);
+	}
+
+	/*
+	 * For mode, don't use scanf, since we want to force an octal
+	 * interpretation and need to limit the length of the owner and group
+	 * specifications.
+	 */
+	if (mode_ptr && mapvar(1, mode_ptr) == 0)
+		mode = strtol(mode_ptr, &pt, 8);
+	else {
+		error(1);
+		logerr(gettext(MSG_GARBDEFLT), (attrib_save) ?
+		    ((attrib_save[0]) ? attrib_save : gettext("none")) :
+		    gettext("unreadable at mode"));
+		return (1);
+	}
+
+	/* free any previous memory from qstrdup */
+	if (d_own[nfp])
+		free(d_own[nfp]);
+	if (d_grp[nfp])
+		free(d_grp[nfp]);
+
+	d_mod[nfp] = mode;
+	d_own[nfp] = qstrdup(owner);
+	d_grp[nfp] = qstrdup(group);
+
+	attrpreset(d_mod[nfp], d_own[nfp], d_grp[nfp]);
+
+	return (0);
+}
+
+static void
+doinclude(void)
+{
+	char	file[PATH_MAX];
+	char	temp[PATH_MAX];
+
+	(void) fgets(temp, PATH_MAX, fp);
+
+	/*
+	 * IMPORTANT NOTE: THE SIZE OF temp IS HARD CODED INTO THE
+	 * FOLLOWING CALL TO fscanf -- YOU MUST CHANGE THIS LINE IF
+	 * THE SIZE OF fscanf IS EVER CHANGED!!!
+	 */
+	(void) sscanf(temp, "%1024s", file);
+
+	translate(file, temp);
+	canonize(temp);
+
+	if (*temp == NULL)
+		return;
+	else if (*temp != '/')
+		(void) snprintf(file, sizeof (file), "%s/%s", dname[nfp], temp);
+	else
+		(void) strlcpy(file, temp, sizeof (file));
+
+	canonize(file);
+	pushenv(file);
+}
+
+/*
+ * This does what mappath() does except that it does it for ALL variables
+ * using whitespace as a token separator. This is used to resolve search
+ * paths and assignment statements. It doesn't effect the build versus
+ * install decision made for pkgmap variables.
+ */
+static void
+translate(register char *pt, register char *copy)
+{
+	char *pt2, varname[MAX_PKG_PARAM_LENGTH];
+
+token:
+	/* eat white space */
+	while (isspace(*pt))
+		pt++;
+	while (*pt && !isspace(*pt)) {
+		if (*pt == '$') {
+			pt2 = varname;
+			while (*++pt && !strchr("/= \t\n\r", *pt))
+				*pt2++ = *pt;
+			*pt2 = '\0';
+			if (pt2 = getenv(varname)) {
+				while (*pt2)
+					*copy++ = *pt2++;
+			}
+		} else
+			*copy++ = *pt++;
+	}
+	if (*pt) {
+		*copy++ = ' ';
+		goto token;
+	}
+	*copy = '\0';
+}
+
+static void
+error(int flag)
+{
+	static char *lasterr = NULL;
+
+	if (lasterr != proto[nfp]) {
+		lasterr = proto[nfp];
+		(void) fprintf(stderr, gettext("ERROR in %s:\n"), lasterr);
+	}
+	if (flag)
+		errflg++;
+}
+
+/* Set up defaults and change to the build directory. */
+static void
+pushenv(char *file)
+{
+	register char *pt;
+	static char	topdir[PATH_MAX];
+
+	if ((nfp+1) >= NRECURS) {
+		error(1);
+		logerr(gettext(MSG_NRECURS), NRECURS);
+		logerr(gettext(MSG_IGNINCLUDE), file);
+		return;
+	}
+
+	if (strcmp(file, "-") == 0) {
+		fp = stdin;
+	} else if ((fp = fopen(file, "r")) == NULL) {
+		error(1);
+		logerr(gettext(MSG_RDINCLUDE), file, errno);
+		if (nfp >= 0) {
+			logerr(gettext(MSG_IGNINCLUDE), file);
+			fp = sfp[nfp];
+			return;
+		} else
+			quit(1);
+	}
+	sfp[++nfp] = fp;
+	srchp[nfp][0] = NULL;
+	rootp[nfp][0] = NULL;
+	d_mod[nfp] = (mode_t)(-1);
+	d_own[nfp] = NULL;
+	d_grp[nfp] = NULL;
+
+	if (!nfp) {
+		/* upper level proto file */
+		proto[nfp] = file;
+		if (file[0] == '/')
+			pt = strcpy(topdir, file);
+		else {
+			/* path is relative to the prototype file specified */
+			pt = getcwd(NULL, PATH_MAX);
+			if (pt == NULL) {
+				progerr(gettext(ERR_GETCWD), errno);
+				quit(99);
+			}
+			(void) snprintf(topdir, sizeof (topdir),
+						"%s/%s", pt, file);
+		}
+		if (pt = strrchr(topdir, '/'))
+			*pt = '\0'; /* should always happen */
+		if (topdir[0] == '\0')
+			(void) strlcpy(topdir, "/", sizeof (topdir));
+		dname[nfp] = topdir;
+	} else {
+		proto[nfp] = qstrdup(file);
+		dname[nfp] = qstrdup(file);
+		if (pt = strrchr(dname[nfp], '/'))
+			*pt = '\0';
+		else {
+			/* same directory as the last prototype */
+			free(dname[nfp]);
+			dname[nfp] = qstrdup(dname[nfp-1]);
+			return; /* no need to canonize() or chdir() */
+		}
+	}
+
+	canonize(dname[nfp]);
+
+	if (chdir(dname[nfp])) {
+		error(1);
+		logerr(gettext(MSG_CHDIR), dname[nfp]);
+		if (!nfp)
+			quit(1); /* must be able to cd to upper level */
+		logerr(gettext(MSG_IGNINCLUDE), proto[nfp]);
+		(void) popenv();
+	}
+}
+
+/* Restore defaults and return to the prior directory. */
+static int
+popenv(void)
+{
+	int i;
+
+	(void) fclose(fp);
+	if (nfp) {
+		if (proto[nfp])
+			free(proto[nfp]);
+		if (dname[nfp])
+			free(dname[nfp]);
+		for (i = 0; srchp[nfp][i]; i++)
+			free(srchp[nfp][i]);
+		for (i = 0; rootp[nfp][i]; i++)
+			free(rootp[nfp][i]);
+		if (d_own[nfp])
+			free(d_own[nfp]);
+		if (d_grp[nfp])
+			free(d_grp[nfp]);
+
+		fp = sfp[--nfp];
+
+		if (chdir(dname[nfp])) {
+			error(1);
+			logerr(gettext(MSG_CHDIR), dname[nfp]);
+			logerr(gettext(MSG_INCOMPLETE), proto[nfp]);
+			return (popenv());
+		}
+		return (1);
+	}
+	return (0);
+}
+
+/*
+ * If this parameter isn't already in place, put it into the local
+ * environment. This means that command line directives override prototype
+ * file directives.
+ */
+static void
+lputenv(char *s)
+{
+	char *pt;
+	int i;
+
+	pt = strchr(s, '=');
+	if (!pt)
+		return;
+
+	*pt = '\0';
+	for (i = 0; i < nrdonly; i++) {
+		if (strcmp(rdonly[i], s) == 0) {
+			*pt = '=';
+			return;
+		}
+	}
+	*pt = '=';
+
+	if (putenv(qstrdup(s))) {
+		progerr(gettext(ERR_ENVBUILD), errno);
+		quit(99);
+	}
+}
+
+static char *
+srchroot(char *path, char *copy)
+{
+	struct stat statbuf;
+	int i;
+
+	i = 0;
+	root = rootlist[i++];
+	do {
+		/* convert with root & basedir info */
+		cvtpath(path, copy);
+		/* make it pretty again */
+		canonize(copy);
+
+		if (stat(copy, &statbuf) || !(statbuf.st_mode & S_IFREG)) {
+			root = rootlist[i++];
+			continue; /* host source must be a regular file */
+		}
+		return (copy);
+	} while (root != NULL);
+	error(1);
+	logerr(gettext(MSG_SRCHROOT), path);
+	return (NULL);
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/usr/src/cmd/svr4pkg/pkgmk/quit.c	Fri Jun 05 10:28:40 2009 -0400
@@ -0,0 +1,74 @@
+/*
+ * 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
+ * or http://www.opensolaris.org/os/licensing.
+ * 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 1993 Sun Microsystems, Inc.  All rights reserved.
+ * Use is subject to license terms.
+ */
+
+/* Copyright (c) 1984, 1986, 1987, 1988, 1989 AT&T */
+/* All Rights Reserved */
+
+
+#include <stdio.h>
+#include <signal.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <pkgdev.h>
+#include <locale.h>
+#include <libintl.h>
+#include <pkglib.h>
+#include "libadm.h"
+
+extern struct pkgdev pkgdev;
+extern char	pkgloc[], *t_pkgmap, *t_pkginfo;
+
+extern int	started;
+
+#define	MSG_COMPLETE	"## Packaging complete.\n"
+#define	MSG_TERM	"## Packaging terminated at user request.\n"
+#define	MSG_ERROR	"## Packaging was not successful.\n"
+
+void
+quit(int retcode)
+{
+	(void) signal(SIGINT, SIG_IGN);
+	(void) signal(SIGHUP, SIG_IGN);
+
+	if (retcode == 3)
+		(void) fprintf(stderr, gettext(MSG_TERM));
+	else if (retcode)
+		(void) fprintf(stderr, gettext(MSG_ERROR));
+	else
+		(void) fprintf(stderr, gettext(MSG_COMPLETE));
+
+	if (retcode && started)
+		(void) rrmdir(pkgloc); /* clean up output directory */
+
+	if (pkgdev.mount)
+		(void) pkgumount(&pkgdev);
+
+	if (t_pkgmap)
+		(void) unlink(t_pkgmap);
+	if (t_pkginfo)
+		(void) unlink(t_pkginfo);
+	exit(retcode);
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/usr/src/cmd/svr4pkg/pkgmk/splpkgmap.c	Fri Jun 05 10:28:40 2009 -0400
@@ -0,0 +1,633 @@
+/*
+ * 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
+ * or http://www.opensolaris.org/os/licensing.
+ * 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.
+ */
+
+/* Copyright (c) 1984, 1986, 1987, 1988, 1989 AT&T */
+/* All Rights Reserved */
+
+
+#include <stdio.h>
+#include <errno.h>
+#include <string.h>
+#include <limits.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <sys/types.h>
+#include <sys/param.h>
+#include <pkgdev.h>
+#include <pkgstrct.h>
+#include <locale.h>
+#include <libintl.h>
+#include <pkglib.h>
+#include <libadm.h>
+#include <libinst.h>
+
+extern struct pkgdev pkgdev;
+
+#define	MALSIZ	500
+#define	EFACTOR	128ULL	/* typical size of a single entry in a pkgmap file */
+
+#define	WRN_LIMIT	"WARNING: -l limit (%llu blocks) exceeds device " \
+			"capacity (%llu blocks)"
+#define	ERR_MEMORY	"memory allocation failure, errno=%d"
+#define	ERR_TOOBIG	"%s (%llu blocks) does not fit on a volume"
+#define	ERR_INFOFIRST	"information file <%s> must appear on first part"
+#define	ERR_INFOSPACE	"all install files must appear on first part"
+#define	ERR_VOLBLKS	"Objects selected for part %d require %llu blocks, " \
+			"limit=%llu."
+#define	ERR_VOLFILES	"Objects selected for part %d require %llu files, " \
+			"limit=%llu."
+#define	ERR_FREE	"package does not fit space currently available in <%s>"
+
+struct data {
+	fsblkcnt_t	blks;
+	struct cfent *ept;
+};
+
+struct class_type {
+	char *name;
+	int first;
+	int last;
+};
+
+static fsblkcnt_t	btotal;	/* blocks stored on current part */
+static fsblkcnt_t	bmax; 	/* maximum number of blocks on any part */
+
+static fsfilcnt_t	ftotal;	/* files stored on current part */
+static fsfilcnt_t	fmax;	/* maximum number of files on any part */
+static fsblkcnt_t	bpkginfo; 	/* blocks used by pkginfo file */
+static char	**dirlist;
+static short	volno; 		/* current part */
+static int	nparts = -1; 	/* total number of parts */
+static int	nclass;
+static fsblkcnt_t 	DIRSIZE;
+static struct	class_type *cl;
+
+static int	nodecount(char *path);
+static int	store(struct data **, unsigned int, char *, fsblkcnt_t,
+    fsblkcnt_t);
+static void	addclass(char *aclass, int vol);
+static void	allocnode(char *path);
+static void	newvolume(struct data **, unsigned int, fsblkcnt_t limit,
+    fsblkcnt_t);
+static void	sortsize(struct data *f, struct data **sf, unsigned int eptnum);
+
+int
+splpkgmap(struct cfent **eptlist, unsigned int eptnum, char *order[],
+    ulong_t bsize, ulong_t frsize, fsblkcnt_t *plimit, fsfilcnt_t *pilimit,
+    fsblkcnt_t *pllimit)
+{
+	struct data	*f, **sf;
+	struct cfent	*ept;
+	register int	i, j;
+	int		new_vol_set;
+	short		new_vol;
+	int		flag, errflg;
+	fsblkcnt_t	total;
+	fsblkcnt_t	btemp;
+	fsfilcnt_t	ftemp;
+
+	f = (struct data *)calloc(eptnum, sizeof (struct data));
+	if (f == NULL) {
+		progerr(gettext(ERR_MEMORY), errno);
+		quit(99);
+	}
+
+	sf = (struct data **)calloc(eptnum, sizeof (struct data *));
+	if (sf == NULL) {
+		progerr(gettext(ERR_MEMORY), errno);
+		quit(99);
+	}
+
+	nclass = 0;
+	cl = (struct class_type *)calloc(MALSIZ, sizeof (struct class_type));
+	if (cl == NULL) {
+		progerr(gettext(ERR_MEMORY), errno);
+		quit(99);
+	}
+
+	errflg = 0;
+
+	/*
+	 * The next bit of code checks to see if, when creating a package
+	 * on a directory, there are enough free blocks and inodes before
+	 * continuing.
+	 */
+	total = 0;
+	/*
+	 * DIRSIZE takes up 1 logical block, iff we have no frags, else
+	 * it just takes a frag
+	 */
+	DIRSIZE = ((fsblkcnt_t)frsize > 0) ?
+	    howmany(frsize, DEV_BSIZE) :
+	    howmany(bsize, DEV_BSIZE);
+
+	if (!pkgdev.mount) {
+		allocnode(NULL);
+		/*
+		 * If we appear to have a valid value for free inodes
+		 * and there's not enough for the package contents,
+		 * then exit
+		 */
+		if ((*pilimit > 0) && (eptnum+1 > *pilimit)) {
+			progerr(gettext(ERR_FREE), pkgdev.dirname);
+			quit(1);
+		}
+		for (i = 0; i < eptnum; i++) {
+			if (strchr("dxslcbp", eptlist[i]->ftype))
+				continue;
+			else {
+				total +=
+				    (nodecount(eptlist[i]->path) * DIRSIZE);
+				total +=
+				    nblk(eptlist[i]->cinfo.size, bsize, frsize);
+				if (total > *plimit) {
+					progerr(gettext(ERR_FREE),
+						pkgdev.dirname);
+					quit(1);
+				}
+				allocnode(eptlist[i]->path);
+			}
+		}
+	}
+	/*
+	 * if there is a value in pllimit (-l specified limit), use that for
+	 * the limit from now on.
+	 */
+
+	if (*pllimit != 0) {
+		if (pkgdev.mount && *pllimit > *plimit)
+			logerr(gettext(WRN_LIMIT), *pllimit, *plimit);
+		*plimit = *pllimit;
+	}
+	/*
+	 * calculate number of physical blocks used by each object
+	 */
+	for (i = 0; i < eptnum; i++) {
+		f[i].ept = ept = eptlist[i];
+		if (ept->volno > nparts)
+			nparts = ept->volno;
+		addclass(ept->pkg_class, 0);
+		if (strchr("dxslcbp", ept->ftype))
+			/*
+			 * virtual object (no contents)
+			 */
+			f[i].blks = 0;
+		else
+			/*
+			 * space consumers
+			 *
+			 * (directories are space consumers as well, but they
+			 * get accounted for later).
+			 *
+			 */
+
+			f[i].blks = nblk(ept->cinfo.size, bsize, frsize);
+
+		if (!bpkginfo && (strcmp(f[i].ept->path, "pkginfo") == 0))
+			bpkginfo = f[i].blks;
+	}
+
+	/*
+	 * Make sure that items slated for a given 'part' do not exceed a single
+	 * volume.
+	 */
+	for (i = 1; i <= nparts; i++) {
+		btemp = (bpkginfo + 2LL);
+		ftemp = 2LL;
+		if (i == 1) {
+			/*
+			 * save room for install directory
+			 */
+			ftemp += 2;
+			btemp += nblk(eptnum * EFACTOR, bsize, frsize);
+			btemp += 2;
+		}
+		allocnode(NULL);
+		for (j = 0; j < eptnum; j++) {
+			if (i == 1 && f[j].ept->ftype == 'i' &&
+			    (strcmp(f[j].ept->path, "pkginfo") == 0 ||
+			    strcmp(f[j].ept->path, "pkgmap") == 0))
+				continue;
+			if (f[j].ept->volno == i ||
+			    (f[j].ept->ftype == 'i' && i == 1)) {
+				ftemp += nodecount(f[j].ept->path);
+				btemp += f[j].blks;
+				allocnode(f[j].ept->path);
+			}
+		}
+		btemp += (ftemp * DIRSIZE);
+		if (btemp > *plimit) {
+			progerr(gettext(ERR_VOLBLKS), i, btemp, *plimit);
+			errflg++;
+		/* If we have a valid inode limit, ensure this part will fit */
+		} else if ((*pilimit > 0) && (ftemp+1 > *pilimit)) {
+			progerr(gettext(ERR_VOLFILES), i, ftemp + 1, *pilimit);
+			errflg++;
+		}
+	}
+	if (errflg)
+		quit(1);
+
+	/*
+	 * "sf" - array sorted in decreasing file size order, based on "f".
+	 */
+	sortsize(f, sf, eptnum);
+
+	/*
+	 * initialize first volume
+	 */
+	newvolume(sf, eptnum, *plimit, *pilimit);
+
+	/*
+	 * reserve room on first volume for pkgmap
+	 */
+	btotal += nblk((fsblkcnt_t)(eptnum * EFACTOR), bsize, frsize);
+	ftotal++;
+
+
+	/*
+	 * initialize directory info
+	 */
+	allocnode(NULL);
+
+	/*
+	 * place installation files on first volume!
+	 */
+	flag = 0;
+	for (j = 0; j < eptnum; ++j) {
+		if (f[j].ept->ftype != 'i')
+			continue;
+		else if (!flag++) {
+			/*
+			 * save room for install directory
+			 */
+			ftotal++;
+			btotal += 2ULL;
+		}
+		if (!f[j].ept->volno) {
+			f[j].ept->volno = 1;
+			ftotal++;
+			btotal += f[j].blks;
+		} else if (f[j].ept->volno != 1) {
+			progerr(gettext(ERR_INFOFIRST), f[j].ept->path);
+			errflg++;
+		}
+	}
+
+	if (errflg)
+		quit(1);
+	if (btotal > *plimit) {
+		progerr(gettext(ERR_INFOSPACE));
+		quit(1);
+	}
+
+	/*
+	 * Make sure that any given file will fit on a single volume, this
+	 * calculation has to take into account packaging overhead, otherwise
+	 * the function store() will go into a severe recursive plunge.
+	 */
+	for (j = 0; j < eptnum; ++j) {
+		/*
+		 * directory overhead.
+		 */
+		btemp = nodecount(f[j].ept->path) * DIRSIZE;
+		/*
+		 * packaging overhead.
+		 */
+		btemp += (bpkginfo + 2L); 	/* from newvolume() */
+		if ((f[j].blks + btemp) > *plimit) {
+			errflg++;
+			progerr(gettext(ERR_TOOBIG), f[j].ept->path, f[j].blks);
+		}
+	}
+	if (errflg)
+		quit(1);
+
+	/*
+	 * place classes listed on command line
+	 */
+	if (order) {
+		for (i = 0; order[i]; ++i)  {
+			while (store(sf, eptnum, order[i], *plimit, *pilimit))
+				/* stay in loop until store is complete */
+				/* void */;
+		}
+	}
+
+	while (store(sf, eptnum, (char *)0, *plimit, *pilimit))
+		/* stay in loop until store is complete */
+		/* void */;
+
+	/*
+	 * place all virtual objects, e.g. links and spec devices
+	 */
+	for (i = 0; i < nclass; ++i) {
+		/*
+		 * if no objects were associated, attempt to
+		 * distribute in order of class list
+		 */
+		if (cl[i].first == 0)
+			cl[i].last = cl[i].first = (i ? cl[i-1].last : 1);
+		for (j = 0; j < eptnum; j++) {
+			if ((f[j].ept->volno == 0) &&
+			    strcmp(f[j].ept->pkg_class, cl[i].name) == 0) {
+				if (strchr("sl", f[j].ept->ftype))
+					f[j].ept->volno = cl[i].last;
+				else
+					f[j].ept->volno = cl[i].first;
+			}
+		}
+	}
+
+	if (btotal)
+		newvolume(sf, eptnum, *plimit, *pilimit);
+
+	if (nparts > (volno - 1)) {
+		new_vol = volno;
+		for (i = volno; i <= nparts; i++) {
+			new_vol_set = 0;
+			for (j = 0; j < eptnum; j++) {
+				if (f[j].ept->volno == i) {
+					f[j].ept->volno = new_vol;
+					new_vol_set = 1;
+				}
+			}
+			new_vol += new_vol_set;
+		}
+		nparts = new_vol - 1;
+	} else
+		nparts = volno - 1;
+
+	*plimit = bmax;
+	*pilimit = fmax;
+
+	/*
+	 * free up dynamic space used by this module
+	 */
+	free(f);
+	free(sf);
+	for (i = 0; i < nclass; ++i)
+		free(cl[i].name);
+	free(cl);
+	for (i = 0; dirlist[i]; i++)
+		free(dirlist[i]);
+	free(dirlist);
+
+	return (errflg ? -1 : nparts);
+}
+
+static int
+store(struct data **sf, unsigned int eptnum, char *aclass, fsblkcnt_t limit,
+    fsfilcnt_t ilimit)
+{
+	int	i, svnodes, choice, select;
+	long	ftemp;
+	fsblkcnt_t	btemp;
+
+	select = 0;
+	choice = (-1);
+	for (i = 0; i < eptnum; ++i) {
+		if (sf[i]->ept->volno || strchr("sldxcbp", sf[i]->ept->ftype))
+			continue; /* defer storage until class is selected */
+		if (aclass && strcmp(aclass, sf[i]->ept->pkg_class))
+			continue;
+		select++; /* we need to place at least one object */
+		ftemp = nodecount(sf[i]->ept->path);
+		btemp = sf[i]->blks + (ftemp * DIRSIZE);
+		if (((limit == 0) || ((btotal + btemp) <= limit)) &&
+		    ((ilimit == 0) || ((ftotal + ftemp) < ilimit))) {
+			/* largest object which fits on this volume */
+			choice = i;
+			svnodes = ftemp;
+			break;
+		}
+	}
+	if (!select)
+		return (0); /* no more to objects to place */
+
+	if (choice < 0) {
+		newvolume(sf, eptnum, limit, ilimit);
+		return (store(sf, eptnum, aclass, limit, ilimit));
+	}
+	sf[choice]->ept->volno = (char)volno;
+	ftotal += svnodes + 1;
+	btotal += sf[choice]->blks + (svnodes * DIRSIZE);
+	allocnode(sf[i]->ept->path);
+	addclass(sf[choice]->ept->pkg_class, volno);
+	return (++choice); /* return non-zero if more work to do */
+}
+
+static void
+allocnode(char *path)
+{
+	register int i;
+	int	found;
+	char	*pt;
+
+	if (path == NULL) {
+		if (dirlist) {
+			/*
+			 * free everything
+			 */
+			for (i = 0; dirlist[i]; i++)
+				free(dirlist[i]);
+			free(dirlist);
+		}
+		dirlist = (char **)calloc(MALSIZ, sizeof (char *));
+		if (dirlist == NULL) {
+			progerr(gettext(ERR_MEMORY), errno);
+			quit(99);
+		}
+		return;
+	}
+
+	pt = path;
+	if (*pt == '/')
+		pt++;
+	/*
+	 * since the pathname supplied is never just a directory,
+	 * we store only the dirname of of the path.
+	 */
+	while (pt = strchr(pt, '/')) {
+		*pt = '\0';
+		found = 0;
+		for (i = 0; dirlist[i] != NULL; i++) {
+			if (strcmp(path, dirlist[i]) == 0) {
+				found++;
+				break;
+			}
+		}
+		if (!found) {
+			/* insert this path in node list */
+			dirlist[i] = qstrdup(path);
+			if ((++i % MALSIZ) == 0) {
+				dirlist = (char **)realloc(dirlist,
+					(i+MALSIZ) * sizeof (char *));
+				if (dirlist == NULL) {
+					progerr(gettext(ERR_MEMORY), errno);
+					quit(99);
+				}
+			}
+			dirlist[i] = (char *)NULL;
+		}
+		*pt++ = '/';
+	}
+}
+
+static int
+nodecount(char *path)
+{
+	char	*pt;
+	int	i, found, count;
+
+	pt = path;
+	if (*pt == '/')
+		pt++;
+
+	/*
+	 * we want to count the number of path
+	 * segments that need to be created, not
+	 * including the basename of the path;
+	 * this works only since we are never
+	 * passed a pathname which itself is a
+	 * directory
+	 */
+	count = 0;
+	while (pt = strchr(pt, '/')) {
+		*pt = '\0';
+		found = 0;
+		for (i = 0; dirlist[i]; i++) {
+			if (strcmp(path, dirlist[i]) != 0) {
+				found++;
+				break;
+			}
+		}
+		if (!found)
+			count++;
+		*pt++ = '/';
+	}
+	return (count);
+}
+
+static void
+newvolume(struct data **sf, unsigned int eptnum, fsblkcnt_t limit,
+    fsblkcnt_t ilimit)
+{
+	register int i;
+	int	newnodes;
+
+	if (volno) {
+		(void) fprintf(stderr,
+		    gettext("part %2d -- %llu blocks, %llu entries\n"),
+		    volno, btotal, ftotal);
+		if (btotal > bmax)
+			bmax = btotal;
+		if (ftotal > fmax)
+			fmax = ftotal;
+		btotal = bpkginfo + 2ULL;
+		ftotal = 2;
+	} else {
+		btotal = 2ULL;
+		ftotal = 1;
+	}
+	volno++;
+
+	/*
+	 * zero out directory storage
+	 */
+	allocnode((char *)0);
+
+	/*
+	 * force storage of files whose volume number has already been assigned
+	 */
+	for (i = 0; i < eptnum; i++) {
+		if (sf[i]->ept->volno == volno) {
+			newnodes = nodecount(sf[i]->ept->path);
+			ftotal += newnodes + 1;
+			btotal += sf[i]->blks + (newnodes * DIRSIZE);
+			if (btotal > limit) {
+				progerr(gettext(ERR_VOLBLKS), volno, btotal,
+					limit);
+				quit(1);
+			} else if ((ilimit == 0) && (ftotal+1 > ilimit)) {
+				progerr(gettext(ERR_VOLFILES), volno, ftotal+1,
+				    ilimit);
+				quit(1);
+			}
+		}
+	}
+}
+
+static void
+addclass(char *aclass, int vol)
+{
+	int i;
+
+	for (i = 0; i < nclass; ++i) {
+		if (strcmp(cl[i].name, aclass) == 0) {
+			if (vol <= 0)
+				return;
+			if (!cl[i].first || (vol < cl[i].first))
+				cl[i].first = vol;
+			if (vol > cl[i].last)
+				cl[i].last = vol;
+			return;
+		}
+	}
+	cl[nclass].name = qstrdup(aclass);
+	cl[nclass].first = vol;
+	cl[nclass].last = vol;
+	if ((++nclass % MALSIZ) == 0) {
+		cl = (struct class_type *)realloc((char *)cl,
+			sizeof (struct class_type) * (nclass+MALSIZ));
+		if (!cl) {
+			progerr(gettext(ERR_MEMORY), errno);
+			quit(99);
+		}
+	}
+}
+
+static void
+sortsize(struct data *f, struct data **sf, unsigned int eptnum)
+{
+	int	nsf;
+	int	j, k;
+	unsigned int	i;
+
+	nsf = 0;
+	for (i = 0; i < eptnum; i++) {
+		for (j = 0; j < nsf; ++j) {
+			if (f[i].blks > sf[j]->blks) {
+				for (k = nsf; k > j; k--) {
+					sf[k] = sf[k-1];
+				}
+				break;
+			}
+		}
+		sf[j] = &f[i];
+		nsf++;
+	}
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/usr/src/cmd/svr4pkg/pkgname/Makefile	Fri Jun 05 10:28:40 2009 -0400
@@ -0,0 +1,40 @@
+#
+# 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
+# or http://www.opensolaris.org/os/licensing.
+# 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 2009 Sun Microsystems, Inc.  All rights reserved.
+# Use is subject to license terms.
+#
+PROG=		pkgname
+
+OBJS=		pkgname.o
+
+include $(SRC)/cmd/svr4pkg/Makefile.svr4pkg
+
+LDLIBS +=	-ladm
+
+
+.KEEP_STATE:
+all:            $(PROG)
+
+install:	all $(ROOTPKGBINPROG)
+
+include $(SRC)/cmd/svr4pkg/Makefile.svr4pkg.targ
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/usr/src/cmd/svr4pkg/pkgname/pkgname.c	Fri Jun 05 10:28:40 2009 -0400
@@ -0,0 +1,51 @@
+/*
+ * 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
+ * or http://www.opensolaris.org/os/licensing.
+ * 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 2009 Sun Microsystems, Inc.  All rights reserved.
+ * Use is subject to license terms.
+ */
+
+/* Copyright (c) 1984, 1986, 1987, 1988, 1989 AT&T */
+/* All Rights Reserved */
+
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <locale.h>
+#include <libadm.h>
+
+int
+main(int argc, char *argv[])
+{
+	(void) setlocale(LC_ALL, "");
+
+#if !defined(TEXT_DOMAIN)	/* Should be defined by cc -D */
+#define	TEXT_DOMAIN "SYS_TEST"
+#endif
+	(void) textdomain(TEXT_DOMAIN);
+
+	while (--argc > 0) {
+		if (pkgnmchk(argv[argc], (char *)0, 1))
+			exit(1);
+	}
+	return (0);
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/usr/src/cmd/svr4pkg/pkgparam/Makefile	Fri Jun 05 10:28:40 2009 -0400
@@ -0,0 +1,41 @@
+#
+# 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
+# or http://www.opensolaris.org/os/licensing.
+# 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 2009 Sun Microsystems, Inc.  All rights reserved.
+# Use is subject to license terms.
+#
+
+PROG=		pkgparam
+
+OBJS=		pkgparam.o
+
+include $(SRC)/cmd/svr4pkg/Makefile.svr4pkg
+
+LDLIBS	+=	-lpkg -linstzones -ladm
+
+
+.KEEP_STATE:
+all:            $(PROG)
+
+install:	all $(ROOTPROG)
+
+include $(SRC)/cmd/svr4pkg/Makefile.svr4pkg.targ
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/usr/src/cmd/svr4pkg/pkgparam/pkgparam.c	Fri Jun 05 10:28:40 2009 -0400
@@ -0,0 +1,205 @@
+/*
+ * 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
+ * or http://www.opensolaris.org/os/licensing.
+ * 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 2009 Sun Microsystems, Inc.  All rights reserved.
+ * Use is subject to license terms.
+ */
+
+/* Copyright (c) 1984, 1986, 1987, 1988, 1989 AT&T */
+/* All Rights Reserved */
+
+
+#include <stdio.h>
+#include <string.h>
+#include <errno.h>
+#include <limits.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <sys/types.h>
+#include <pkgstrct.h>
+#include <pkginfo.h>
+#include <locale.h>
+#include <libintl.h>
+
+#include <pkglib.h>
+#include <libadm.h>
+#include <libinst.h>
+
+extern char	*pkgfile;
+
+#define	ERR_ROOT_SET	"Could not set install root from the environment."
+#define	ERR_ROOT_CMD	"Command line install root contends with environment."
+#define	ERR_MESG	"unable to locate parameter information for \"%s\""
+#define	ERR_FLT		"parsing error in parameter file"
+#define	ERR_USAGE	"usage:\n" \
+			"\t%s [-v] [-d device] pkginst [param [param ...]]\n" \
+			"\t%s [-v] -f file [param [param ...]]\n"
+#define	HASHSIZE	151
+#define	BSZ		4
+
+
+static char	*device = NULL;
+static int	errflg = 0;
+static int	vflag = 0;
+
+static void	print_entry(char *, char *);
+
+static void
+usage(void)
+{
+	char	*prog = get_prog_name();
+
+	(void) fprintf(stderr, gettext(ERR_USAGE), prog, prog);
+	exit(1);
+}
+
+int
+main(int argc, char *argv[])
+{
+	char *value, *pkginst;
+	char *param, parambuf[128];
+	int c;
+
+	pkgfile = NULL;
+
+	/* initialize locale mechanism */
+
+	(void) setlocale(LC_ALL, "");
+
+#if !defined(TEXT_DOMAIN)	/* Should be defined by cc -D */
+#define	TEXT_DOMAIN "SYS_TEST"
+#endif
+	(void) textdomain(TEXT_DOMAIN);
+
+	/* determine program name */
+
+	(void) set_prog_name(argv[0]);
+
+	/* establish installation root directory */
+
+	if (!set_inst_root(getenv("PKG_INSTALL_ROOT"))) {
+		progerr(gettext(ERR_ROOT_SET));
+		exit(1);
+	}
+
+	while ((c = getopt(argc, argv, "R:vd:f:?")) != EOF) {
+		switch (c) {
+		    case 'v':
+			vflag++;
+			break;
+
+		    case 'f':
+			/* -f could specify filename to get parameters from */
+			pkgfile = optarg;
+			break;
+
+		    case 'd':
+			/* -d could specify stream or mountable device */
+			device = flex_device(optarg, 1);
+			break;
+
+		    case 'R':
+			if (!set_inst_root(optarg)) {
+				progerr(gettext(ERR_ROOT_CMD));
+				exit(1);
+			}
+			break;
+
+		    default:
+		    case '?':
+			usage();
+		}
+	}
+
+	set_PKGpaths(get_inst_root());
+
+	if (pkgfile) {
+		if (device)
+			usage();
+		pkginst = pkgfile;
+	} else {
+		if ((optind+1) > argc)
+			usage();
+
+		if (pkghead(device))
+			return (1); /* couldn't obtain info about device */
+		pkginst = argv[optind++];
+	}
+
+	/* If a filename was specified or install db does not exist */
+	do {
+		param = argv[optind];
+		if (!param) {
+			param = parambuf;
+			*param = '\0';
+		}
+		value = pkgparam(pkginst, param);
+		if (value == NULL) {
+			if (errno == EFAULT) {
+				progerr(gettext(ERR_FLT));
+				errflg++;
+				break;
+			} else if (errno != EINVAL) {
+				/*
+				 * some other error besides no value for this
+				 * particular parameter
+				 */
+				progerr(gettext(ERR_MESG), pkginst);
+				errflg++;
+				break;
+			}
+			if (!argv[optind])
+				break;
+			continue;
+		}
+
+		print_entry(param, value);
+
+	} while (!argv[optind] || (++optind < argc));
+	(void) pkgparam(NULL, NULL); /* close open FDs so umount won't fail */
+
+	(void) pkghead(NULL);
+	return (errflg ? 1 : 0);
+}
+
+static void
+print_entry(char *param, char *value)
+{
+	if (vflag) {
+		(void) printf("%s='", param);
+		while (*value) {
+			if (*value == '\'') {
+				(void) printf("'\"'\"'");
+				value++;
+			} else
+				(void) putchar(*value++);
+		}
+		(void) printf("'\n");
+	} else
+		(void) printf("%s\n", value);
+}
+
+void
+quit(int retval)
+{
+	exit(retval);
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/usr/src/cmd/svr4pkg/pkgproto/Makefile	Fri Jun 05 10:28:40 2009 -0400
@@ -0,0 +1,41 @@
+#
+# 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
+# or http://www.opensolaris.org/os/licensing.
+# 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 2009 Sun Microsystems, Inc.  All rights reserved.
+# Use is subject to license terms.
+#
+
+PROG=		pkgproto
+
+OBJS=		main.o
+
+include $(SRC)/cmd/svr4pkg/Makefile.svr4pkg
+
+LDLIBS	+=	-lpkg -ladm
+
+
+.KEEP_STATE:
+all:            $(PROG)
+
+install: all $(ROOTPROG)
+
+include $(SRC)/cmd/svr4pkg/Makefile.svr4pkg.targ
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/usr/src/cmd/svr4pkg/pkgproto/main.c	Fri Jun 05 10:28:40 2009 -0400
@@ -0,0 +1,512 @@
+/*
+ * 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
+ * or http://www.opensolaris.org/os/licensing.
+ * 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 2009 Sun Microsystems, Inc.  All rights reserved.
+ * Use is subject to license terms.
+ */
+
+/* Copyright (c) 1984, 1986, 1987, 1988, 1989 AT&T */
+/* All Rights Reserved */
+
+
+#include <stdio.h>
+#include <ctype.h>
+#include <dirent.h>
+#include <limits.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <string.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <pkgstrct.h>
+#include <errno.h>
+#include <locale.h>
+#include <libintl.h>
+#include <pkglib.h>
+#include "libadm.h"
+#include "libinst.h"
+
+extern int	holdcinfo;
+
+#define	WRN_SCARYLINK	"WARNING: <%s>, target of symlink <%s>, does not exist."
+
+#define	ERR_PATHLONG	"path argument too long"
+#define	ERR_CLASSLONG	"classname argument too long"
+#define	ERR_CLASSCHAR	"bad character in classname"
+#define	ERR_STAT	"unable to stat <%s>"
+#define	ERR_WRITE	"write of entry failed"
+#define	ERR_POPEN	"unable to create pipe to <%s>"
+#define	ERR_PCLOSE	"unable to close pipe to <%s>"
+#define	ERR_RDLINK	"unable to read link for <%s>"
+#define	ERR_MEMORY	"memory allocation failure, errno=%d"
+
+#define	LINK	1
+
+struct link {
+	char	*path;
+	ino_t	ino;
+	dev_t	dev;
+	struct link *next;
+};
+
+static struct link *firstlink = (struct link *)0;
+static struct link *lastlink = (struct link *)0;
+static char *scan_raw_ln(char *targ_name, char *link_name);
+
+static char	*def_class = "none";
+
+static int	errflg = 0;
+static int	iflag = 0;	/* follow symlinks */
+static int	xflag = 0;	/* confirm contents of files */
+static int	nflag = 0;
+static char	construction[PATH_MAX], mylocal[PATH_MAX];
+
+static void	findlink(struct cfent *ept, char *path, char *svpath);
+static void	follow(char *path);
+static void	output(char *path, int n, char *local);
+static void	usage(void);
+
+int
+main(int argc, char *argv[])
+{
+	int c;
+	char *pt, path[PATH_MAX];
+	char	*abi_sym_ptr;
+	extern char	*optarg;
+	extern int	optind;
+
+	(void) setlocale(LC_ALL, "");
+
+#if !defined(TEXT_DOMAIN)	/* Should be defined by cc -D */
+#define	TEXT_DOMAIN "SYS_TEST"
+#endif
+	(void) textdomain(TEXT_DOMAIN);
+
+	(void) set_prog_name(argv[0]);
+
+	while ((c = getopt(argc, argv, "xnic:?")) != EOF) {
+		switch (c) {
+		    case 'x':	/* include content info */
+			xflag++;
+			break;
+
+		    case 'n':
+			nflag++;
+			break;
+
+		    case 'c':	/* assign class */
+			def_class = optarg;
+			/* validate that classname is acceptable */
+			if (strlen(def_class) > (size_t)CLSSIZ) {
+				progerr(gettext(ERR_CLASSLONG));
+				exit(1);
+			}
+			for (pt = def_class; *pt; pt++) {
+				if (!isalpha(*pt) && !isdigit(*pt)) {
+					progerr(gettext(ERR_CLASSCHAR));
+					exit(1);
+				}
+			}
+			break;
+
+		    case 'i':	/* follow symlinks */
+			iflag++;
+			break;
+
+		    default:
+			usage();
+		}
+	}
+
+	if (iflag) {
+		/* follow symlinks */
+		set_nonABI_symlinks();
+	} else {
+		/* bug id 4244631, not ABI compliant */
+		abi_sym_ptr = getenv("PKG_NONABI_SYMLINKS");
+		if (abi_sym_ptr && strncasecmp(abi_sym_ptr, "TRUE", 4) == 0) {
+			set_nonABI_symlinks();
+		}
+	}
+	holdcinfo = !xflag;
+	if (optind == argc) {
+		/* take path list from stdin */
+		while (fgets(path, sizeof (path), stdin) != (char *)NULL) {
+			output(path, 0, NULL);
+		}
+	} else {
+		while (optind < argc) {
+			follow(argv[optind++]);
+		}
+	}
+
+	return (errflg ? 1 : 0);
+}
+
+static void
+output(char *path, int n, char *local)
+{
+	char		mypath[PATH_MAX];
+	int		len;
+	int		s;
+	struct cfent	entry;
+
+	/*
+	 * remove any trailing newline characters from the end of path
+	 */
+
+	len = strlen(path);
+	while ((len > 0) && (path[len-1] == '\n')) {
+		path[--len] = '\0';
+	}
+
+	entry.volno = 0;
+	entry.ftype = '?';
+	entry.path = mypath;
+	(void) strlcpy(entry.pkg_class, def_class, sizeof (entry.pkg_class));
+	(void) strlcpy(entry.path, path, PATH_MAX);
+	entry.ainfo.local = NULL;
+	entry.ainfo.mode = BADMODE;
+	(void) strlcpy(entry.ainfo.owner, BADOWNER, sizeof (entry.ainfo.owner));
+	(void) strlcpy(entry.ainfo.group, BADGROUP, sizeof (entry.ainfo.group));
+	errflg = 0;
+
+	if (xflag) {
+		entry.ftype = '?';
+		if (cverify(0, &entry.ftype, path, &entry.cinfo, 1)) {
+			errflg++;
+			logerr(gettext("ERROR: %s"), path);
+			logerr(getErrbufAddr());
+			return;
+		}
+	}
+
+	/*
+	 * Use averify to figure out the attributes. This has trouble
+	 * divining the identity of a symlink which points to a
+	 * non-existant target. For that reason, if it comes back as
+	 * an existence problem, we fake in a symlink and see if averify
+	 * likes that. If it does, all we have is a risky symlink.
+	 */
+	if ((s = averify(0, &entry.ftype, path, &entry.ainfo)) == VE_EXIST &&
+	    !iflag) {
+		entry.ftype = 's';	/* try again assuming symlink */
+		/* try to read what it points to */
+		if ((s = readlink(path, mylocal, PATH_MAX)) > 0) {
+			mylocal[s] = '\000';	/* terminate it */
+			entry.ainfo.local = mylocal;
+			if (averify(0, &entry.ftype, path, &entry.ainfo)) {
+				errflg++;
+			} else
+				/* It's a link to a file not in this package. */
+				ptext(stderr, gettext(WRN_SCARYLINK),
+				    mylocal, path);
+		} else {
+			errflg++;
+		}
+	} else if (s != 0 && s != VE_CONT)
+		errflg++;
+
+	if (errflg) {
+		logerr(gettext("ERROR: %s"), path);
+		logerr(getErrbufAddr());
+		return;
+	}
+
+	if (n) {
+		/* replace first n characters with 'local' */
+		if (strchr("fev", entry.ftype)) {
+			entry.ainfo.local = mylocal;
+			(void) strlcpy(entry.ainfo.local, entry.path,
+				PATH_MAX);
+			canonize(entry.ainfo.local);
+		}
+		if (local[0]) {
+			entry.ainfo.local = mylocal;
+			(void) strlcpy(entry.path, local, PATH_MAX);
+			(void) strcat(entry.path, path+n);
+		} else
+			(void) strlcpy(entry.path,
+				(path[n] == '/') ? path+n+1 : path+n,
+				PATH_MAX);
+	}
+
+	canonize(entry.path);
+	if (entry.path[0]) {
+		findlink(&entry, path, entry.path);
+		if (strchr("dcbp", entry.ftype) ||
+		(nflag && !strchr("sl", entry.ftype)))
+			entry.ainfo.local = NULL;
+		if (ppkgmap(&entry, stdout)) {
+			progerr(gettext(ERR_WRITE));
+			exit(99);
+		}
+	}
+}
+
+static void
+follow(char *path)
+{
+	struct stat stbuf;
+	FILE	*pp;
+	char	*pt,
+		local[PATH_MAX],
+		newpath[PATH_MAX],
+		cmd[PATH_MAX+32];
+	int n;
+
+	errflg = 0;
+
+	if (pt = strchr(path, '=')) {
+		*pt++ = '\0';
+		n = ((unsigned int)pt - (unsigned int)path - 1);
+		if (n >= PATH_MAX) {
+			progerr(gettext(ERR_PATHLONG));
+			errflg++;
+			return;
+		}
+
+		n = strlen(pt);
+
+		if (n < PATH_MAX) {
+			(void) strlcpy(local, pt, sizeof (local));
+			n = strlen(path);
+		} else {
+			progerr(gettext(ERR_PATHLONG));
+			errflg++;
+			return;
+		}
+	} else {
+		n = 0;
+		local[0] = '\0';
+	}
+
+	if (stat(path, &stbuf)) {
+		progerr(gettext(ERR_STAT), path);
+		errflg++;
+		return;
+	}
+
+	if (stbuf.st_mode & S_IFDIR) {
+		(void) snprintf(cmd, sizeof (cmd), "find %s -print", path);
+		if ((pp = popen(cmd, "r")) == NULL) {
+			progerr(gettext(ERR_POPEN), cmd);
+			exit(1);
+		}
+		while (fscanf(pp, "%[^\n]\n", newpath) == 1)
+			output(newpath, n, local);
+		if (pclose(pp)) {
+			progerr(gettext(ERR_PCLOSE), cmd);
+			errflg++;
+		}
+	} else
+		output(path, n, local);
+}
+
+/*
+ * Scan a raw link for origination errors. Given
+ *	targ_name = hlink/path/file1
+ *		and
+ *	link_name = hlink/path/file2
+ * we don't want the link to be verbatim since link_name must be relative
+ * to it's source. This functions checks for identical directory paths
+ * and if it's clearly a misplaced relative path, the duplicate
+ * directories are stripped. This is necessary because pkgadd is actually
+ * in the source directory (hlink/path) when it creates the link.
+ *
+ * NOTE : The buffer we get with targ_name is going to be used later
+ * and cannot be modified. That's why we have yet another PATH_MAX
+ * size buffer in this function.
+ */
+static char *
+scan_raw_ln(char *targ_name, char *link_name)
+{
+	char *const_ptr;	/* what we return */
+	char *file_name;	/* name of the file in link_name */
+	char *this_dir;		/* current directory in targ_name */
+	char *next_dir;		/* next directory in targ_name  */
+	char *targ_ptr;		/* current character in targ_name */
+
+	const_ptr = targ_name;	/* Point to here 'til we know it's different. */
+
+	/*
+	 * If the link is absolute or it is in the current directory, no
+	 * further testing necessary.
+	 */
+	if (RELATIVE(targ_name) &&
+	    (file_name = strrchr(link_name, '/')) != NULL) {
+
+		/*
+		 * This will be walked down to the highest directory
+		 * not common to both the link and the target.
+		 */
+		targ_ptr = targ_name;
+
+		/*
+		 * At this point targ_name is a relative path through at
+		 * least one directory.
+		 */
+		this_dir = targ_ptr;	/* first directory in targ_name */
+		file_name++;		/* point to the name not the '/' */
+
+		/*
+		 * Scan across the pathname until we reach a different
+		 * directory or the final file name.
+		 */
+		do {
+			size_t str_size;
+
+			next_dir = strchr(targ_ptr, '/');
+			if (next_dir)
+				next_dir++;	/* point to name not '/' */
+			else	/* point to the end of the string */
+				next_dir = targ_ptr+strlen(targ_ptr);
+
+			/* length to compare */
+			str_size = ((ptrdiff_t)next_dir - (ptrdiff_t)this_dir);
+
+			/*
+			 * If both paths begin with the same directory, then
+			 * skip that common directory in both the link and
+			 * the target.
+			 */
+			if (strncmp(this_dir, link_name, str_size) == 0) {
+				/* point to the target so far */
+				const_ptr = this_dir = next_dir;
+				/* Skip past it in the target */
+				targ_ptr = (char *)(targ_ptr+str_size);
+				/* Skip past it in the link */
+				link_name = (char *)(link_name+str_size);
+			/*
+			 * If these directories don't match then the
+			 * directory above is the lowest common directory. We
+			 * need to construct a relative path from the lowest
+			 * child up to that directory.
+			 */
+			} else {
+				int d = 0;
+				char *dptr = link_name;
+
+				/* Count the intermediate directories. */
+				while ((dptr = strchr(dptr, '/')) != NULL) {
+					dptr++;
+					d++;
+				}
+				/*
+				 * Now targ_ptr is pointing to the fork in
+				 * the path and dptr is pointing to the lowest
+				 * child in the link. We now insert the
+				 * appropriate number of "../'s" to get to
+				 * the first common directory. We'll
+				 * construct this in the construction
+				 * buffer.
+				 */
+				if (d) {
+					char *tptr;
+
+					const_ptr = tptr = construction;
+					while (d--) {
+						(void) strlcpy(tptr,
+							"../", PATH_MAX);
+						tptr += 3;
+					}
+					(void) strlcpy(tptr, targ_ptr,
+						PATH_MAX);
+				}
+				break;		/* done */
+			}
+		} while (link_name != file_name);	/* at file name */
+	}
+
+	return (const_ptr);
+}
+
+static void
+findlink(struct cfent *ept, char *path, char *svpath)
+{
+	struct stat	statbuf;
+	struct link	*link, *new;
+	char		buf[PATH_MAX];
+	int		n;
+
+	if (lstat(path, &statbuf)) {
+		progerr(gettext(ERR_STAT), path);
+		errflg++;
+	}
+	if ((statbuf.st_mode & S_IFMT) == S_IFLNK) {
+		if (!iflag) {
+			ept->ainfo.local = mylocal;
+			ept->ftype = 's';
+			n = readlink(path, buf, PATH_MAX);
+			if (n <= 0) {
+				progerr(gettext(ERR_RDLINK), path);
+				errflg++;
+				(void) strlcpy(ept->ainfo.local,
+					"unknown", PATH_MAX);
+			} else {
+				(void) strncpy(ept->ainfo.local, buf, n);
+				ept->ainfo.local[n] = '\0';
+			}
+		}
+		return;
+	}
+
+	if (stat(path, &statbuf))
+		return;
+	if (statbuf.st_nlink <= 1)
+		return;
+
+	for (link = firstlink; link; link = link->next) {
+		if ((statbuf.st_ino == link->ino) &&
+		(statbuf.st_dev == link->dev)) {
+			ept->ftype = 'l';
+			ept->ainfo.local = mylocal;
+			(void) strlcpy(ept->ainfo.local,
+					scan_raw_ln(link->path, ept->path),
+					PATH_MAX);
+			return;
+		}
+	}
+	if ((new = (struct link *)calloc(1, sizeof (struct link))) == NULL) {
+		progerr(gettext(ERR_MEMORY), errno);
+		exit(1);
+	}
+
+	if (firstlink) {
+		lastlink->next = new;
+		lastlink = new;
+	} else
+		firstlink = lastlink = new;
+
+	new->path = strdup(svpath);
+	new->ino = statbuf.st_ino;
+	new->dev = statbuf.st_dev;
+}
+
+static void
+usage(void)
+{
+	(void) fprintf(stderr,
+	    gettext("usage: %s [-i] [-c class] [path ...]\n"), get_prog_name());
+	exit(1);
+	/*NOTREACHED*/
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/usr/src/cmd/svr4pkg/pkgremove/Makefile	Fri Jun 05 10:28:40 2009 -0400
@@ -0,0 +1,48 @@
+#
+# 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
+# or http://www.opensolaris.org/os/licensing.
+# 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 2009 Sun Microsystems, Inc.  All rights reserved.
+# Use is subject to license terms.
+#
+
+PROG=		pkgremove
+
+OBJS=		check.o		\
+		delmap.o	\
+		main.o		\
+		predepend.o	\
+		quit.o		\
+		special.o	\
+		wsreg_pkgrm.o
+
+include $(SRC)/cmd/svr4pkg/Makefile.svr4pkg
+
+LDLIBS	+=	-lpkg -linstzones -ladm
+LDLIBS	+=	-lnsl -lsocket -lwsreg
+
+
+.KEEP_STATE:
+all:            $(PROG)
+
+install:        all $(ROOTPKGBINPROG)
+
+include $(SRC)/cmd/svr4pkg/Makefile.svr4pkg.targ
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/usr/src/cmd/svr4pkg/pkgremove/check.c	Fri Jun 05 10:28:40 2009 -0400
@@ -0,0 +1,328 @@
+/*
+ * 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
+ * or http://www.opensolaris.org/os/licensing.
+ * 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 2003 Sun Microsystems, Inc.  All rights reserved.
+ * Use is subject to license terms.
+ */
+
+/* Copyright (c) 1984, 1986, 1987, 1988, 1989 AT&T */
+/* All Rights Reserved */
+
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <string.h>
+#ifndef SUNOS41
+#include <utmpx.h>
+#endif
+#include <dirent.h>
+#include <sys/types.h>
+#include <pkgstrct.h>
+#include <locale.h>
+#include <libintl.h>
+#include "install.h"
+#include <pkglib.h>
+#include "libadm.h"
+#include "libinst.h"
+#include "wsreg_pkgrm.h"
+#include "messages.h"
+
+extern struct admin adm;
+/* extern struct cfent **eptlist; */
+
+extern char	pkgloc[], *pkginst, *msgtext;
+
+static boolean_t	preremoveCheck = B_FALSE;
+static char		*zoneName = (char *)NULL;
+
+
+void
+rcksetPreremoveCheck(boolean_t a_preremoveCheck)
+{
+	preremoveCheck = a_preremoveCheck;
+}
+
+void
+rcksetZoneName(char *a_zoneName)
+{
+	zoneName = a_zoneName;
+}
+
+int
+rckrunlevel(void)
+{
+	struct utmpx utmpx;
+	struct utmpx *putmpx;
+	char	ans[MAX_INPUT];
+	char	*pt;
+	char	*rstates;
+	int	n;
+	char	*uxstate;
+
+	if (ADM(runlevel, "nocheck")) {
+		return (0);
+	}
+
+	pt = getenv("RSTATES");
+	if (pt == NULL) {
+		return (0);
+	}
+
+	utmpx.ut_type = RUN_LVL;
+	putmpx = getutxid(&utmpx);
+	if (putmpx == NULL) {
+		progerr(ERR_RUNSTATE);
+		return (99);
+	}
+	uxstate = strtok(&putmpx->ut_line[10], " \t\n");
+
+	rstates = qstrdup(pt);
+	if ((pt = strtok(pt, " \t\n, ")) == NULL)
+		return (0); /* no list is no list */
+	do {
+		if (strcmp(pt, uxstate) == NULL) {
+			free(rstates);
+			return (0);
+		}
+	} while (pt = strtok(NULL, " \t\n, "));
+
+	if (preremoveCheck == B_FALSE) {
+		msgtext = MSG_PKGREMOVE_RUNLEVEL;
+		ptext(stderr, msgtext, uxstate);
+	} else {
+		(void) fprintf(stdout, "runlevel=%s", uxstate);
+	}
+
+	pt = strtok(rstates, " \t\n, ");
+	do {
+		if (preremoveCheck == B_FALSE) {
+			ptext(stderr, "\\t%s", pt);
+		} else {
+			(void) fprintf(stdout, ":%s", pt);
+		}
+	} while (pt = strtok(NULL, " \t\n, "));
+
+	if (preremoveCheck == B_TRUE) {
+		(void) fprintf(stdout, "\n");
+	}
+
+	free(rstates);
+
+	if (ADM(runlevel, "quit")) {
+		return (4);
+	}
+
+	if (echoGetFlag() == B_FALSE) {
+		return (5);
+	}
+
+	msgtext = NULL;
+
+	n = ckyorn(ans, NULL, NULL, HLP_PKGREMOVE_RUNLEVEL,
+	    ASK_PKGREMOVE_CONTINUE);
+
+	if (n != 0) {
+		return (n);
+	}
+
+	if (strchr("yY", *ans) == NULL) {
+		return (3);
+	}
+
+	return (0);
+}
+
+int
+rckpatchpkg(char *p, char *pt)
+{
+	int n;
+	char ans[MAX_INPUT];
+
+	ptext(stderr, WRN_PKGREMOVE_PATCHES, p, p, p, pt);
+
+	n = ckyorn(ans, NULL, NULL, NULL, ASK_PKGREMOVE_CONTINUE);
+
+	if (n != 0) {
+		return (n);
+	}
+
+	if (strchr("yY", *ans) == NULL) {
+		return (3);
+	}
+
+	return (0);
+}
+
+int
+rckdepend(void)
+{
+	int	n;
+	char	ans[MAX_INPUT];
+	char	**id, **name;
+
+	if (ADM(rdepend, "nocheck")) {
+		return (0);
+	}
+
+	if (zoneName == (char *)NULL) {
+		echo(MSG_CHECKREMOVE_PKG_IN_GZ, pkginst);
+	} else {
+		echo(MSG_CHECKREMOVE_PKG_IN_ZONE, pkginst, zoneName);
+	}
+
+	if (wsreg_pkgrm_check(get_inst_root(), pkginst, &id, &name) > 0) {
+		int i;
+
+		if (ADM(rdepend, "quit")) {
+			return (4);
+		}
+
+		if (echoGetFlag() == B_FALSE) {
+			return (5);
+		}
+
+		msgtext = MSG_PKGREMOVE_WSDEPEND;
+		echo(msgtext);
+
+		(void) printf("%-36s  %s", MSG_PKGREMOVE_ID_STR,
+		    MSG_PKGREMOVE_NAME_STR);
+		(void) printf("\n------------------------------------  "
+		    "--------------------------------------\n");
+
+		for (i = 0; id[i] != NULL; i++) {
+			(void) printf("%-36s  %s\n", id[i],
+			    (name[i])?(name[i]):"");
+			free(id[i]);
+			if (name[i]) {
+				free(name[i]);
+			}
+		}
+
+		free(id);
+		free(name);
+
+		msgtext = NULL;
+
+		n = ckyorn(ans, NULL, NULL, HLP_PKGREMOVE_WSDEPEND,
+		    ASK_PKGREMOVE_CONTINUE);
+
+		if (n != 0) {
+			return (n);
+		}
+
+		if (strchr("yY", *ans) == NULL) {
+			return (3);
+		}
+	}
+
+	if (dockdeps(pkginst, 1, preremoveCheck)) {
+		msgtext = MSG_PKGREMOVE_DEPEND;
+
+		if (preremoveCheck == B_FALSE) {
+			echo(msgtext);
+		}
+
+		if (ADM(rdepend, "quit")) {
+			return (4);
+		}
+
+		if (echoGetFlag() == B_FALSE) {
+			return (5);
+		}
+
+		msgtext = NULL;
+
+		n = ckyorn(ans, NULL, NULL, HLP_PKGREMOVE_DEPEND,
+		    ASK_PKGREMOVE_CONTINUE);
+
+		if (n != 0) {
+			return (n);
+		}
+
+		if (strchr("yY", *ans) == NULL) {
+			return (3);
+		}
+	}
+
+	return (0);
+}
+
+int
+rckpriv(void)
+{
+	struct dirent	*dp;
+	DIR		*dirfp;
+	int		n;
+	char		found;
+	char		ans[MAX_INPUT];
+	char		path[PATH_MAX];
+
+	if (ADM(action, "nocheck")) {
+		return (0);
+	}
+
+	(void) snprintf(path, sizeof (path), "%s/install", pkgloc);
+	if ((dirfp = opendir(path)) == NULL)
+		return (0);
+
+	found = 0;
+	while ((dp = readdir(dirfp)) != NULL) {
+		if ((strcmp(dp->d_name, "preremove") == NULL) ||
+		    (strcmp(dp->d_name, "postremove") == NULL) ||
+		    (strncmp(dp->d_name, "r.", 2) == NULL)) {
+			found++;
+			break;
+		}
+	}
+	(void) closedir(dirfp);
+
+	if (found) {
+		if (preremoveCheck == B_FALSE) {
+			ptext(stderr, MSG_PKGREMOVE_PRIV);
+		}
+		msgtext = MSG_PKGSCRIPTS_FOUND;
+
+		if (ADM(action, "quit")) {
+			return (4);
+		}
+
+		if (echoGetFlag() == B_FALSE) {
+			return (5);
+		}
+
+		msgtext = NULL;
+
+		n = ckyorn(ans, NULL, NULL, HLP_PKGREMOVE_PRIV,
+		    ASK_PKGREMOVE_CONTINUE);
+
+		if (n != 0) {
+			return (n);
+		}
+
+		if (strchr("yY", *ans) == NULL) {
+			return (3);
+		}
+	}
+
+	return (0);
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/usr/src/cmd/svr4pkg/pkgremove/delmap.c	Fri Jun 05 10:28:40 2009 -0400
@@ -0,0 +1,159 @@
+/*
+ * 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
+ * or http://www.opensolaris.org/os/licensing.
+ * 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 2006 Sun Microsystems, Inc.  All rights reserved.
+ * Use is subject to license terms.
+ */
+
+/* Copyright (c) 1984, 1986, 1987, 1988, 1989 AT&T */
+/* All Rights Reserved */
+
+
+#include <stdio.h>
+#include <errno.h>
+#include <string.h>
+#include <limits.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <sys/types.h>
+#include <pkgstrct.h>
+#include <locale.h>
+#include <libintl.h>
+#include <pkglib.h>
+#include <libadm.h>
+#include <libinst.h>
+
+extern int	dbchg, warnflag, otherstoo;
+extern char	*pkginst;
+
+#define	EPTMALLOC	128
+
+#define	ERR_WRENT	"write of entry failed, errno=%d"
+#define	ERR_MEMORY	"no memory, errno=%d"
+#define	ERR_READ_C	"bad read of contents file"
+#define	ERR_READ_DB	"bad read of the database"
+
+extern struct cfent	**eptlist;
+extern int	eptnum;
+
+int
+delmap(int flag, char *pkginst)
+{
+	struct cfent	*ept;
+	struct pinfo	*pinfo;
+	VFP_T		*vfp;
+	VFP_T		*vfpo;
+	int		n;
+	char		*unknown = "Unknown";
+
+
+	if (!ocfile(&vfp, &vfpo, 0L)) {
+		quit(99);
+	}
+
+	/* re-use any memory used to store pathnames */
+	(void) pathdup(NULL);
+
+	if (eptlist != NULL)
+		free(eptlist);
+	eptlist = (struct cfent **)calloc(EPTMALLOC,
+					sizeof (struct cfent *));
+	if (eptlist == NULL) {
+		progerr(gettext(ERR_MEMORY), errno);
+		quit(99);
+	}
+
+	ept = (struct cfent *)calloc(1,
+				(unsigned)sizeof (struct cfent));
+	if (!ept) {
+		progerr(gettext(ERR_MEMORY), errno);
+		quit(99);
+	}
+
+	eptnum = 0;
+	while (n = srchcfile(ept, "*", vfp, (VFP_T *)NULL)) {
+		if (n < 0) {
+			char	*errstr = getErrstr();
+			progerr(gettext("bad read of contents file"));
+			progerr(gettext("pathname=%s"),
+				(ept->path && *ept->path) ? ept->path :
+				unknown);
+			progerr(gettext("problem=%s"),
+				(errstr && *errstr) ? errstr : unknown);
+			exit(99);
+		}
+		pinfo = eptstat(ept, pkginst, (flag ? '@' : '-'));
+		if (ept->npkgs > 0) {
+			if (putcvfpfile(ept, vfpo)) {
+				progerr(gettext(ERR_WRENT), errno);
+				quit(99);
+			}
+		}
+
+		if (flag || (pinfo == NULL))
+			continue;
+
+		dbchg++;
+
+		/*
+		 * If (otherstoo > 0), more than one package has an
+		 * interest in the ept entry in the database. Setting
+		 * ept->ftype = '\0' effectively marks the file as being
+		 * "shared", thus ensuring the ept entry will not
+		 * subsequently be removed. Shared editable files (ftype
+		 * 'e') are a special case: they should be passed to a
+		 * class action script if present. Setting ept->ftype =
+		 * '^' indicates this special case of shared editable
+		 * file, allowing the distinction to be made later.
+		 */
+		if (!pinfo->editflag && otherstoo)
+			ept->ftype = (ept->ftype == 'e') ? '^' : '\0';
+		if (*pinfo->aclass)
+			(void) strcpy(ept->pkg_class, pinfo->aclass);
+		eptlist[eptnum] = ept;
+
+		ept->path = pathdup(ept->path);
+		if (ept->ainfo.local != NULL)
+			ept->ainfo.local = pathdup(ept->ainfo.local);
+
+		ept = (struct cfent *)calloc(1, sizeof (struct cfent));
+		if ((++eptnum % EPTMALLOC) == 0) {
+			eptlist = (struct cfent **)realloc(eptlist,
+			(eptnum+EPTMALLOC)*sizeof (struct cfent *));
+			if (eptlist == NULL) {
+				progerr(gettext(ERR_MEMORY), errno);
+				quit(99);
+			}
+		}
+	}
+
+	eptlist[eptnum] = (struct cfent *)NULL;
+
+	n = swapcfile(&vfp, &vfpo, pkginst, dbchg);
+	if (n == RESULT_WRN) {
+		warnflag++;
+	} else if (n == RESULT_ERR) {
+		quit(99);
+	}
+
+	return (0);
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/usr/src/cmd/svr4pkg/pkgremove/main.c	Fri Jun 05 10:28:40 2009 -0400
@@ -0,0 +1,1434 @@
+/*
+ * 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
+ * or http://www.opensolaris.org/os/licensing.
+ * 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 2009 Sun Microsystems, Inc.  All rights reserved.
+ * Use is subject to license terms.
+ */
+
+/* Copyright (c) 1984, 1986, 1987, 1988, 1989 AT&T */
+/* All Rights Reserved */
+
+
+#include <stdio.h>
+#include <limits.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <string.h>
+#include <signal.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <sys/stat.h>
+#include <sys/types.h>
+#include <pkgstrct.h>
+#include <pkginfo.h>
+#include <pkglocs.h>
+#include <locale.h>
+#include <libintl.h>
+#include <assert.h>
+#include <cfext.h>
+#include <instzones_api.h>
+#include <pkglib.h>
+#include <install.h>
+#include <libinst.h>
+#include <libadm.h>
+#include <messages.h>
+
+struct cfent **eptlist;
+extern int	eptnum;
+
+extern char	*pkgdir;
+extern char	**environ;
+
+/* quit.c */
+extern sighdlrFunc_t	*quitGetTrapHandler(void);
+extern void		quitSetSilentExit(boolean_t a_silentExit);
+extern void		quitSetZoneName(char *a_zoneName);
+
+
+
+/* check.c */
+extern void	rcksetPreremoveCheck(boolean_t);
+extern void	rcksetZoneName(char *);
+extern int	rckpriv(void);
+extern int	rckdepend(void);
+extern int	rckrunlevel(void);
+
+/* predepend.c */
+extern void	predepend(char *oldpkg);
+
+/* delmap.c */
+extern int delmap(int flag, char *pkginst);
+
+#define	DEFPATH		"/sbin:/usr/sbin:/usr/bin"
+
+#ifdef	ALLOW_EXCEPTION_PKG_LIST
+#define	SCRIPT	0	/* Tells exception_pkg() which pkg list to use */
+#define	LINK	1
+#endif
+
+#if !defined(TEXT_DOMAIN)	/* Should be defined by cc -D */
+#define	TEXT_DOMAIN "SYS_TEST"
+#endif
+
+/* This is the text for the "-O inherited-filesystem=" option */
+
+#define	INHERITFS	"inherited-filesystem="
+#define	INHERITFS_LEN	((sizeof (INHERITFS))-1)
+
+/* This is the text for the "-O parent-zone-name=" option */
+
+#define	PARENTZONENAME	"parent-zone-name="
+#define	PARENTZONENAME_LEN	((sizeof (PARENTZONENAME))-1)
+
+/* This is the text for the "-O parent-zone-type=" option */
+
+#define	PARENTZONETYPE	"parent-zone-type="
+#define	PARENTZONETYPE_LEN	((sizeof (PARENTZONETYPE))-1)
+
+struct	admin adm; 	/* holds info about installation admin */
+int	dreboot; 	/* non-zero if reboot required after installation */
+int	ireboot;	/* non-zero if immediate reboot required */
+int	failflag;	/* non-zero if fatal error has occurred */
+int	warnflag;	/* non-zero if non-fatal error has occurred */
+int	pkgverbose;	/* non-zero if verbose mode is selected */
+int	started;
+int	nocnflct = 0; 	/* pkgdbmerg needs this defined */
+int	nosetuid = 0; 	/* pkgdbmerg needs this defined */
+
+char	*pkginst; 	/* current package (source) instance to process */
+
+int	dbchg;
+char	*msgtext;
+char	pkgloc[PATH_MAX];
+
+/*
+ * The following variable is the name of the device to which stdin
+ * is connected during execution of a procedure script. /dev/null is
+ * correct for all ABI compliant packages. For non-ABI-compliant
+ * packages, the '-o' command line switch changes this to /dev/tty
+ * to allow user interaction during these scripts. -- JST
+ */
+static char 	*script_in = PROC_STDIN;	/* assume ABI compliance */
+
+static char	*client_mntdir; 	/* mount point for client's basedir */
+static char	pkgbin[PATH_MAX],
+		rlockfile[PATH_MAX],
+		*admnfile, 		/* file to use for installation admin */
+		*tmpdir; 		/* location to place temporary files */
+
+static boolean_t	path_valid(char *path);
+static void		ckreturn(int retcode, char *msg);
+static void		rmclass(char *aclass, int rm_remote, char *a_zoneName);
+static void		usage(void);
+
+/*
+ * Set by -O debug: debug output is enabled?
+ */
+static boolean_t	debugFlag = B_FALSE;
+
+/*
+ * Set by -O preremovecheck: do remove dependency checking only
+ */
+static boolean_t	preremoveCheck = B_FALSE;
+
+/* Set by -O parent-zone-name= */
+
+static char		*parentZoneName = (char *)NULL;
+
+/* Set by -O parent-zone-type= */
+
+static char		*parentZoneType = (char *)NULL;
+
+static int	nointeract;	/* != 0 no interaction with user should occur */
+
+
+
+int
+main(int argc, char *argv[])
+{
+	FILE		*fp;
+	char		*abi_comp_ptr;
+	char		*abi_sym_ptr;
+	char		*p;
+	char		*prog_full_name = NULL;
+	char		*pt;
+	char		*value;
+	char		*vfstab_file = NULL;
+	char		*zoneName = (char *)NULL;
+	char		cmdbin[PATH_MAX];
+	char		param[MAX_PKG_PARAM_LENGTH];
+	char		path[PATH_MAX];
+	char		script[PATH_MAX];
+	int		c;
+	int		err;
+	int		fd;
+	int		i;
+	int		map_client = 1;
+	int		n;
+	int		nodelete = 0; 	/* do not delete file or run scripts */
+	int		pkgrmremote = 0;	/* dont remove remote objects */
+	struct sigaction	nact;
+	struct sigaction	oact;
+
+	/* reset contents of all default paths */
+
+	(void) memset(cmdbin, '\0', sizeof (cmdbin));
+
+	/* initialize locale environment */
+
+	(void) setlocale(LC_ALL, "");
+	(void) textdomain(TEXT_DOMAIN);
+
+	/* initialize program name */
+
+	prog_full_name = argv[0];
+	(void) set_prog_name(argv[0]);
+
+	/* tell spmi zones interface how to access package output functions */
+
+	z_set_output_functions(echo, echoDebug, progerr);
+
+	/* exit if not root */
+
+	if (getuid()) {
+		progerr(ERR_NOT_ROOT, get_prog_name());
+		exit(1);
+		/* NOTREACHED */
+	}
+
+	/* Read PKG_INSTALL_ROOT from the environment, if it's there. */
+
+	if (!set_inst_root(getenv("PKG_INSTALL_ROOT"))) {
+		progerr(ERR_ROOT_SET);
+		exit(1);
+	}
+
+	/* parse command line options */
+
+	while ((c = getopt(argc, argv, "?Aa:b:FMN:nO:oR:V:vy")) != EOF) {
+		switch (c) {
+		/*
+		 * Same as pkgrm: Allow admin to remove package objects from
+		 * a shared area from a reference client.
+		 */
+		case 'A':
+		    pkgrmremote++;
+		    break;
+
+		/*
+		 * Same as pkgrm: Use the installation
+		 * administration file, admin, in place of the
+		 * default admin file. pkgrm first looks in the
+		 * current working directory for the administration
+		 * file.  If the specified administration file is not
+		 * in the current working directory, pkgrm looks in
+		 * the /var/sadm/install/admin directory for the
+		 * administration file.
+		 */
+		case 'a':
+		    admnfile = flex_device(optarg, 0);
+		    break;
+
+		/*
+		 * Same as pkgrm: location where package executables
+		 * can be found - default is /usr/sadm/install/bin.
+		 */
+		case 'b':
+			if (!path_valid(optarg)) {
+				progerr(ERR_PATH, optarg);
+				exit(1);
+			}
+			if (isdir(optarg) != 0) {
+				char *p = strerror(errno);
+				progerr(ERR_CANNOT_USE_DIR, optarg, p);
+				exit(1);
+			}
+			(void) strlcpy(cmdbin, optarg, sizeof (cmdbin));
+			break;
+
+		/*
+		 * Same as pkgrm: suppresses the removal of any
+		 * files and any class action scripts, and suppresses
+		 * the running of any class action scripts.  The
+		 * package files remain but the package looks like it
+		 * is not installed. This is mainly for use by the
+		 * upgrade process.
+		 */
+		case 'F':
+		    nodelete++;
+		    break;
+
+		/*
+		 * Same as pkgrm: Instruct pkgrm not to use the
+		 * $root_path/etc/vfstab file for determining the
+		 * client's mount points. This option assumes the
+		 * mount points are correct on the server and it
+		 * behaves consistently with Solaris 2.5 and earlier
+		 * releases.
+		 */
+		case 'M':
+		    map_client = 0;
+		    break;
+
+		/*
+		 * Different from pkgrm: specify program name to use
+		 * for messages.
+		 */
+		case 'N':
+		    (void) set_prog_name(optarg);
+		    break;
+
+		/*
+		 * Same as pkgrm: package removal occurs in
+		 * non-interactive mode.  Suppress output of the list of
+		 * removed files. The default mode is interactive.
+		 */
+		case 'n':
+		    nointeract++;
+		    (void) echoSetFlag(B_FALSE);
+		    break;
+
+		/*
+		 * Almost same as pkgrm: the -O option allows the behavior
+		 * of the package tools to be modified. Recognized options:
+		 * -> debug
+		 * ---> enable debugging output
+		 * -> preremovecheck
+		 * ---> perform a "pre removal" check of the specified
+		 * ---> package - suppress all regular output and cause a
+		 * ---> series of one or more "name=value" pair format lines
+		 * ---> to be output that describes the "removability" of
+		 * ---> the specified package
+		 * -> enable-hollow-package-support
+		 * --> Enable hollow package support. When specified, for any
+		 * --> package that has SUNW_PKG_HOLLOW=true:
+		 * --> Do not calculate and verify package size against target
+		 * --> Do not run any package procedure or class action scripts
+		 * --> Do not create or remove any target directories
+		 * --> Do not perform any script locking
+		 * --> Do not install or uninstall any components of any package
+		 * --> Do not output any status or database update messages
+		 */
+		case 'O':
+			for (p = strtok(optarg, ","); p != (char *)NULL;
+				p = strtok(NULL, ",")) {
+
+				/* process debug option */
+
+				if (strcmp(p, "debug") == 0) {
+					/* set debug flag/enable debug output */
+					debugFlag = B_TRUE;
+					(void) echoDebugSetFlag(debugFlag);
+
+					/* debug info on arguments to pkgadd */
+					for (n = 0; n < argc && argv[n]; n++) {
+						echoDebug(DBG_ARG, n, argv[n]);
+					}
+
+					continue;
+				}
+
+				/* process enable-hollow-package-support opt */
+
+				if (strcmp(p,
+					"enable-hollow-package-support") == 0) {
+					set_depend_pkginfo_DB(B_TRUE);
+					continue;
+				}
+
+				/* process inherited-filesystem= option */
+
+				if (strncmp(p, INHERITFS, INHERITFS_LEN) == 0) {
+					if (z_add_inherited_file_system(
+						p+INHERITFS_LEN) == B_FALSE) {
+						progerr(ERR_NOSUCH_INHERITED,
+							p+INHERITFS_LEN);
+						quit(1);
+						/* NOTREACHED */
+					}
+					continue;
+				}
+
+				/* process preremovecheck option */
+
+				if (strcmp(p, "preremovecheck") == 0) {
+					preremoveCheck = B_TRUE;
+					nointeract++;	/* -n */
+					nodelete++;	/* -F */
+					quitSetSilentExit(B_TRUE);
+					continue;
+				}
+
+				/* process addzonename option */
+
+				if (strcmp(p, "addzonename") == 0) {
+					zoneName = z_get_zonename();
+					quitSetZoneName(zoneName);
+					continue;
+				}
+
+				/* process parent-zone-name option */
+
+				if (strncmp(p, PARENTZONENAME,
+						PARENTZONENAME_LEN) == 0) {
+					parentZoneName = p+PARENTZONENAME_LEN;
+					continue;
+				}
+
+				/* process parent-zone-type option */
+
+				if (strncmp(p, PARENTZONETYPE,
+						PARENTZONETYPE_LEN) == 0) {
+					parentZoneType = p+PARENTZONETYPE_LEN;
+					continue;
+				}
+
+				/* option not recognized - issue warning */
+
+				progerr(ERR_INVALID_O_OPTION, p);
+				continue;
+			}
+			break;
+
+		/*
+		 * Different from pkgrm: This is an old non-ABI package
+		 */
+
+		case 'o':
+		    script_in = PROC_XSTDIN;
+		    break;
+
+		/*
+		 * Same as pkgrm: defines the full path name of a
+		 * directory to use as the root_path.  All files,
+		 * including package system information files, are
+		 * relocated to a directory tree starting in the
+		 * specified root_path.
+		 */
+		case 'R':
+		    if (!set_inst_root(optarg)) {
+			    progerr(ERR_ROOT_CMD);
+			    exit(1);
+		    }
+		    break;
+
+		/*
+		 * Same as pkgrm: allow admin to establish the client
+		 * filesystem using a vfstab-like file of stable format.
+		 */
+		case 'V':
+		    vfstab_file = flex_device(optarg, 2);
+		    map_client = 1;
+		    break;
+
+		/*
+		 * Same as pkgrm: trace all of the scripts that
+		 * get executed by pkgrm, located in the
+		 * pkginst/install directory. This option is used for
+		 * debugging the procedural and non-procedural
+		 * scripts.
+		 */
+		case 'v':
+		    pkgverbose++;
+		    break;
+
+		/*
+		 * Different from pkgrm: process this package using
+		 * old non-ABI symlinks
+		 */
+		case 'y':
+		    set_nonABI_symlinks();
+		    break;
+
+		default:
+		    usage();
+		    /*NOTREACHED*/
+		    /*
+		     * Although usage() calls a noreturn function,
+		     * needed to add return (1);  so that main() would
+		     * pass compilation checks. The statement below
+   		     * should never be executed.
+		     */
+		    return (1);
+		}
+	}
+
+	/*
+	 * ********************************************************************
+	 * validate command line options
+	 * ********************************************************************
+	 */
+
+	(void) echoDebugSetFlag(debugFlag);
+	(void) log_set_verbose(debugFlag);
+
+	if (z_running_in_global_zone()) {
+		echoDebug(DBG_ENTRY_IN_GZ, prog_full_name);
+	} else {
+		echoDebug(DBG_ENTRY_IN_LZ, prog_full_name, getzoneid(),
+			z_get_zonename());
+	}
+
+	/* establish cmdbin path */
+
+	if (cmdbin[0] == '\0') {
+		(void) strlcpy(cmdbin, PKGBIN, sizeof (cmdbin));
+	}
+
+	/* Read the mount table */
+
+	if (get_mntinfo(map_client, vfstab_file)) {
+		quit(99);
+	}
+
+	/*
+	 * This function defines the standard /var/... directories used later
+	 * to construct the paths to the various databases.
+	 */
+
+	set_PKGpaths(get_inst_root());
+
+	/*
+	 * If this is being removed from a client whose /var filesystem is
+	 * mounted in some odd way, remap the administrative paths to the
+	 * real filesystem. This could be avoided by simply mounting up the
+	 * client now; but we aren't yet to the point in the process where
+	 * modification of the filesystem is permitted.
+	 */
+	if (is_an_inst_root()) {
+		int fsys_value;
+
+		fsys_value = fsys(get_PKGLOC());
+		if (use_srvr_map_n(fsys_value))
+			set_PKGLOC(server_map(get_PKGLOC(), fsys_value));
+
+		fsys_value = fsys(get_PKGADM());
+		if (use_srvr_map_n(fsys_value))
+			set_PKGADM(server_map(get_PKGADM(), fsys_value));
+	} else {
+		pkgrmremote = 0;	/* Makes no sense on local host. */
+	}
+
+	/*
+	 * hook SIGINT and SIGHUP interrupts into quit.c's trap handler
+	 */
+
+	/* hold SIGINT/SIGHUP interrupts */
+
+	(void) sighold(SIGHUP);
+	(void) sighold(SIGINT);
+
+	/* connect quit.c:trap() to SIGINT */
+
+	nact.sa_handler = quitGetTrapHandler();
+	nact.sa_flags = SA_RESTART;
+	(void) sigemptyset(&nact.sa_mask);
+
+	(void) sigaction(SIGINT, &nact, &oact);
+
+	/* connect quit.c:trap() to SIGHUP */
+
+	nact.sa_handler = quitGetTrapHandler();
+	nact.sa_flags = SA_RESTART;
+	(void) sigemptyset(&nact.sa_mask);
+
+	(void) sigaction(SIGHUP, &nact, &oact);
+
+	/* release hold on signals */
+
+	(void) sigrelse(SIGHUP);
+	(void) sigrelse(SIGINT);
+
+	pkginst = argv[optind++];
+	if (optind != argc) {
+		usage();
+	}
+
+	/* validate package software database (contents) file */
+
+	if (vcfile() == 0) {
+		quit(99);
+	}
+
+	/*
+	 * Acquire the package lock - currently at "remove initialization"
+	 */
+
+	if (!lockinst(get_prog_name(), pkginst, "remove-initial")) {
+		quit(99);
+	}
+
+	/* establish temporary directory to use */
+
+	tmpdir = getenv("TMPDIR");
+	if (tmpdir == NULL) {
+		tmpdir = P_tmpdir;
+	}
+
+	echoDebug(DBG_PKGREMOVE_TMPDIR, tmpdir);
+
+	/*
+	 * Initialize installation admin parameters by reading
+	 * the adminfile.
+	 */
+
+	echoDebug(DBG_PKGREMOVE_ADMINFILE, admnfile ? admnfile : "");
+	setadminFile(admnfile);
+
+	/*
+	 * about to perform first operation that could be modified by the
+	 * preremove check option - if preremove check is selected (that is,
+	 * only gathering dependencies), then output a debug message to
+	 * indicate that the check is beginning. Also turn echo() output
+	 * off and set various other flags.
+	 */
+
+	if (preremoveCheck == B_TRUE) {
+		(void) echoSetFlag(B_FALSE);
+		echoDebug(DBG_PKGREMOVE_PRERMCHK, pkginst ? pkginst : "",
+			zoneName ? zoneName : "global");
+		rcksetPreremoveCheck(B_TRUE);
+		rcksetZoneName(zoneName);
+	}
+
+	(void) snprintf(pkgloc, sizeof (pkgloc), "%s/%s", get_PKGLOC(),
+			pkginst);
+	(void) snprintf(pkgbin, sizeof (pkgbin), "%s/install", pkgloc);
+	(void) snprintf(rlockfile, sizeof (rlockfile), "%s/!R-Lock!", pkgloc);
+
+	if (chdir(pkgbin)) {
+		progerr(ERR_CHDIR, pkgbin);
+		quit(99);
+	}
+
+	echo(MSG_PREREMOVE_REMINST, pkginst);
+
+	/*
+	 * if a lock file is present, then a previous attempt to remove this
+	 * package may have been unsuccessful.
+	 */
+
+	if (access(rlockfile, F_OK) == 0) {
+		echo(ERR_UNSUCC);
+		echoDebug(DBG_PKGINSTALL_HAS_LOCKFILE, pkginst, rlockfile,
+			zoneName ? zoneName : "global");
+	}
+
+	/*
+	 * Process all parameters from the pkginfo file
+	 * and place them in the execution environment
+	 */
+
+	/* Add DB retreival of the pkginfo parameters here */
+	(void) snprintf(path, sizeof (path), "%s/pkginfo", pkgloc);
+	if ((fp = fopen(path, "r")) == NULL) {
+		progerr(ERR_PKGINFO, path);
+		quit(99);
+	}
+
+	/* Mount up the client if necessary. */
+	if (map_client && !mount_client()) {
+		logerr(MSG_MANMOUNT);
+	}
+
+	/* Get mount point of client */
+	client_mntdir = getenv("CLIENT_MNTDIR");
+
+	getuserlocale();
+
+	/*
+	 * current environment has been read; clear environment out
+	 * so putparam() can be used to populate the new environment
+	 * to be passed to any executables/scripts.
+	 */
+
+	environ = NULL;
+
+	if (nonABI_symlinks()) {
+		putparam("PKG_NONABI_SYMLINKS", "TRUE");
+	}
+
+	/*
+	 * read the pkginfo file and fix any PKGSAV path - the correct
+	 * install_root will be prepended to the existing path.
+	 */
+
+	param[0] = '\0';
+	while (value = fpkgparam(fp, param)) {
+		int validx = 0;
+		char *newvalue;
+
+		/* strip out any setting of PATH */
+
+		if (strcmp(param, "PATH") == 0) {
+			free(value);
+			param[0] = '\0';
+			continue;
+		}
+
+		/* if not PKGSAV then write out unchanged */
+
+		if (strcmp(param, "PKGSAV") != 0) {
+			putparam(param, value);
+			free(value);
+			param[0] = '\0';
+			continue;
+		}
+
+		/*
+		 * PKGSAV parameter found - interpret the directory:
+		 * If in host:path format or marked with the leading "//",
+		 * then there is no client-relative translation - take it
+		 * literally later rather than use fixpath().
+		 */
+
+		if (strstr(value, ":/")) {
+			/* no modification needed */
+			validx = 0;
+		} else if (strstr(value, "//") == value) {
+			validx = 1;
+		} else if (is_an_inst_root()) {
+			/* This PKGSAV needs to be made client-relative. */
+			newvalue = fixpath(value);
+			free(value);
+			value = newvalue;
+		}
+		putparam(param, value+validx);
+		free(value);
+		param[0] = '\0';
+	}
+
+	(void) fclose(fp);
+
+	/* write parent condition information to environment */
+
+	putConditionInfo(parentZoneName, parentZoneType);
+
+	putuserlocale();
+
+	/*
+	 * Now do all the various setups based on ABI compliance
+	 */
+
+	/* Read the environment provided by the pkginfo file */
+	abi_comp_ptr = getenv("NONABI_SCRIPTS");
+
+	/* if not ABI compliant set global flag */
+	abi_sym_ptr = getenv("PKG_NONABI_SYMLINKS");
+	if (abi_sym_ptr && strncasecmp(abi_sym_ptr, "TRUE", 4) == 0) {
+		set_nonABI_symlinks();
+	}
+
+	/*
+	 * If pkginfo says it's not compliant then set non_abi_scripts.
+	 * Oh, for two releases, set it from exception package names as
+	 * well.
+	 */
+	/*
+	 * *********************************************************************
+	 * this feature is removed starting with Solaris 10 - there is no built
+	 * in list of packages that should be run "the old way"
+	 * *********************************************************************
+	 */
+
+#ifdef	ALLOW_EXCEPTION_PKG_LIST
+	if (exception_pkg(pkginst, SCRIPT) ||
+	    (abi_comp_ptr && strncmp(abi_comp_ptr, "TRUE", 4) == 0))
+		script_in = PROC_XSTDIN;
+#else
+	if (abi_comp_ptr && strncmp(abi_comp_ptr, "TRUE", 4) == 0) {
+		script_in = PROC_XSTDIN;
+	}
+#endif
+	/*
+	 * *********************************************************************
+	 * this feature is removed starting with Solaris 10 - there is no built
+	 * in list of packages that should be run "the old way"
+	 * *********************************************************************
+	 */
+
+#ifdef	ALLOW_EXCEPTION_PKG_LIST
+	/* Until 2.9, set it from the execption list */
+	if (exception_pkg(pkginst, LINK)) {
+		set_nonABI_symlinks();
+	}
+#endif
+
+	/*
+	 * Since this is a removal, we can tell whether it's absolute or
+	 * not from the resident pkginfo file read above.
+	 */
+	if ((err = set_basedirs((getenv("BASEDIR") != NULL), adm.basedir,
+	    pkginst, nointeract)) != 0) {
+		quit(err);
+	}
+
+	/*
+	 * See if were are removing a package that only wants to update
+	 * the database or only remove files associated with CAS's. We
+	 * only check the PKG_HOLLOW_VARIABLE variable if told to do so by
+	 * the caller.
+	 */
+
+	if (is_depend_pkginfo_DB()) {
+		pt = getenv(PKG_HOLLOW_VARIABLE);
+
+		if ((pt != NULL) && (strncasecmp(pt, "true", 4) == 0)) {
+			echoDebug(DBG_PKGREMOVE_HOLLOW_ENABLED);
+
+			/*
+			 * this is a hollow package and hollow package support
+			 * is enabled -- override admin settings to suppress
+			 * checks that do not make sense since no scripts will
+			 * be executed and no files will be removed.
+			 */
+
+			setadminSetting("conflict", "nocheck");
+			setadminSetting("setuid", "nocheck");
+			setadminSetting("action", "nocheck");
+			setadminSetting("partial", "nocheck");
+			setadminSetting("space", "nocheck");
+			setadminSetting("authentication", "nocheck");
+		} else {
+			echoDebug(DBG_PKGREMOVE_HOLLOW_DISABLED);
+			set_depend_pkginfo_DB(B_FALSE);
+		}
+	}
+
+	put_path_params();
+
+	/* If client mount point, add it to pkgremove environment */
+
+	if (client_mntdir != NULL) {
+		putparam("CLIENT_MNTDIR", client_mntdir);
+	}
+
+	/* Establish the class list and the class attributes. */
+
+	if ((value = getenv("CLASSES")) != NULL) {
+		cl_sets(qstrdup(value));
+	} else {
+		progerr(ERR_CLASSES, path);
+		quit(99);
+	}
+
+	/* establish path and tmpdir */
+
+	if (cmdbin[0] == '\0') {
+		(void) strlcpy(cmdbin, PKGBIN, sizeof (cmdbin));
+	}
+
+	(void) snprintf(path, sizeof (path), "%s:%s", DEFPATH, cmdbin);
+	putparam("PATH", path);
+
+	putparam("TMPDIR", tmpdir);
+
+	/*
+	 * Check ulimit requirement (provided in pkginfo). The purpose of
+	 * this limit is to terminate pathological file growth resulting from
+	 * file edits in scripts. It does not apply to files in the pkgmap
+	 * and it does not apply to any database files manipulated by the
+	 * installation service.
+	 */
+	if (value = getenv("ULIMIT")) {
+		if (assign_ulimit(value) == -1) {
+			progerr(ERR_BADULIMIT, value);
+			warnflag++;
+		}
+		putparam("PKG_ULIMIT", "TRUE");
+	}
+
+	/*
+	 * If only gathering dependencies, check and output status of all
+	 * remaining dependencies and exit.
+	 */
+
+	if (preremoveCheck == B_TRUE) {
+		/*
+		 * make sure current runlevel is appropriate
+		 */
+
+		(void) fprintf(stdout, "rckrunlevel=%d\n", rckrunlevel());
+
+		/*
+		 * determine if any packaging scripts provided with
+		 * this package will execute as a priviledged user
+		 */
+
+		(void) fprintf(stdout, "rckpriv=%d\n", rckpriv());
+
+		/*
+		 * verify package dependencies
+		 */
+
+		(void) fprintf(stdout, "rckdepend=%d\n", rckdepend());
+
+		/*
+		 * ****** preremove check done - exit ******
+		 */
+
+		echoDebug(DBG_PKGREMOVE_PRERMCHK_OK);
+		quit(0);
+		/*NOTREACHED*/
+	}
+
+	/*
+	 * Not gathering dependencies only, proceed to check dependencies
+	 * and continue with the package removal operation.
+	 */
+
+	/*
+	 * make sure current runlevel is appropriate
+	 */
+
+	n = rckrunlevel();
+
+	if (n != 0) {
+		quit(n);
+		/* NOTREACHED */
+	}
+
+	/*
+	 * determine if any packaging scripts provided with
+	 * this package will execute as a priviledged user
+	 */
+
+	n = rckpriv();
+
+	if (n != 0) {
+		quit(n);
+		/* NOTREACHED */
+	}
+
+	/*
+	 * verify package dependencies
+	 */
+	n = rckdepend();
+
+	if (n != 0) {
+		quit(n);
+		/* NOTREACHED */
+	}
+
+	/*
+	 * *********************************************************************
+	 * the actual removal of the package begins here
+	 * *********************************************************************
+	 */
+
+	/*
+	 * create lockfile to indicate start of removal
+	 */
+	started++;
+	if ((fd = open(rlockfile, O_WRONLY|O_CREAT|O_TRUNC, 0644)) < 0) {
+		progerr(ERR_LOCKFILE, rlockfile);
+		quit(99);
+	} else {
+		(void) close(fd);
+	}
+
+	if (zoneName == (char *)NULL) {
+		echo(MSG_PKGREMOVE_PROCPKG_GZ);
+		echoDebug(DBG_PKGREMOVE_PROCPKG_GZ, pkginst, rlockfile);
+	} else {
+		echo(MSG_PKGREMOVE_PROCPKG_LZ, zoneName);
+		echoDebug(DBG_PKGREMOVE_PROCPKG_LZ, pkginst, rlockfile,
+			zoneName);
+	}
+	if (delmap(0, pkginst) != 0) {
+		progerr(ERR_DB_QUERY, pkginst);
+		quit(99);
+	}
+
+	/*
+	 * Run a preremove script if one is provided by the package.
+	 * Don't execute preremove script if only updating the DB.
+	 * Don't execute preremove script if files are not being deleted.
+	 * Don't execute preremove script if one or more files reside in
+	 * an inherited FS.
+	 */
+
+	/* update the lock - at the preremove script */
+	lockupd("preremove");
+
+	/* execute preremove script if one is provided */
+	(void) snprintf(script, sizeof (script), "%s/preremove", pkgbin);
+	if (access(script, F_OK) != 0) {
+		/* no script present */
+		echoDebug(DBG_PKGREMOVE_POC_NONE, pkginst,
+			zoneName ? zoneName : "global");
+	} else if (nodelete) {
+		/* not deleting files: skip preremove script */
+		echoDebug(DBG_PKGREMOVE_POC_NODEL, pkginst, script,
+			zoneName ? zoneName : "global");
+	} else if (is_depend_pkginfo_DB()) {
+		/* updating db only: skip preremove script */
+		echoDebug(DBG_PKGREMOVE_POC_DBUPD, pkginst, script,
+			zoneName ? zoneName : "global");
+	} else {
+		/* script present and ok to run: run the script */
+		set_ulimit("preremove", ERR_PREREMOVE);
+		if (zoneName == (char *)NULL) {
+			echo(MSG_PKGREMOVE_EXEPOC_GZ);
+			echoDebug(DBG_PKGREMOVE_EXEPOC_GZ, pkginst, script);
+		} else {
+			echo(MSG_PKGREMOVE_EXEPOC_LZ, zoneName);
+			echoDebug(DBG_PKGREMOVE_EXEPOC_LZ, pkginst, script,
+				zoneName);
+		}
+		putparam("PKG_PROC_SCRIPT", "preremove");
+		if (pkgverbose) {
+			ckreturn(pkgexecl(script_in, PROC_STDOUT,
+				PROC_USER, PROC_GRP, SHELL, "-x",
+				script, NULL), ERR_PREREMOVE);
+		} else {
+			ckreturn(pkgexecl(script_in, PROC_STDOUT,
+				PROC_USER, PROC_GRP, SHELL, script,
+				NULL), ERR_PREREMOVE);
+		}
+		clr_ulimit();
+	}
+
+	/* update the lock - doing removal */
+
+	lockupd("remove");
+
+	/*
+	 * Ensure that the contents file is updated even if the db has
+	 * been upgraded, in the case that there are relevant entries
+	 * in a special_contents file.  The return value is ignored
+	 * since we do not want special_contents operation to prevent
+	 * pkgremove from succeeding.  We do report errors to stderr.
+	 */
+
+	/*
+	 * Remove all components belonging to this package.
+	 * Don't remove components if only updating the DB.
+	 * Don't remove components if files are not being deleted.
+	 */
+
+	if (nodelete) {
+		echoDebug(DBG_PKGREMOVE_REM_NODEL, pkginst,
+			zoneName ? zoneName : "global");
+	} else if (is_depend_pkginfo_DB()) {
+		echoDebug(DBG_PKGREMOVE_REM_DBUPD, pkginst,
+			zoneName ? zoneName : "global");
+	} else {
+		echoDebug(DBG_PKGREMOVE_REM, pkginst,
+			zoneName ? zoneName : "global");
+		/*
+		 * remove package one class at a time
+		 */
+
+		/* reverse order of classes */
+		for (i = cl_getn() - 1; i >= 0; i--) {
+			rmclass(cl_nam(i), pkgrmremote, zoneName);
+		}
+
+		rmclass(NULL, pkgrmremote, zoneName);
+	}
+
+	z_destroyMountTable();
+
+	/*
+	 * Execute postremove script, if any
+	 * Don't execute postremove script if only updating the DB.
+	 * Don't execute postremove script if files are not being deleted.
+	 */
+
+	/* update the lock - at the postremove script */
+	lockupd("postremove");
+
+	/* execute postremove script if one is provided */
+	(void) snprintf(script, sizeof (script), "%s/postremove", pkgbin);
+	if (access(script, F_OK) != 0) {
+		/* no script present */
+		echoDebug(DBG_PKGREMOVE_PIC_NONE, pkginst,
+			zoneName ? zoneName : "global");
+	} else if (nodelete) {
+		/* not deleting files: skip postremove script */
+		echoDebug(DBG_PKGREMOVE_PIC_NODEL, pkginst, script,
+			zoneName ? zoneName : "global");
+	} else if (is_depend_pkginfo_DB()) {
+		/* updating db only: skip postremove script */
+		echoDebug(DBG_PKGREMOVE_PIC_DBUPD, pkginst, script,
+			zoneName ? zoneName : "global");
+	} else {
+		/* script present and ok to run: run the script */
+		set_ulimit("postremove", ERR_POSTREMOVE);
+		if (zoneName == (char *)NULL) {
+			echo(MSG_PKGREMOVE_EXEPIC_GZ);
+			echoDebug(DBG_PKGREMOVE_EXEPIC_GZ, pkginst, script);
+		} else {
+			echo(MSG_PKGREMOVE_EXEPIC_LZ, zoneName);
+			echoDebug(DBG_PKGREMOVE_EXEPIC_LZ, pkginst, script,
+				zoneName);
+		}
+		putparam("PKG_PROC_SCRIPT", "postremove");
+		putparam("TMPDIR", tmpdir);
+		if (pkgverbose) {
+			ckreturn(pkgexecl(script_in, PROC_STDOUT, PROC_USER,
+			    PROC_GRP, SHELL, "-x", script, NULL),
+			    ERR_POSTREMOVE);
+		} else {
+			ckreturn(pkgexecl(script_in, PROC_STDOUT, PROC_USER,
+			    PROC_GRP, SHELL, script, NULL),
+			    ERR_POSTREMOVE);
+		}
+		clr_ulimit();
+	}
+
+	if (zoneName == (char *)NULL) {
+		echo(MSG_PKGREMOVE_UPDINF_GZ);
+	} else {
+		echo(MSG_PKGREMOVE_UPDINF_LZ, zoneName);
+	}
+
+	if (delmap(1, pkginst) != 0) {
+		progerr(ERR_DB_QUERY, pkginst);
+		quit(99);
+	}
+
+	if (!warnflag && !failflag) {
+		if (pt = getenv("PREDEPEND"))
+			predepend(pt);
+		(void) chdir("/");
+		if (rrmdir(pkgloc))
+			warnflag++;
+	}
+
+	if ((z_running_in_global_zone() == B_TRUE) &&
+		(pkgIsPkgInGzOnly(get_inst_root(), pkginst) == B_TRUE)) {
+		boolean_t	b;
+
+		b = pkgRemovePackageFromGzonlyList(get_inst_root(), pkginst);
+		if (b == B_FALSE) {
+			progerr(ERR_PKGREMOVE_GZONLY_REMOVE, pkginst);
+			ckreturn(1, NULL);
+		}
+	}
+
+	/* release the generic package lock */
+
+	(void) unlockinst();
+
+	quit(0);
+	/* LINTED: no return */
+}
+
+int
+issymlink(char *path)
+{
+	struct stat statbuf;
+
+	/*
+	 * Obtain status of path; if symbolic link get link's status
+	 */
+
+	if (lstat(path, &statbuf) != 0) {
+		return (1);	/* not symlink */
+	}
+
+	/*
+	 * Status obtained - if symbolic link, return 0
+	 */
+
+	if ((statbuf.st_mode & S_IFMT) == S_IFLNK) {
+		return (0);	/* is a symlink */
+	}
+
+	/*
+	 * Not a symbolic link - return 1
+	 */
+
+	return (1);		/* not symlink */
+}
+
+static void
+rmclass(char *aclass, int rm_remote, char *a_zoneName)
+{
+	struct cfent	*ept;
+	FILE	*fp;
+	char	tmpfile[PATH_MAX];
+	char	script[PATH_MAX];
+	int	i;
+	char	*tmp_path;
+	char	*save_path = NULL;
+	struct stat st;
+
+	if (aclass == NULL) {
+		for (i = 0; i < eptnum; i++) {
+			if (eptlist[i] != NULL) {
+				rmclass(eptlist[i]->pkg_class,
+					rm_remote, a_zoneName);
+			}
+		}
+		return;
+	}
+
+	/* locate class action script to execute */
+	(void) snprintf(script, sizeof (script), "%s/r.%s", pkgbin, aclass);
+	if (access(script, F_OK) != 0) {
+		(void) snprintf(script, sizeof (script), "%s/r.%s",
+		    PKGSCR, aclass);
+		if (access(script, F_OK) != 0)
+			script[0] = '\0';
+	}
+	if (script[0] != '\0') {
+		int td;
+
+		(void) snprintf(tmpfile, sizeof (tmpfile), "%s/RMLISTXXXXXX",
+		    tmpdir);
+		td = mkstemp(tmpfile);
+		if (td == -1) {
+			progerr(ERR_TMPFILE);
+			quit(99);
+		}
+		if ((fp = fdopen(td, "w")) == NULL) {
+			progerr(ERR_WTMPFILE, tmpfile);
+			quit(99);
+		}
+	}
+
+	if (a_zoneName == (char *)NULL) {
+		echo(MSG_PKGREMOVE_REMPATHCLASS_GZ, aclass);
+	} else {
+		echo(MSG_PKGREMOVE_REMPATHCLASS_LZ, aclass, a_zoneName);
+	}
+
+	/* process paths in reverse order */
+	i = eptnum;
+	while (--i >= 0) {
+		ept = eptlist[i];
+
+		if ((ept == NULL) || strcmp(aclass, ept->pkg_class)) {
+			continue;
+		}
+
+		/* save the path, and prepend the ir */
+		if (is_an_inst_root()) {
+			save_path = ept->path;
+			tmp_path = fixpath(ept->path);
+			ept->path = tmp_path;
+		}
+
+		if (!ept->ftype || (ept->ftype == '^' && !script[0])) {
+			/*
+			 * A path owned by more than one package is marked with
+			 * a NULL ftype (seems odd, but that's how it's
+			 * done). Such files are sacro sanct. Shared editable
+			 * files are a special case, and are marked with an
+			 * ftype of '^'. These files should only be ignored if
+			 * no class action script is present. It is the CAS's
+			 * responsibility to not remove the editable object.
+			 */
+			echo(MSG_SHARED, ept->path);
+		} else if (ept->pinfo->status == SERVED_FILE && !rm_remote) {
+			/*
+			 * If the path is provided to the client from a
+			 * server, don't remove anything unless explicitly
+			 * requested through the "-f" option.
+			 */
+			echo(MSG_SERVER, ept->path);
+		} else if (z_path_is_inherited(ept->path, ept->ftype,
+		    get_inst_root())) {
+			/*
+			 * object is in an area inherited from the global zone,
+			 * and the object cannot be removed - output a message
+			 * indicating the object cannot be removed and continue.
+			 */
+			echo(MSG_NOTREMOVED_INHERITED, ept->path);
+		} else if (script[0]) {
+			/*
+			 * If there's a class action script, just put the
+			 * path name into the list.
+			 */
+			(void) fprintf(fp, "%s\n", ept->path);
+		} else if (strchr("dx", ept->ftype) != NULL ||
+		    (lstat(ept->path, &st) == 0 && S_ISDIR(st.st_mode))) {
+			/* Directories are rmdir()'d. */
+
+			if (rmdir(ept->path)) {
+				if (errno == EBUSY) {
+					echo(MSG_DIRBUSY, ept->path);
+				} else if (errno == EEXIST) {
+					echo(MSG_NOTEMPTY, ept->path);
+				} else if (errno != ENOENT) {
+					progerr(ERR_RMDIR, ept->path);
+					warnflag++;
+				}
+			} else {
+				if (ept->pinfo->status == SERVED_FILE) {
+					echo(MSG_RMSRVR, ept->path);
+				} else {
+					echo("%s", ept->path);
+				}
+			}
+
+		} else {
+			/*
+			 * Before removing this object one more
+			 * check should be done to assure that a
+			 * shared object is not removed.
+			 * This can happen if the original object
+			 * was incorrectly updated with the
+			 * incorrect class identifier.
+			 * This handles pathologcal cases that
+			 * weren't handled above.
+			 */
+			if (ept->npkgs > 1) {
+				echo(MSG_SHARED, ept->path);
+				continue;
+			}
+
+			/* Regular files are unlink()'d. */
+
+			if (unlink(ept->path)) {
+				if (errno != ENOENT) {
+					progerr(ERR_RMPATH, ept->path);
+					warnflag++;
+				}
+			} else {
+				if (ept->pinfo->status == SERVED_FILE) {
+					echo(MSG_RMSRVR, ept->path);
+				} else {
+					echo("%s", ept->path);
+				}
+			}
+		}
+
+		/* restore the original path */
+
+		if (is_an_inst_root()) {
+			ept->path = save_path;
+		}
+
+		/*
+		 * free memory allocated for this entry memory used for
+		 * pathnames will be freed later by a call to pathdup()
+		 */
+
+		if (eptlist[i]) {
+			free(eptlist[i]);
+		}
+		eptlist[i] = NULL;
+	}
+	if (script[0]) {
+		(void) fclose(fp);
+		set_ulimit(script, ERR_CASFAIL);
+		if (pkgverbose)
+			ckreturn(pkgexecl(tmpfile, CAS_STDOUT, CAS_USER,
+			    CAS_GRP, SHELL, "-x", script, NULL),
+			    ERR_CASFAIL);
+		else
+			ckreturn(pkgexecl(tmpfile, CAS_STDOUT, CAS_USER,
+			    CAS_GRP, SHELL, script, NULL),
+			    ERR_CASFAIL);
+		clr_ulimit();
+		if (isfile(NULL, tmpfile) == 0) {
+			if (unlink(tmpfile) == -1)
+				progerr(ERR_RMPATH, tmpfile);
+		}
+	}
+}
+
+static void
+ckreturn(int retcode, char *msg)
+{
+	switch (retcode) {
+	    case 2:
+	    case 12:
+	    case 22:
+		warnflag++;
+		/*FALLTHRU*/
+		if (msg)
+			progerr(msg);
+	    case 10:
+	    case 20:
+		if (retcode >= 10)
+			dreboot++;
+		if (retcode >= 20)
+			ireboot++;
+		/*FALLTHRU*/
+	    case 0:
+		break; /* okay */
+
+	    case -1:
+		retcode = 99;
+		/*FALLTHRU*/
+	    case 99:
+	    case 1:
+	    case 11:
+	    case 21:
+	    case 4:
+	    case 14:
+	    case 24:
+	    case 5:
+	    case 15:
+	    case 25:
+		if (msg)
+			progerr(msg);
+		/*FALLTHRU*/
+	    case 3:
+	    case 13:
+	    case 23:
+		quit(retcode);
+		/* NOT REACHED */
+	    default:
+		if (msg)
+			progerr(msg);
+		quit(1);
+	}
+}
+
+static void
+usage(void)
+{
+	(void) fprintf(stderr, ERR_USAGE_PKGREMOVE);
+
+	exit(1);
+}
+
+/*
+ * Name:		path_valid
+ * Description:	Checks a string for being a valid path
+ *
+ * Arguments:	path - path to validate
+ *
+ * Returns :	B_TRUE - success, B_FALSE otherwise.
+ *		B_FALSE means path was null, too long (>PATH_MAX),
+ *		or too short (<1)
+ */
+static boolean_t
+path_valid(char *path)
+{
+	if (path == NULL) {
+		return (B_FALSE);
+	} else if (strlen(path) > PATH_MAX) {
+		return (B_FALSE);
+	} else if (strlen(path) >= 1) {
+		return (B_TRUE);
+	} else {
+		/* path < 1 */
+		return (B_FALSE);
+	}
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/usr/src/cmd/svr4pkg/pkgremove/predepend.c	Fri Jun 05 10:28:40 2009 -0400
@@ -0,0 +1,72 @@
+/*
+ * 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
+ * or http://www.opensolaris.org/os/licensing.
+ * 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 1993 Sun Microsystems, Inc.  All rights reserved.
+ * Use is subject to license terms.
+ */
+
+/* Copyright (c) 1984, 1986, 1987, 1988, 1989 AT&T */
+/* All Rights Reserved */
+
+
+#include <stdio.h>
+#include <string.h>
+#include <limits.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <errno.h>
+#include <locale.h>
+#include <libintl.h>
+#include <pkglocs.h>
+#include "pkglib.h"
+#include "libinst.h"
+#include "libadm.h"
+
+extern int	warnflag;
+
+#define	ERR_UNLINK	"unable to unlink <%s>"
+
+void
+predepend(char *oldpkg)
+{
+	struct stat status;
+	char	spath[PATH_MAX];
+
+	oldpkg = strtok(oldpkg, " \t\n");
+	if (oldpkg == NULL)
+		return;
+
+	do {
+		(void) sprintf(spath, "%s/%s.name", get_PKGOLD(), oldpkg);
+		if (lstat(spath, &status) == 0) {
+			if (status.st_mode & S_IFLNK) {
+				if (unlink(spath)) {
+					progerr(gettext(ERR_UNLINK), spath);
+					warnflag++;
+				}
+				return;
+			}
+		}
+	} while (oldpkg = strtok(NULL, " \t\n"));
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/usr/src/cmd/svr4pkg/pkgremove/quit.c	Fri Jun 05 10:28:40 2009 -0400
@@ -0,0 +1,321 @@
+/*
+ * 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
+ * or http://www.opensolaris.org/os/licensing.
+ * 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 2004 Sun Microsystems, Inc.  All rights reserved.
+ * Use is subject to license terms.
+ */
+
+/* Copyright (c) 1984, 1986, 1987, 1988, 1989 AT&T */
+/* All Rights Reserved */
+
+
+/*
+ * System includes
+ */
+
+#include <stdio.h>
+#include <string.h>
+#include <signal.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <sys/utsname.h>
+#include <locale.h>
+#include <libintl.h>
+
+/*
+ * consolidation pkg command library includes
+ */
+
+#include <pkglib.h>
+
+/*
+ * local pkg command library includes
+ */
+
+#include "install.h"
+#include "libadm.h"
+#include "libinst.h"
+#include "messages.h"
+
+#define	MAILCMD	"/usr/bin/mail"
+
+/* lockinst.c */
+extern void	unlockinst(void);
+
+/* mntinfo.c */
+extern int	unmount_client(void);
+
+extern char	*msgtext;
+extern char	*pkginst;
+
+extern int	started;
+extern int	dreboot;	/* != 0 if reboot required after installation */
+extern int	failflag;	/* != 0 if fatal error has occurred (1) */
+extern int	ireboot;	/* != 0 if immediate reboot required */
+extern int	warnflag;	/* != 0 if non-fatal error has occurred (2) */
+
+extern struct admin	adm;
+
+/*
+ * exported functions
+ */
+
+void			quit(int retcode);
+void			quitSetSilentExit(boolean_t a_silentExit);
+void			quitSetZoneName(char *a_zoneName);
+sighdlrFunc_t		*quitGetTrapHandler(void);
+
+/*
+ * forward declarations
+ */
+
+static void		mailmsg(int retcode);
+static void		quitmsg(int retcode);
+static void		trap(int signo);
+
+static char		*zoneName = (char *)NULL;
+static boolean_t	silentExit = B_FALSE;
+static int		includeZonename = 0;
+static int		trapEntered = 0;
+
+/*
+ * *****************************************************************************
+ * global external (public) functions
+ * *****************************************************************************
+ */
+
+/*
+ * Name:	quitGetTrapHandler
+ * Description:	return address of this modules "signal trap" handler
+ * Arguments:	void
+ * Returns:	sighdlrFunc_t
+ *			The address of the trap handler that can be passed to
+ *			the signal() type system calls
+ */
+
+sighdlrFunc_t *
+quitGetTrapHandler()
+{
+	return (&trap);
+}
+
+/*
+ * Name:	quitSetZoneName
+ * Description:	set the zone name the program is running in
+ * Arguments:	a_zoneName - pointer to string representing the name of the zone
+ *			that the program is running in
+ * Returns:	void
+ */
+
+void
+quitSetZoneName(char *a_zoneName)
+{
+	zoneName = a_zoneName;
+	if ((zoneName == (char *)NULL || *zoneName == '\0')) {
+		includeZonename = 0;
+	} else {
+		includeZonename = 1;
+	}
+}
+
+/*
+ * Name:	quitSetSilentExit
+ * Description:	set the "silent exit" flag - if silent exit is TRUE, then
+ *		no messages are output by quit() when it is called
+ * Arguments:	a_silentExit - indicates whether or not silent exit is set
+ * Returns:	void
+ */
+
+void
+quitSetSilentExit(boolean_t a_silentExit)
+{
+	silentExit = a_silentExit;
+}
+
+/*
+ * Name:	quit
+ * Description:	cleanup and exit
+ * Arguments:	a_retcode - the code to use to determine final exit status;
+ *			if this is NOT "99" and if a "ckreturnFunc" is
+ *			set, then that function is called with a_retcode
+ *			to set the final exit status.
+ *		Valid values are:
+ *		0 - success
+ *		1 - package operation failed (fatal error)
+ *		2 - non-fatal error (warning)
+ *		3 - user selected quit (operation interrupted)
+ *		4 - admin settings prevented operation
+ *		5 - interaction required and -n (non-interactive) specified
+ *		"10" is added to indicate "immediate reboot required"
+ *		"20" is be added to indicate "reboot after install required"
+ *		99 - do not interpret the code - just exit "99"
+ * Returns:	<<this function does not return - calls exit()>>
+ */
+
+void
+quit(int retcode)
+{
+	/* disable interrupts */
+
+	(void) signal(SIGINT, SIG_IGN);
+	(void) signal(SIGHUP, SIG_IGN);
+
+	/* process return code if not quit(99) */
+
+	if (retcode != 99) {
+		if ((retcode % 10) == 0) {
+			if (failflag) {
+				retcode += 1;
+			} else if (warnflag) {
+				retcode += 2;
+			}
+		}
+
+		if (ireboot) {
+			retcode = (retcode % 10) + 20;
+		}
+
+		if (dreboot) {
+			retcode = (retcode % 10) + 10;
+		}
+	}
+
+	/*
+	 * In the event that this quit() was called prior to completion of
+	 * the task, do an unlockinst() just in case.
+	 */
+	unlockinst();
+
+	/* unmount the mounts that are our responsibility. */
+	(void) unmount_client();
+
+	/* send mail to appropriate user list */
+	mailmsg(retcode);
+
+	/* display message about this installation */
+	quitmsg(retcode);
+
+	/* final exit debugging message */
+
+	echoDebug(DBG_EXIT_WITH_CODE, retcode);
+
+	exit(retcode);
+	/*NOTREACHED*/
+}
+
+/*
+ * *****************************************************************************
+ * static internal (private) functions
+ * *****************************************************************************
+ */
+
+static void
+quitmsg(int retcode)
+{
+	if (silentExit == B_TRUE) {
+		return;
+	}
+
+	(void) putc('\n', stderr);
+
+	/* if there is no pkgname, no message to report */
+	if (pkginst != (char *)NULL) {
+		ptext(stderr, qreason(3, retcode, 0, includeZonename),
+			pkginst, zoneName);
+	}
+
+	if (retcode && !started) {
+		ptext(stderr, MSG_NOCHANGE);
+	}
+}
+
+static void
+mailmsg(int retcode)
+{
+	struct utsname utsbuf;
+	FILE	*pp;
+	char	*cmd;
+	size_t	len;
+
+	if (silentExit == B_TRUE) {
+		return;
+	}
+
+	if (!started || (adm.mail == NULL))
+		return;
+
+	len = strlen(adm.mail) + sizeof (MAILCMD) + 2;
+	cmd = calloc(len, sizeof (char));
+	if (cmd == NULL) {
+		logerr(WRN_NOMAIL);
+		return;
+	}
+
+	(void) snprintf(cmd, len, "%s %s", MAILCMD, adm.mail);
+	if ((pp = popen(cmd, "w")) == NULL) {
+		logerr(WRN_NOMAIL);
+		return;
+	}
+
+	if (msgtext) {
+		ptext(pp, gettext(msgtext));
+	}
+
+	(void) strcpy(utsbuf.nodename, gettext("(unknown)"));
+	(void) uname(&utsbuf);
+	ptext(pp, qreason(4, retcode, 0, includeZonename), pkginst,
+		utsbuf.nodename, zoneName);
+
+	if (pclose(pp)) {
+		logerr(WRN_FLMAIL);
+	}
+}
+
+/*
+ * Name:	trap
+ * Description:	signal handler connected via quitGetTrapHandler()
+ * Arguments:	signo - [RO, *RO] - (int)
+ *			Integer representing the signal that caused the trap
+ *			to this function to occur
+ * Returns:	<< NONE >>
+ * NOTE:	This function exits the program after doing mandatory cleanup.
+ * NOTE:	Even though quit() should NOT return, there is a call to _exit()
+ *		put after each call to quit() just in case quit() ever returned
+ *		by mistake.
+ */
+
+static void
+trap(int signo)
+{
+	/* prevent reentrance */
+
+	if (trapEntered++ != 0) {
+		return;
+	}
+
+	if ((signo == SIGINT) || (signo == SIGHUP)) {
+		quit(3);
+		_exit(3);
+	}
+	quit(1);
+	_exit(1);
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/usr/src/cmd/svr4pkg/pkgremove/special.c	Fri Jun 05 10:28:40 2009 -0400
@@ -0,0 +1,710 @@
+/*
+ * 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
+ * or http://www.opensolaris.org/os/licensing.
+ * 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 2003 Sun Microsystems, Inc.  All rights reserved.
+ * Use is subject to license terms.
+ */
+
+
+/*
+ * special.c
+ *
+ * This module contains code required to remove special contents from
+ * the contents file when a pkgrm is done on a system upgraded to use
+ * the new database.
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <assert.h>
+#include <errno.h>
+#include <unistd.h>
+#include <string.h>
+#include <time.h>
+#include <limits.h>
+#include <fnmatch.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <pkgstrct.h>
+#include "pkglib.h"
+#include <libintl.h>
+
+/* This specifies the maximum length of a contents file line read in. */
+#define	LINESZ	8192
+
+#define	SPECIAL_MALLOC	"unable to maintain package contents text due to "\
+			"insufficient memory."
+#define	SPECIAL_ACCESS	"unable to maintain package contents text due to "\
+			"an access failure."
+#define	SPECIAL_INPUT	"unable to maintain package contents text: alternate "\
+			"root path too long"
+
+/*
+ * strcompare
+ *
+ * This function is used by qsort to sort an array of special contents
+ * rule strings.  This array must be sorted to facilitate efficient
+ * rule processing.  See qsort(3c) regarding qsort compare functions.
+ */
+static int
+strcompare(const void *pv1, const void *pv2)
+{
+	char **ppc1 = (char **) pv1;
+	char **ppc2 = (char **) pv2;
+	int i = strcmp(*ppc1, *ppc2);
+	if (i < 0)
+		return (-1);
+	if (i > 0)
+		return (1);
+	return (0);
+}
+
+/*
+ * match
+ *
+ * This function determines whether a file name (pc) matches a rule
+ * from the special contents file (pcrule).  We assume that neither
+ * string is ever NULL.
+ *
+ * Return: 1 on match, 0 on no match.
+ * Side effects: none.
+ */
+static int
+match(const char *pc, char *pcrule)
+{
+	int n = strlen(pcrule);
+	int wild = 0;
+	if (pcrule[n - 1] == '*') {
+		wild = 1;
+		pcrule[n - 1] = '\0';
+	}
+
+	if (!wild) {
+		if (fnmatch(pc, pcrule, FNM_PATHNAME) == 0 ||
+		    fnmatch(pc, pcrule, 0) == 0)
+		return (1);
+	} else {
+		int j;
+		j = strncmp(pc, pcrule, n - 1);
+		pcrule[n - 1] = '*';
+		if (j == 0)
+			return (1);
+	}
+	return (0);
+}
+
+/*
+ * search_special_contents
+ *
+ * This function assumes that a series of calls will be made requesting
+ * whether a given path matches the special contents rules or not.  We
+ * assume that
+ *
+ *   a) the special_contents array is sorted
+ *   b) the calls will be made with paths in a sorted order
+ *
+ * Given that, we can keep track of where the last search ended and
+ * begin the new search at that point.  This reduces the cost of a
+ * special contents matching search to O(n) from O(n^2).
+ *
+ *   ppcSC  A pointer to an array of special contents obtained via
+ *	  get_special_contents().
+ *   path   A path: determine whether it matches the special
+ *	  contents rules or not.
+ *   piX    The position in the special_contents array we have already
+ *	  arrived at through searching.  This must be initialized to
+ *	  zero before initiating a series of search_special_contents
+ *	  operations.
+ *
+ * Example:
+ * {
+ *	int i = 0, j, max;
+ *	char **ppSC = NULL;
+ *	if (get_special_contents(NULL, &ppcSC, &max) != 0) exit(1);
+ *	for (j = 0; paths != NULL && paths[j] != NULL; j++) {
+ *		if (search_special_contents(ppcSC, path[j], &i)) {
+ *			do_something_with_special_path(path[j]);
+ *		}
+ *	}
+ * }
+ *
+ * Return: 1 if there is a match, 0 otherwise.
+ * Side effects: The value of *piX will be set between calls to this
+ *    function.  To make this function thread safe, use search arrays.
+ *    Also:  Nonmatching entries are eliminated, set to NULL.
+ */
+static int
+search_special_contents(char **ppcSC, const char *pcpath, int *piX, int max)
+{
+	int wild;
+	if (ppcSC == NULL || *piX == max)
+		return (0);
+
+	while (*piX < max) {
+
+		int j, k;
+		if (ppcSC[*piX] == NULL) {
+			(*piX)++;
+			continue;
+		}
+
+		j = strlen(ppcSC[*piX]);
+		k = strcmp(pcpath, ppcSC[*piX]);
+		wild = (ppcSC[*piX][j - 1] == '*');
+
+		/*
+		 * Depending on whether the path string compared with the
+		 * rule, we take different actions.  If the path is less
+		 * than the rule, we keep the rule.  If the path equals
+		 * the rule, we advance the rule (as long as the rule is
+		 * not a wild card).  If the path is greater than the rule,
+		 * we have to advance the rule list until we are less or equal
+		 * again.  This way we only have to make one pass through the
+		 * rules, as we make one pass through the path strings.  We
+		 * assume that the rules and the path strings are sorted.
+		 */
+		if (k < 0) {
+
+			if (wild == 0)
+				return (0);
+
+			if (match(pcpath, ppcSC[*piX]))
+				return (1);
+			break;
+
+		} else if (k == 0) {
+
+			int x = match(pcpath, ppcSC[*piX]);
+			if (wild == 0) (*piX)++;
+			return (x);
+
+		} else {
+			/* One last try. */
+			if (match(pcpath, ppcSC[*piX]))
+				return (1);
+
+			/*
+			 * As pcpath > ppcSC[*piX] we have passed up this
+			 * rule - it cannot apply.  Therefore, we do not
+			 * need to retain it.  Removing the rule will make
+			 * subsequent searching more efficient.
+			 */
+			free(ppcSC[*piX]);
+			ppcSC[*piX] = NULL;
+
+			(*piX)++;
+		}
+	}
+	return (0);
+}
+
+/*
+ * get_special_contents
+ *
+ * Retrieves the special contents file entries, if they exist.  These
+ * are sorted.  We do not assume the special_contents file is in sorted
+ * order.
+ *
+ *   pcroot   The root of the install database.  If NULL assume '/'.
+ *   pppcSC   A pointer to a char **.  This pointer will be set to
+ *		point at NULL if there is no special_contents file or
+ *		to a sorted array of strings, NULL terminated, otherwise.
+ *   piMax    The # of entries in the special contents result.
+ *
+ * Returns:  0 on no error, nonzero on error.
+ * Side effects:  the pppcSC pointer is set to point at a newly
+ *   allocated array of pointers to strings..  The caller must
+ *   free this buffer.  The value of *piMax is set to the # of
+ *   entries in ppcSC.
+ */
+static int
+get_special_contents(const char *pcroot, char ***pppcSC, int *piMax)
+{
+	int e, i;
+	FILE *fp;
+	char line[2048];
+	char **ppc;
+	char *pc = "var/sadm/install/special_contents";
+	char path[PATH_MAX];
+	struct stat s;
+
+	/* Initialize the return values. */
+	*piMax = 0;
+	*pppcSC = NULL;
+
+	if (pcroot == NULL) {
+		pcroot = "/";
+	}
+
+	if (pcroot[strlen(pcroot) - 1] == '/') {
+		if (snprintf(path, PATH_MAX, "%s%s", pcroot, pc) >= PATH_MAX) {
+			progerr(gettext(SPECIAL_INPUT));
+			return (1);
+		}
+	} else {
+		if (snprintf(path, PATH_MAX, "%s/%s", pcroot, pc)
+		    >= PATH_MAX) {
+			progerr(gettext(SPECIAL_INPUT));
+			return (1);
+		}
+	}
+
+	errno = 0;
+	e = stat(path, &s);
+	if (e != 0 && errno == ENOENT)
+		return (0); /* No special contents file.  Do nothing. */
+
+	if (access(path, R_OK) != 0 || (fp = fopen(path, "r")) == NULL) {
+		/* Could not open special contents which exists */
+		progerr(gettext(SPECIAL_ACCESS));
+		return (1);
+	}
+
+	for (i = 0; fgets(line, 2048, fp) != NULL; i++);
+	rewind(fp);
+	if ((ppc = (char **) calloc(i + 1, sizeof (char *))) == NULL) {
+		progerr(gettext(SPECIAL_MALLOC));
+		return (1);
+	}
+
+	for (i = 0; fgets(line, 2048, fp) != NULL; ) {
+		int n;
+		if (line[0] == '#' || line[0] == ' ' || line[0] == '\n' ||
+		    line[0] == '\t' || line[0] == '\r')
+			continue;
+		n = strlen(line);
+		if (line[n - 1] == '\n')
+			line[n - 1] = '\0';
+		ppc[i++] = strdup(line);
+	}
+
+	qsort(ppc, i, sizeof (char *), strcompare);
+
+	*pppcSC = ppc;
+	*piMax = i;
+	return (0);
+}
+
+/*
+ * free_special_contents
+ *
+ * This function frees special_contents which have been allocated using
+ * get_special_contents.
+ *
+ *   pppcSC    A pointer to a buffer allocated using get_special_contents.
+ *   max       The number of entries allocated.
+ *
+ * Result: None.
+ * Side effects: Frees memory allocated using get_special_contents and
+ *    sets the pointer passed in to NULL.
+ */
+static void
+free_special_contents(char ***pppcSC, int max)
+{
+	int i;
+	char **ppc = NULL;
+	if (*pppcSC == NULL)
+		return;
+
+	ppc = *pppcSC;
+	for (i = 0; ppc != NULL && i < max; i++)
+		if (ppc[i] == NULL)
+			free(ppc[i]);
+
+	if (ppc != NULL)
+		free(ppc);
+
+	*pppcSC = NULL;
+}
+
+/*
+ * get_path
+ *
+ * Return the first field of a string delimited by a space.
+ *
+ *   pcline	A line from the contents file.
+ *
+ * Return: NULL if an error.  Otherwise a string allocated by this
+ *   function.  The caller must free the string.
+ * Side effects: none.
+ */
+static char *
+get_path(const char *pcline)
+{
+	int i = strcspn(pcline, " ");
+	char *pc = NULL;
+	if (i <= 1 || (pc = (char *) calloc(i + 1, 1)) == NULL)
+		return (NULL);
+	(void) memcpy(pc, pcline, i);
+	return (pc);
+}
+
+/*
+ * generate_special_contents_rules
+ *
+ * This procedure will generate an array of integers which will be a mask
+ * to apply to the ppcfextra array.  If set to 1, then the content must be
+ * added to the contents file.  Otherwise it will not be:  The old contents
+ * file will be used for this path value, if one even exists.
+ *
+ *    ient	The number of ppcfextra contents installed.
+ *    ppcfent	The contents installed.
+ *    ppcSC	The rules (special contents)
+ *    max	The number of special contents rules.
+ *    ppiIndex	The array of integer values, determining whether
+ *		individual ppcfextra items match special contents rules.
+ *		This array will be created and set in this function and
+ *		returned.
+ *
+ * Return: 0 success, nonzero failure
+ * Side effects: allocates an array of integers that the caller must free.
+ */
+static int
+generate_special_contents_rules(int ient, struct cfent **ppcfent,
+    char **ppcSC, int max, int **ppiIndex)
+{
+	int i, j;
+	int *pi = (int *) calloc(ient, sizeof (int));
+	if (pi == NULL) {
+		progerr(gettext(SPECIAL_MALLOC));
+		return (1);
+	}
+
+	/*
+	 * For each entry in ppcfextra, check if it matches a rule.
+	 * If it does not, set the entry in the index to -1.
+	 */
+	for (i = 0, j = 0; i < ient && j < max; i++) {
+		if (search_special_contents(ppcSC, ppcfent[i]->path,
+		    &j, max) == 1) {
+			pi[i] = 1;
+
+		} else {
+			pi[i] = 0;
+		}
+	}
+
+	/*
+	 * In case we ran out of rules before contents, we will not use
+	 * those contents.  Make sure these contents are set to 0 and
+	 * will not be copied from the ppcfent array into the contents
+	 * file.
+	 */
+	for (i = i; i < ient; i++)
+		pi[i] = 0;
+
+	*ppiIndex = pi;
+	return (0);
+}
+
+
+/*
+ * pathcmp
+ *
+ * Compare a path to a cfent.  It will match either if the path is
+ * equal to the cfent path, or if the cfent is a symbolic or link
+ * and *that* matches.
+ *
+ *    path	a path
+ *    pent      a contents entry
+ *
+ * Returns: as per strcmp
+ * Side effects: none.
+ */
+static int
+pathcmp(const char *pc, const struct cfent *pent)
+{
+	int i;
+	if ((pent->ftype == 's' || pent->ftype == 'l') &&
+	    pent->ainfo.local) {
+		char *p, *q;
+		if ((p = strstr(pc, "=")) == NULL) {
+
+			i = strcmp(pc, pent->path);
+
+			/* A path without additional chars strcmp's to less */
+			if (i == 0)
+				i = -1;
+
+		} else {
+			/* Break the link path into two pieces. */
+			*p = '\0';
+
+			/* Compare the first piece. */
+			i = strcmp(pc, pent->path);
+
+			/* If equal we must compare the second piece. */
+			if (i == 0) {
+				q = p + 1;
+				i = strcmp(q, pent->ainfo.local);
+			}
+
+			/* Restore the link path. */
+			*p = '=';
+		}
+	} else {
+		i = strcmp(pc, pent->path);
+	}
+
+	return (i);
+}
+
+/*
+ * -----------------------------------------------------------------------
+ * Externally visible function.
+ */
+
+/*
+ * special_contents_remove
+ *
+ * Given a set of entries to remove and an alternate root, this function
+ * will do everything required to ensure that the entries are removed
+ * from the contents file if they are listed in the special_contents
+ * file.  The contents file will get changed only in the case that the
+ * entire operation has succeeded.
+ *
+ *  ient	The number of entries.
+ *  ppcfent	The entries to remove.
+ *  pcroot	The alternate install root.  Could be NULL.  In this
+ *		case, assume root is '/'
+ *
+ * Result: 0 on success, nonzero on failure.  If an error occurs, an
+ *    error string will get output to standard error alerting the user.
+ * Side effects: The contents file may change as a result of this call,
+ *    such that lines in the in the file will be changed or removed.
+ *    If the call fails, a t.contents file may be left behind.  This
+ *    temporary file should be removed subsequently.
+ */
+int
+special_contents_remove(int ient, struct cfent **ppcfent, const char *pcroot)
+{
+	int result = 0;		/* Assume we will succeed.  Return result. */
+	char **ppcSC = NULL;	/* The special contents rules, sorted. */
+	int i, j;		/* Indexes into contents & special contents */
+	FILE *fpi = NULL,	/* Input of contents file */
+	    *fpo = NULL;	/* Output to temp contents file */
+	char cpath[PATH_MAX],	/* Contents file path */
+	    tcpath[PATH_MAX];	/* Temp contents file path */
+	const char *pccontents = "var/sadm/install/contents";
+	const char *pctcontents = "var/sadm/install/t.contents";
+	char line[LINESZ];	/* Reads in and writes out contents lines. */
+	time_t t;		/* Used to create a timestamp comment. */
+	int max;		/* Max number of special contents entries. */
+	int *piIndex;		/* An index to ppcfents to remove from cfile */
+
+	cpath[0] = tcpath[0] = '\0';
+
+	if (ient == 0 || ppcfent == NULL || ppcfent[0] == NULL) {
+		goto remove_done;
+	}
+
+	if ((get_special_contents(pcroot, &ppcSC, &max)) != 0) {
+		result = 1;
+		goto remove_done;
+	}
+
+	/* Check if there are no special contents actions to take. */
+	if (ppcSC == NULL) {
+		goto remove_done;
+	}
+
+	if (pcroot == NULL) pcroot = "/";
+	if (pcroot[strlen(pcroot) - 1] == '/') {
+		if (snprintf(cpath, PATH_MAX, "%s%s", pcroot, pccontents)
+		    >= PATH_MAX ||
+		    snprintf(tcpath, PATH_MAX, "%s%s", pcroot, pctcontents)
+		    >= PATH_MAX) {
+			progerr(gettext(SPECIAL_INPUT));
+			result = -1;
+			goto remove_done;
+		}
+	} else {
+		if (snprintf(cpath, PATH_MAX, "%s/%s", pcroot, pccontents)
+		    >= PATH_MAX ||
+		    snprintf(tcpath, PATH_MAX, "%s/%s", pcroot, pctcontents)
+		    >= PATH_MAX) {
+			progerr(gettext(SPECIAL_INPUT));
+			result = -1;
+			goto remove_done;
+		}
+	}
+
+	/* Open the temporary contents file to write, contents to read. */
+	if (access(cpath, F_OK | R_OK) != 0) {
+		/*
+		 * This is not a problem since no contents means nothing
+		 * to remove due to special contents rules.
+		 */
+		result = 0;
+		cpath[0] = '\0'; /* This signals omission of 'rename cleanup' */
+		goto remove_done;
+	}
+
+	if (access(cpath, W_OK) != 0) {
+		/* can't write contents file, something is wrong. */
+		progerr(gettext(SPECIAL_ACCESS));
+		result = 1;
+		goto remove_done;
+
+	}
+
+	if ((fpi = fopen(cpath, "r")) == NULL) {
+		/* Given the access test above, this should not happen. */
+		progerr(gettext(SPECIAL_ACCESS));
+		result = 1;
+		goto remove_done;
+	}
+
+	if ((fpo = fopen(tcpath, "w")) == NULL) {
+		/* open t.contents failed */
+		progerr(gettext(SPECIAL_ACCESS));
+		result = 1;
+		goto remove_done;
+	}
+
+	if (generate_special_contents_rules(ient, ppcfent, ppcSC, max, &piIndex)
+	    != 0) {
+		result = 1;
+		goto remove_done;
+	}
+
+	/*
+	 * Copy contents to t.contents unless there is an entry in
+	 * the ppcfent array which corresponds to an index set to 1.
+	 *
+	 * These items are the removed package contents which matche an
+	 * entry in ppcSC (the special_contents rules).
+	 *
+	 * Since both the contents and rules are sorted, we can
+	 * make a single efficient pass.
+	 */
+	(void) memset(line, 0, LINESZ);
+
+	for (i = 0, j = 0; fgets(line, LINESZ, fpi) != NULL; ) {
+
+		char *pcpath = NULL;
+
+		/*
+		 * Note:  This could be done better:  We should figure out
+		 * which are the last 2 lines and only trim those off.
+		 * This will suffice to do this and will only be done as
+		 * part of special_contents handling.
+		 */
+		if (line[0] == '#')
+			continue; /* Do not copy the final 2 comment lines */
+
+		pcpath = get_path(line);
+
+		if (pcpath != NULL && i < ient) {
+			int k;
+			while (piIndex[i] == 0)
+				i++;
+
+			if (i < ient)
+				k = pathcmp(pcpath, ppcfent[i]);
+
+			if (k < 0 || i >= ient) {
+				/* Just copy contents -> t.contents */
+				/*EMPTY*/
+			} else if (k == 0) {
+				/* We have a match.  Do not copy the content. */
+				i++;
+				free(pcpath);
+				(void) memset(line, 0, LINESZ);
+				continue;
+			} else while (i < ient) {
+
+				/*
+				 * This is a complex case:  The content
+				 * entry is further along alphabetically
+				 * than the rule.  Skip over all rules which
+				 * apply until we come to a rule which is
+				 * greater than the current entry, or equal
+				 * to it.  If equal, do not copy, otherwise
+				 * do copy the entry.
+				 */
+				if (piIndex[i] == 0) {
+					i++;
+					continue;
+				} else if ((k = pathcmp(pcpath, ppcfent[i]))
+				    >= 0) {
+					i++;
+					if (k == 0) {
+						free(pcpath);
+						(void) memset(line, 0, LINESZ);
+						break;
+					}
+				} else {
+					/* path < rule, end special case */
+					break;
+				}
+			}
+
+			/*
+			 * Avoid copying the old content when path == rule
+			 * This occurs when the complex case ends on a match.
+			 */
+			if (k == 0)
+				continue;
+		}
+
+		if (fprintf(fpo, "%s", line) < 0) {
+			/* Failing to write output would be catastrophic. */
+			progerr(gettext(SPECIAL_ACCESS));
+			result = 1;
+			break;
+		}
+		(void) memset(line, 0, LINESZ);
+	}
+
+	t = time(NULL);
+	(void) fprintf(fpo, "# Last modified by pkgremove\n");
+	(void) fprintf(fpo, "# %s", ctime(&t));
+
+remove_done:
+	free_special_contents(&ppcSC, max);
+
+	if (fpi != NULL)
+		(void) fclose(fpi);
+
+	if (fpo != NULL)
+		(void) fclose(fpo);
+
+	if (result == 0) {
+		if (tcpath[0] != '\0' && cpath[0] != '\0' &&
+		    rename(tcpath, cpath) != 0) {
+			progerr(gettext(SPECIAL_ACCESS));
+			result = 1;
+		}
+	} else {
+		if (tcpath[0] != '\0' && remove(tcpath) != 0) {
+			/*
+			 * Do not output a diagnostic message.  This condition
+			 * occurs only when we are unable to clean up after
+			 * a failure.  A temporary file will linger.
+			 */
+			result = 1;
+		}
+	}
+
+	return (result);
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/usr/src/cmd/svr4pkg/pkgremove/wsreg_pkgrm.c	Fri Jun 05 10:28:40 2009 -0400
@@ -0,0 +1,472 @@
+/*
+ * 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
+ * or http://www.opensolaris.org/os/licensing.
+ * 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 2004 Sun Microsystems, Inc.  All rights reserved.
+ * Use is subject to license terms.
+ */
+
+
+/*
+ * wsreg_pkgrm.c
+ *
+ * Background information:
+ *
+ * In the past, pkgrm did not check whether a package was needed by
+ * products in the product registry.  The only check that pkgrm does
+ * is whether any packages depend on the package to be removed.  This
+ * meant that it was trivial to use pkgrm correctly and damage products
+ * (installed by webstart wizards) - without even receiving a warning.
+ *
+ * This enhancement to pkgrm will determine if the package to remove is
+ * needed by any registered products.  If not, a '0' is returned and the
+ * pkgrm can proceed.  If there is a conflict, nonzero is returned and
+ * a list of all products which will be effected.  Note that removing
+ * one package may damage several products.  This is because some
+ * packages are used by several products, and some components are shared
+ * by several products.
+ *
+ * The list returned is a string, which the caller must free by calling
+ * free().
+ *
+ * The purpose of the list is to inform the user, exactly as is done with
+ * the 'depends' information.  The user must be presented with the list
+ * as a warning and be able to either abort the operation or proceed -
+ * well advised of the consequences.
+ *
+ * How this works
+ *
+ * Installed products are associated with 'components' in a product
+ * registry database.  Components in the product registry are often
+ * associated with packages.  Packages are the mechanism in which
+ * software is actually installed, on Solaris.  For example, when a
+ * webstart wizard install occurs, one or more packages are added.
+ * These are associated with 'components' (install metadata containers)
+ * in the product registry.  The product registry interface acts as
+ * though these packages *really are* installed.
+ *
+ * In order to ensure that this remains the case, the product registry
+ * is examined for instances of a package before that package is removed.
+ *
+ * See libwsreg(3LIB) for general information about the product
+ * registry library used to determine if removing a package is OK.
+ *
+ * See prodreg(1M) for information about a tool which can be used
+ * to inspect the product registry.  Any component which has an
+ * attribute 'pkgs' will list those packages which cannot be removed
+ * safely.  For example: 'pkgs= SUNWfoo SUNWbar' would imply that
+ * neither SUNWfoo or SUNWbar can be removed.
+ */
+
+#include <assert.h>
+#include <string.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <ctype.h>
+#include <errno.h>
+#include <locale.h>
+
+#include "wsreg_pkgrm.h"
+
+struct dstrp {
+	char **ppc;
+	int    len;
+	int    max;
+};
+
+static int append_dstrp(struct dstrp *pd, const char *str);
+static int in_list(const char *pcList, const char *pcItem);
+static void get_all_dependents_r(struct dstrp *, struct dstrp *,
+    Wsreg_component *, int *, const char *);
+static char *get_locale();
+
+/*
+ * wsreg_pkgrm_check
+ *
+ * This routine determines if removing a particular package will
+ * 'damage' a product.
+ *
+ *    pcRoot      IN:  The alternate root directory.  If this parameter
+ *                     is NULL - then the root "/" is assumed.
+ *
+ *    pcPKG       IN:  The name of the package to remove (a normal NULL-
+ *                     terminated string.)
+ *                     This parameter must not be NULL.
+ *
+ *    pppcID     OUT:  The location of a char ** pointer is passed in.
+ *                     This parameter must not be NULL.  The result
+ *                     will be a NULL terminated array of ID strings.
+ *                     The caller must free both the array of strings
+ *                     and each individual string.  Example:
+ *
+ *                     char ** ppcID;
+ *                     int i;
+ *
+ *                     if (wsreg_pkgrm_check(NULL, "SUNWblah", &ppcID, ..)
+ *                         > 0) {
+ *
+ *                         for (i = 0; ppcID[i]; i++) {
+ *                             do_something(ppcID[i]);
+ *                             free(ppcID[i]);
+ *                         }
+ *                         free(ppcID);
+ *                     }
+ *
+ *    pppcName   OUT:  As pppcID, except this contains the human readable
+ *                     localized name of the component.  The index of the
+ *                     name array coincides with that of the ID array, so
+ *                     there will be the same number of items in both and
+ *                     the component whose name is *pppcName[0] has the
+ *                     id *pppcID[0].
+ *
+ * Returns: 0 if there is no problem.  pkgrm my proceed.
+ *          positive - there is a conflict.  pppcID & pppcName return strings.
+ *          negative - there was a problem running this function.
+ *                     Error conditions include: (errno will be set)
+ *                      ENOENT	The pcRoot directory was not valid.
+ *			ENOMEM	The string to return could not be allocated.
+ *			EACCES	The registry database could not be read.
+ *
+ * Side effects: The pppcID and pppcName parameters may be changed and set
+ *     to the value of arrays of strings which the caller must free.
+ */
+int
+wsreg_pkgrm_check(const char *pcRoot, const char *pcPKG,
+    char ***pppcID, char ***pppcName)
+{
+	Wsreg_component **ppws;
+	struct dstrp id = { NULL, 0, 0}, nm = {NULL, 0, 0};
+	int i, r;
+	char *locale = get_locale();
+	if (locale == NULL)
+		locale = "en";
+
+	if (locale == NULL) {
+		errno = ENOMEM;
+		return (-1);
+	}
+
+	assert(pcPKG != NULL && pppcName != NULL && pppcID != NULL);
+
+	*pppcID = NULL;
+	*pppcName = NULL;
+
+	errno = 0;
+	r = 0; /* A return value 0 indicates nothing was found. */
+
+	if (pcRoot == NULL)
+		pcRoot = "/";
+
+	if (wsreg_initialize(WSREG_INIT_NORMAL, pcRoot) != WSREG_SUCCESS ||
+		wsreg_can_access_registry(O_RDONLY) == 0) {
+		errno = EACCES;
+		return (-1);
+	}
+
+	ppws = wsreg_get_all();
+
+	for (i = 0; ((ppws != NULL) && (ppws[i] != NULL)); i++) {
+		char *pcpkgs = wsreg_get_data(ppws[i], "pkgs");
+		if (pcpkgs != NULL && in_list(pcpkgs, pcPKG)) {
+			char *pcID = wsreg_get_id(ppws[i]);
+			char *pcName = wsreg_get_display_name(ppws[i],
+			    locale);
+			int depth;
+
+			depth = 0;
+			r = 1;
+
+			if (append_dstrp(&id, pcID) ||
+			    append_dstrp(&nm, pcName)) {
+				errno = ENOMEM;
+				r = -1;
+				break;
+			}
+
+			if (pcID) free(pcID);
+			if (pcName) free(pcName);
+			get_all_dependents_r(&id, &nm, ppws[i], &depth, locale);
+		}
+	}
+
+	if (r > 0) {
+		*pppcID = id.ppc;
+		*pppcName = nm.ppc;
+	}
+
+	free(locale);
+
+	if (ppws != NULL)
+		wsreg_free_component_array(ppws);
+
+	return (r);
+}
+
+/*
+ * in_list
+ *
+ *   pcList   A white space delimited list of words (non-white characters)
+ *   pcItem   A word (not NULL, an empty string or containing white space)
+ *
+ * Returns 0 if pcItem is not in pcList.  nonzero if pcItem is in pcList
+ * Side effects: None
+ */
+static int
+in_list(const char *pcList, const char *pcItem)
+{
+
+	int i = 0, j = 0, k = 0;
+
+	assert(pcItem);
+	k = strlen(pcItem);
+
+	if (pcList == NULL || k == 0)
+		return (0);
+
+	while (pcList[i] != '\0') {
+
+		if (isspace(pcList[i])) {
+			if (i == j) {
+				i++;
+				j++;
+			} else {
+
+				if ((i - j) == k &&
+				    strncmp(&pcList[j], pcItem, i - j) == 0) {
+					return (1);
+				} else {
+					j = i;
+				}
+
+			}
+		} else {
+			i++;
+		}
+
+		/* last element in the list case */
+		if (pcList[i] == '\0' && j < i &&
+		    strncmp(&pcList[j], pcItem, i - j) == 0)
+			return (1);
+	}
+
+	return (0);
+}
+
+#define	APPEND_INCR	20
+
+/*
+ * append_dstrp
+ *
+ * This routine manages a dynamic array of strings in a very minimal way.
+ * It assumes it has been passed a cleared struct dstrp = { NULL, 0, 0 }
+ * It will add the appended string to the end of the array.  When needed,
+ * the array of strings is grown to the next APPEND_INCR in size.
+ *
+ * Note this routine is different than append_dstr since that accumulates
+ * char, this accumulates char *.
+ *
+ *   pd  The dynamic string.  Must be initialized to {NULL,0,0}.  Must not
+ *       be NULL.
+ *
+ *   str The string to add.  May be of 0 length.  If NULL, a string of 0
+ *       length will be added (NOT a NULL).
+ *
+ * Returns: 0 if OK, -1 if malloc failed.
+ * Side effects: The value of pd->ppc[pd->len] changes, taking strdup(str)
+ *     The final entry in the array will be NULL.  There will be pd->len
+ *     entries.  To free this, free each string in the array and the array
+ *     itself.   The caller must free the allocated memory.
+ */
+static int
+append_dstrp(struct dstrp *pd, const char *str)
+{
+	if (str == NULL) str = "";
+
+	if (pd->max == 0) {
+
+		/* Initialize if necessary */
+		pd->len = 0;
+		pd->max = APPEND_INCR;
+		pd->ppc = (char **)calloc(APPEND_INCR * sizeof (char *), 1);
+		if (pd->ppc == NULL)
+			return (-1);
+
+	} else if ((pd->len + 2) == pd->max) {
+
+		/*
+		 * Grow the array.
+		 * Always leave room for a single NULL end item:  That is
+		 * why we grow when +2 equals the max, not +1.
+		 */
+		size_t s = (pd->max + APPEND_INCR) * sizeof (char *);
+		pd->ppc = realloc(pd->ppc, s);
+		if (pd->ppc == NULL) {
+			return (-1);
+		} else {
+			memset(pd->ppc + pd->max, '\0',
+				APPEND_INCR * sizeof (char *));
+		}
+
+		pd->max += APPEND_INCR;
+	}
+
+	if (str == NULL) {
+		pd->ppc[pd->len] = NULL;
+		pd->len++;
+	} else {
+		pd->ppc[pd->len] = (char *)strdup(str);
+		if (pd->ppc[pd->len] == NULL)
+			return (-1);
+		pd->len++;
+	}
+
+	return (0);
+}
+
+#define	DEPTH_MAX	100
+
+/*
+ * get_all_dependents_r
+ *
+ *   This routine accumulates the id and name of all components which
+ *   depend (directly or indirectly) on a component which has a pkg which
+ *   may be removed.  By calling this routine recursively, the entire list
+ *   of existing dependencies can be accumulated.
+ *
+ *   id        The dynamic accumulation of all ids of dependent components.
+ *   nm        The dynamic accumulation of all names of dep. components.
+ *   pws       The component to check for dependencies, record their
+ *             ids and names, then call check these components for redun-
+ *             dancy also.
+ *   pdepth    The depth of the recursion.  This must be set to 0 upon the
+ *             first call to this function.  Only DEPTH_MAX calls will be
+ *             attempted.
+ *   locale    The locale to use for querying for display names.
+ *
+ * Return value: None.
+ * Side effects.  strings will be added to id and nm.  The depth counter
+ *    will increase.
+ */
+static void
+get_all_dependents_r(struct dstrp *id, struct dstrp *nm, Wsreg_component *pws,
+    int *pdepth, const char *locale)
+{
+	int i;
+
+	/* Get the list of dependent components. */
+	Wsreg_component **ppws = wsreg_get_dependent_components(pws);
+	if (ppws == NULL)
+		return;
+
+	if (locale == NULL)
+		locale = "en";
+	if (locale == NULL)
+		return;
+
+	/*
+	 * Prevent infinite loops in the case where there is a cycle
+	 * in the dependency graph.  Such a cycle should never happen,
+	 * but a clueless user of the libwsreg API could construct such
+	 * a failure case.  This is defensive programming.
+	 */
+	if (*pdepth > DEPTH_MAX)
+		return;
+
+	(*pdepth)++;
+
+	for (i = 0; ppws[i]; i++) {
+		char *pcID = wsreg_get_id(ppws[i]);
+		char *pcName = wsreg_get_display_name(ppws[i], locale);
+		if (append_dstrp(id, pcID) ||
+		    append_dstrp(nm, pcName))
+			/*
+			 * Errors in append_dstrp happen only due to malloc
+			 * failing on small allocations.  If we fail here
+			 * this is the least of the user's problems.  We
+			 * can just stop accumulating new info at this point.
+			 */
+			return;
+		get_all_dependents_r(id, nm, ppws[i], pdepth, locale);
+	}
+
+	wsreg_free_component_array(ppws);
+}
+
+/*
+ * init_locale
+ *
+ * Set locale and textdomain for localization.  Note that the return value
+ * of setlocale is the locale string.  It is in the form
+ *
+ *   "/" LC_CTYPE "/" LC_COLLATE "/" LC_CTIME "/" LC_NUMERIC "/"
+ *      LC_MONETARY "/ LC_MESSAGES
+ *
+ *  This routine parses this result line to determine the value of
+ *  the LC_MESSAGES field.  If it is "C", the default language "en"
+ *  is selected.  If not, the string is disected to get only the
+ *  ISO 639 two letter tag:  "en_US.ISO8859-1" becomes "en".
+ *
+ * Returns: Returns a newly allocated language tag string.
+ *          Returns NULL if setlocale() returns a null pointer.
+ * Side effects:
+ * (1) setlocale changes behavior of the application.
+ */
+static char *
+get_locale()
+{
+	int i = 0, c, n;
+	char lang[32];
+	char *pc = setlocale(LC_ALL, "");
+	char *tag = NULL;
+
+	if (pc == NULL) {
+		return (NULL);
+	}
+
+	(void *) memset(lang, 0, 32);
+	if (pc[0] == '/') {
+
+		/* Skip to the 6th field, which is 'LC_MESSAGES.' */
+		c = 0;
+		for (i = 0; (pc[i] != NULL) && (c < 6); i++) {
+			if (pc[i] == '/') c++;
+		}
+
+		/* Strip off any dialect tag and character encoding. */
+		n = 0;
+		while ((pc[i] != NULL) && (pc[i] != '_') &&
+		    (n < 32) && (pc[i] != '.')) {
+			lang[n++] = pc[i++];
+		}
+	}
+
+	if (i > 2) {
+		if (strcmp(lang, "C") == 0) {
+			tag = strdup("en");
+		} else {
+			tag = strdup(lang);
+		}
+	} else {
+		tag = strdup("en");
+	}
+
+	return (tag);
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/usr/src/cmd/svr4pkg/pkgremove/wsreg_pkgrm.h	Fri Jun 05 10:28:40 2009 -0400
@@ -0,0 +1,45 @@
+/*
+ * 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
+ * or http://www.opensolaris.org/os/licensing.
+ * 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 2003 Sun Microsystems, Inc.  All rights reserved.
+ * Use is subject to license terms.
+ */
+
+#ifndef __WSREG_PKGRM__
+#define	__WSREG_PKGRM__
+
+
+#ifdef	__cplusplus
+extern "C" {
+#endif
+
+#include <stdio.h>
+#include <wsreg.h>
+#include <fcntl.h>
+
+int wsreg_pkgrm_check(const char *, const char *, char ***, char ***);
+
+#ifdef	__cplusplus
+}
+#endif
+
+#endif	/* __WSREG_PKGRM__ */
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/usr/src/cmd/svr4pkg/pkgrm/Makefile	Fri Jun 05 10:28:40 2009 -0400
@@ -0,0 +1,44 @@
+#
+# 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
+# or http://www.opensolaris.org/os/licensing.
+# 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 2009 Sun Microsystems, Inc.  All rights reserved.
+# Use is subject to license terms.
+#
+
+PROG=		pkgrm
+
+OBJS=		check.o		\
+		main.o		\
+		presvr4.o	\
+		quit.o
+
+include $(SRC)/cmd/svr4pkg/Makefile.svr4pkg
+
+LDLIBS	+=	-lpkg -linstzones -ladm
+
+
+.KEEP_STATE:
+all:		$(PROG)
+
+install:	all $(ROOTUSRSBINPROG)
+
+include $(SRC)/cmd/svr4pkg/Makefile.svr4pkg.targ
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/usr/src/cmd/svr4pkg/pkgrm/check.c	Fri Jun 05 10:28:40 2009 -0400
@@ -0,0 +1,622 @@
+/*
+ * 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
+ * or http://www.opensolaris.org/os/licensing.
+ * 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 2009 Sun Microsystems, Inc.  All rights reserved.
+ * Use is subject to license terms.
+ */
+
+
+#include <stdio.h>
+#include <limits.h>
+#include <string.h>
+#include <stdlib.h>
+#include <sys/stat.h>
+#include <unistd.h>
+#include <errno.h>
+#include <utmpx.h>
+#include <dirent.h>
+#include <sys/types.h>
+#include <locale.h>
+#include <libintl.h>
+#include <pkgstrct.h>
+#include <pkglocs.h>
+#include <assert.h>
+#include <pkglib.h>
+#include <install.h>
+#include <libinst.h>
+#include <libadm.h>
+#include <messages.h>
+#include <instzones_api.h>
+
+extern int	npkgs;	/* the number of packages yet to be installed */
+
+/*
+ * ckquit is a global that controls 'ckyorn' (defined in libadm)
+ * If ckquit is non-zero, then "quit" is allowed as an answer when
+ * ckyorn is called. If is it zero, then "quit" is not an allowed answer.
+ */
+extern int	ckquit;
+
+extern struct admin adm;
+
+/*
+ * each one of these represents a single kind of dependency check
+ */
+
+static depckError_t er_depsonme = {0, (depckErrorRecord_t *)NULL};
+static depckError_t er_prenci = {0, (depckErrorRecord_t *)NULL};
+static depckError_t er_prereq = {0, (depckErrorRecord_t *)NULL};
+static depckError_t er_rckdepend = {0, (depckErrorRecord_t *)NULL};
+static depckError_t er_rckpriv = {0, (depckErrorRecord_t *)NULL};
+static depckError_t er_rckrunlevel = {0, (depckErrorRecord_t *)NULL};
+static depckError_t er_runlevel = {0, (depckErrorRecord_t *)NULL};
+
+/*
+ * each one of these represents a localized message for a single kind
+ * of dependency check
+ */
+
+static char *IMSG_PKGRMCHK_CKRUNLVL = (char *)NULL;
+static char *IMSG_PKGRMCHK_DEPEND = (char *)NULL;
+static char *IMSG_PKGRMCHK_DEPSONME = (char *)NULL;
+static char *IMSG_PKGRMCHK_PRENCI = (char *)NULL;
+static char *IMSG_PKGRMCHK_PREREQ = (char *)NULL;
+static char *IMSG_PKGRMCHK_PRIV = (char *)NULL;
+static char *IMSG_PKGRMCHK_RUNLEVEL = (char *)NULL;
+
+/*
+ * each one of these represents a function to handle a single kind of
+ * dependency check
+ */
+
+static int rckdepend(char *a_msg, char *a_pkg);
+static int rckdepsonme(char *a_msg, char *a_pkg);
+static int rckprenci(char *a_msg, char *a_pkg);
+static int rckprereq(char *a_msg, char *a_pkg);
+static int rckpriv(char *a_msg, char *a_pkg);
+static int rckrunlevel(char *a_msg, char *a_pkg);
+
+static depckl_t DEPCKL[] = {
+	/*
+	 * Message hierarchy:
+	 * -- runlevel=%s
+	 * --- rckrunlevel=%d
+	 * --- rckpriv=%d  ****
+	 * -- incompat=%s
+	 * -- prerequisite-incomplete=%s
+	 * -- dependonme=%s
+	 * -- dependsonme=%s:%s
+	 * -- prerequisite-installed=%s
+	 * ---rckdepend=%d ****
+	 */
+
+	/* name,	ignore_values,	err_msg,	depcklFunc,	recrd */
+	/*
+	 * package and zone information is collected in the "record" object for
+	 * each occurance - then a message is constructed for each zone that
+	 * reported the condition - the message includes that portion of the
+	 * check past the "=" - then the specified "depcklFunc" is called to
+	 * process each message.
+	 * Message format:
+	 * 	%s %s <%s> %s <%s>
+	 * Message arguments:
+	 *	value, "package", package-name, "zone/zones", zone-name
+	 */
+
+	{ "dependsonme=",		NULL, 	&IMSG_PKGRMCHK_DEPSONME,
+					&rckdepsonme,	&er_depsonme
+	},
+	{ "dependonme=",		NULL, 	&IMSG_PKGRMCHK_DEPSONME,
+					&rckdepsonme,	&er_depsonme
+	},
+	{ "prerequisite-incomplete=",	NULL,	&IMSG_PKGRMCHK_PRENCI,
+					&rckprenci,	&er_prenci
+	},
+	{ "prerequisite-installed=",	NULL,	&IMSG_PKGRMCHK_PREREQ,
+					&rckprereq,	&er_prereq
+	},
+	{ "runlevel=",			NULL,	&IMSG_PKGRMCHK_RUNLEVEL,
+					NULL,		&er_runlevel
+	},
+
+	/*
+	 * these checks are ignored if they return one of the listed values
+	 * if they do NOT return one of the listed values, then the package
+	 * and zone information is collected in the "record" object for each
+	 * occurance - then a single unified message is constructed for all
+	 * zones that report the same condition; then the specified "depcklFunc"
+	 * is called to process the resulting combined message.
+	 * Message format:
+	 * 	%s <%s> %s <%s>
+	 * Message arguments:
+	 *	"package", package-name, "zone/zones", zone-name(s)
+	 */
+
+	{ "rckdepend=",			"0",	&IMSG_PKGRMCHK_DEPEND,
+					&rckdepend,	&er_rckdepend
+	},
+	{ "rckpriv=",			"0",	&IMSG_PKGRMCHK_PRIV,
+					&rckpriv,	&er_rckpriv
+	},
+	{ "rckrunlevel=",		"0",	&IMSG_PKGRMCHK_CKRUNLVL,
+					&rckrunlevel,	&er_rckrunlevel
+	},
+
+	/*
+	 * same as above BUT no check to ignore is done; message always reported
+	 */
+
+	{ NULL,				NULL,	NULL,
+						NULL,		NULL
+	}
+};
+
+/*
+ * Name:	preremove_verify
+ * Description:	verify results of preremoval dependency checking
+ * Arguments:	a_pkglist - pointer to array of strings representing the names
+ *			of all the packages that have been checked
+ *		a_zlst - list of zones that dependencies were checked on
+ *		a_zoneTempDir - pointer to string representing the path where
+ *			the files containing the preremoval dependency
+ *			check data are located
+ * Returns:	int
+ *		== 0 - continue processing
+ *		!= 0 - do not continue processing
+ */
+
+int
+preremove_verify(char **a_pkglist, zoneList_t a_zlst, char *a_zoneTempDir)
+{
+	char		*pkginst;
+	int		i;
+	int		savenpkgs = npkgs;
+
+	/*
+	 * entry assertions
+	 */
+
+	assert(a_pkglist != (char **)NULL);
+	assert(a_zlst != (zoneList_t)NULL);
+	assert(a_zoneTempDir != (char *)NULL);
+
+	/*
+	 * entry debugging info
+	 */
+
+	echoDebug(DBG_PRERVFY_ENTRY);
+
+	/*
+	 * localize messages
+	 */
+
+	IMSG_PKGRMCHK_DEPSONME = MSG_PKGRMCHK_DEPSONME;
+	IMSG_PKGRMCHK_PRENCI = MSG_PKGRMCHK_PRENCI;
+	IMSG_PKGRMCHK_PREREQ = MSG_PKGRMCHK_PREREQ;
+	IMSG_PKGRMCHK_RUNLEVEL = MSG_PKGRMCHK_RUNLEVEL;
+	IMSG_PKGRMCHK_DEPEND = MSG_PKGRMCHK_DEPEND;
+	IMSG_PKGRMCHK_PRIV = MSG_PKGRMCHK_PRIV;
+	IMSG_PKGRMCHK_CKRUNLVL = MSG_PKGRMCHK_CKRUNLVL;
+
+	/*
+	 * outer loop - process each package first
+	 */
+
+	for (i = 0; (pkginst = a_pkglist[i]) != NULL; i++) {
+
+		char	*zoneName;
+		int	zoneIndex;
+
+		/*
+		 * inner loop - for each package process each zone second
+		 */
+
+		if (pkgIsPkgInGzOnly(get_inst_root(), pkginst) == B_TRUE) {
+			continue;
+		}
+
+		for (zoneIndex = 0;
+			(zoneName = z_zlist_get_zonename(a_zlst, zoneIndex)) !=
+				(char *)NULL; zoneIndex++) {
+
+			FILE	*fp;
+			char	line[PATH_MAX+1];
+			char	preremovecheckPath[PATH_MAX+1];
+			int	len;
+
+			/* skip the zone if it is NOT bootable */
+
+			if (z_zlist_is_zone_runnable(a_zlst,
+			    zoneIndex) == B_FALSE) {
+				continue;
+			}
+
+			/* create path to this packages preremove check data */
+
+			len = snprintf(preremovecheckPath,
+			    sizeof (preremovecheckPath),
+			    "%s/%s.%s.preremovecheck.txt",
+			    a_zoneTempDir, pkginst,
+			    z_zlist_get_scratch(a_zlst, zoneIndex));
+
+			if (len > sizeof (preremovecheckPath)) {
+				progerr(ERR_CREATE_PATH_3, a_zoneTempDir,
+					pkginst, zoneName);
+				continue;
+			}
+
+			/* error if preremove check data path is not a file */
+
+			if (isfile((char *)NULL, preremovecheckPath) != 0) {
+				echoDebug(DBG_PRERVFY_NOFILE, pkginst, zoneName,
+					preremovecheckPath, strerror(errno));
+				progerr(ERR_PRERVFY_NOFILE, pkginst, zoneName);
+				continue;
+			}
+
+			/* open the preremove check data file */
+
+			fp = fopen(preremovecheckPath, "r");
+			if (fp == (FILE *)NULL) {
+				progerr(ERR_PRERVFY_OPEN_FILE,
+					preremovecheckPath, pkginst, zoneName,
+					strerror(errno));
+				continue;
+			}
+
+			/* read and process each preremove check data line */
+
+			while (fgets(line, sizeof (line), fp) != (char *)NULL) {
+				int	len;
+				int	j;
+
+				/* remove all new-lines from end of line */
+
+				len = strlen(line);
+				while ((len > 0) && (line[len-1] == '\n')) {
+					line[--len] = '\0';
+				}
+
+				/* ignore comment lines */
+
+				if (line[0] == '#') {
+					continue;
+				}
+
+				/* ignore empty lines */
+
+				if (line[0] == '\0') {
+					continue;
+				}
+
+				/* scan dependency list for this item */
+
+				for (j = 0;
+					DEPCKL[j].name != (char *)NULL; j++) {
+					len = strlen(DEPCKL[j].name);
+
+					if (strncmp(line, DEPCKL[j].name,
+								len) == 0) {
+						break;
+					}
+				}
+
+				echoDebug(DBG_PRERVFY_SCAN, line, pkginst,
+						zoneName);
+
+				/* ignore line if not found */
+
+				if (DEPCKL[j].name == (char *)NULL) {
+					progerr(ERR_PRERVFY_UNKNOWN_LINE, line,
+							pkginst, zoneName);
+					continue;
+				}
+
+				if ((DEPCKL[j].ignore_values != (char *)NULL) &&
+					(*(DEPCKL[j].ignore_values) != '\0') &&
+					(strchr(DEPCKL[j].ignore_values,
+						line[len]) != (char *)NULL)) {
+						continue;
+				}
+				/* found match - record this dependency issue */
+
+				depchkRecordError(DEPCKL[j].record, pkginst,
+					zoneName, &line[len]);
+			}
+
+			/* close preremove check data file */
+
+			(void) fclose(fp);
+		}
+	}
+
+	/*
+	 * all dependency issues have been recorded; report results
+	 */
+
+	i = depchkReportErrors(DEPCKL);
+
+	/* restore "npkgs" */
+
+	npkgs = savenpkgs;
+
+	/* return continue/dont dontinue results */
+
+	return (i);
+}
+
+/*
+ * Name:	getyorn
+ * Description:	Deliver dependency check reason; ask question; return response
+ * Arguments:	a_msg - pointer to string representing the message to output
+ *			such as 'The package <..> contains <...>'
+ *		a_pkg - pointer to string representing the package for which
+ *			the question is being asked
+ *		a_nocheck - should the message be output?
+ *			== 0 - do not output the message
+ *			!= 0 - output the message
+ *		a_quit - should the question NOT be asked?
+ *			== 0 - ask the question
+ *			!= 0 - do not ask the question - return "no"
+ *		a_helpMsg - pointer to string representing help message to be
+ *			made available if the question is asked
+ *			== NULL - no help message is available
+ *		a_adminMsg - pointer to string representing the dependency check
+ *			failure 'reason' - such as "Privilege checking failed."
+ *			== NULL - no failure reason is available
+ * Returns:	int - results of question/response actions
+ *			0 - success
+ *			1 - end of file
+ *			2 - undefined error
+ *			3 - answer was not "y"/was "q"
+ *			4 - quit action taken
+ *			5 - interactive mode required
+ */
+
+static int
+getyorn(char *a_msg, char *a_pkg, int a_nocheck, int a_quit,
+	char *a_helpMsg, char *a_adminMsg)
+{
+	char	ans[MAX_INPUT];
+	char	ask_cont[MSG_MAX];
+	int	n;
+	int	saveCkquit;
+
+	/*
+	 * entry assertions
+	 */
+
+	assert(a_pkg != (char *)NULL);
+	assert(*a_pkg != '\0');
+
+	/*
+	 * entry debugging info
+	 */
+
+	echoDebug(DBG_PRERVFY_GETYORN_ARGS, a_pkg, a_nocheck, a_quit, a_msg,
+			a_adminMsg ? a_adminMsg : "");
+
+	/* return success (0) if "nocheck" is non-zero */
+
+	if (a_nocheck != 0) {
+		echoDebug(DBG_PRERVFY_GETYORN_NOCHECK, a_pkg);
+		return (0);
+	}
+
+	/* output reason for this particular failure */
+
+	if ((a_msg != (char *)NULL) && (*a_msg != '\0')) {
+		ptext(stderr, "%s", a_msg);
+	}
+
+	/* return "4 (administration)" if "quit" is non-zero */
+
+	if (a_quit != 0) {
+		/* output failure "admin reason" if available */
+		if ((a_adminMsg != (char *)NULL) && (*a_adminMsg != '\0')) {
+			ptext(stderr, a_adminMsg);
+		}
+		echoDebug(DBG_PRERVFY_GETYORN_QUIT, a_pkg);
+		return (4);
+	}
+
+	/* return "5 (administration interaction required)" if -n */
+
+	if (echoGetFlag() == B_FALSE) {
+		ptext(stderr, MSG_PRERVFY_GETYORN_SUSP, a_pkg);
+		echoDebug(DBG_PRERVFY_GETYORN_QUIT_USER, a_pkg);
+		return (5);
+	}
+
+	/* prepare question to ask "continue with removal of pkg <xxx>?" */
+
+	(void) snprintf(ask_cont, sizeof (ask_cont), gettext(ASK_PKGRMCHK_CONT),
+		a_pkg);
+
+	/* ask question */
+
+	saveCkquit = ckquit;
+	ckquit = 0;
+
+	n = ckyorn(ans, NULL, NULL, a_helpMsg, ask_cont);
+
+	ckquit = saveCkquit;
+
+	if (n != 0) {
+		ptext(stderr, MSG_PRERVFY_GETYORN_TERM, a_pkg);
+		echoDebug(DBG_PRERVFY_GETYORN_CKYORN, a_pkg, n);
+		return (n);
+	}
+
+	/* return "3 (interruption) if not "y" or "Y" */
+
+	if (strchr("yY", *ans) == NULL) {
+		ptext(stderr, MSG_PRERVFY_GETYORN_TERM_USER, a_pkg);
+		echoDebug(DBG_PRERVFY_GETYORN_NOT_Y, a_pkg, ans);
+		return (3);
+	}
+
+	/* return "0 - success" */
+
+	echoDebug(DBG_PRERVFY_GETYORN_SUCCESS, a_pkg);
+
+	return (0);
+}
+
+/*
+ * Trigger:	dependsonme=<<package>>
+ * Sequence:	- one or more: dependsonme=<<package>>
+ *		- one: rckdepend=<<n>>
+ * Actions:	Output message if "rdepend!=nocheck"
+ *		Return 0
+ *		Terminate when 'rckdepend' processed
+ */
+
+static int
+rckdepsonme(char *a_msg, char *a_pkg)
+{
+	echoDebug(DBG_PRERVFY_RCKDEPSONME, a_pkg, a_msg);
+
+	if (!(ADM(rdepend, "nocheck"))) {
+		ptext(stderr, "%s", a_msg);
+	}
+
+	return (0);
+}
+
+/*
+ * Trigger:	prerequisite-incomplete=<<package>>
+ * Sequence:	- one or more: prerequisite-incomplete=<<package>>
+ *		- one: rckdepend=<<n>>
+ * Actions:	Output message if "rdepend!=nocheck"
+ *		Return 0
+ *		Terminate when 'rckdepend' processed
+ */
+
+static int
+rckprenci(char *a_msg, char *a_pkg)
+{
+	echoDebug(DBG_PRERVFY_RCKPRENCI, a_pkg, a_msg);
+
+	if (!(ADM(rdepend, "nocheck"))) {
+		ptext(stderr, "%s", a_msg);
+	}
+
+	return (0);
+}
+
+/*
+ * Trigger:	prerequisite-installed=<<package>>
+ * Sequence:	- one or more: prerequisite-installed=<<package>>
+ *		- one: rckdepend=<<n>>
+ * Actions:	Output message if "rdepend!=nocheck"
+ *		Return 0
+ *		Terminate when 'rckdepend' processed
+ */
+
+static int
+rckprereq(char *a_msg, char *a_pkg)
+{
+	echoDebug(DBG_PRERVFY_RCKPREREQ, a_pkg, a_msg);
+
+	if (!(ADM(rdepend, "nocheck"))) {
+		ptext(stderr, "%s", a_msg);
+	}
+
+	return (0);
+}
+
+/*
+ * Return value:	int
+ *			0 - success
+ *			1 - end of file
+ *			2 - undefined error
+ *			3 - answer was not "y"/was "q"
+ *			4 - quit action taken
+ *			5 - interactive mode required
+ *			99 - fatal error
+ */
+
+static int
+rckrunlevel(char *a_msg, char *a_pkg)
+{
+	echoDebug(DBG_PRERVFY_RCKRUNLEVEL, a_pkg, a_msg);
+	/*
+	 * For now, we are ignoring runlevel removal issues within
+	 * non-global zones.  This is questionable, but the RSTATES
+	 * feature is rarely used and known uses within Solaris are
+	 * effectively no-ops as of this time
+	 */
+	return (0);
+}
+
+/*
+ * Trigger:	rckdepend=<<n>>
+ * Sequence:	- one or more of:
+ *		-- incompat=<<package>>
+ *		-- prerequisite-incomplete=<<package>>
+ *		-- prerequisite-installed=<<package>>
+ *		-- dependson=<<package>>
+ *		-- dependsonme=<<package>>
+ *		- one: ckpdepend=<<n>>
+ * Actions:	process according to settings
+ * Return value:	int
+ *			0 - success
+ *			1 - end of file
+ *			2 - undefined error
+ *			3 - answer was not "y"/was "q"
+ *			4 - quit action taken
+ *			5 - interactive mode required
+ */
+
+static int
+rckdepend(char *a_msg, char *a_pkg)
+{
+	echoDebug(DBG_PRERVFY_RCKDEPEND, a_pkg, a_msg);
+
+	return (getyorn(a_msg, a_pkg, ADM(rdepend, "nocheck"),
+		ADM(rdepend, "quit"), HLP_PKGRMCHK_DEPEND,
+		ERR_PKGRMCHK_DEPFAILED));
+}
+
+/*
+ * Trigger:	rckpriv=<<n>>
+ * Sequence:	- one: rckpriv=<<n>>
+ * Actions:	process according to settings
+ * Return value:	int
+ *			0 - success
+ *			1 - end of file
+ *			2 - undefined error
+ *			3 - answer was not "y"/was "q"
+ *			4 - quit action taken
+ *			5 - interactive mode required
+ */
+
+static int
+rckpriv(char *a_msg, char *a_pkg)
+{
+	echoDebug(DBG_PRERVFY_RCKPRIV, a_pkg, a_msg);
+
+	return (getyorn(a_msg, a_pkg, ADM(action, "nocheck"),
+		ADM(action, "quit"), HLP_PKGRMCHK_PRIV,
+		ERR_PKGRMCHK_PRIVFAILED));
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/usr/src/cmd/svr4pkg/pkgrm/main.c	Fri Jun 05 10:28:40 2009 -0400
@@ -0,0 +1,3075 @@
+/*
+ * 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
+ * or http://www.opensolaris.org/os/licensing.
+ * 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 2009 Sun Microsystems, Inc.  All rights reserved.
+ * Use is subject to license terms.
+ */
+
+/* Copyright (c) 1984, 1986, 1987, 1988, 1989 AT&T */
+/* All Rights Reserved */
+
+
+/*
+ * System includes
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <string.h>
+#include <signal.h>
+#include <errno.h>
+#include <locale.h>
+#include <libintl.h>
+#include <pkgstrct.h>
+#include <pkgdev.h>
+#include <pkginfo.h>
+#include <pkglocs.h>
+#include <pkglib.h>
+#include <assert.h>
+
+/*
+ * libinstzones includes
+ */
+
+#include <instzones_api.h>
+
+/*
+ * consolidation pkg command library includes
+ */
+
+#include <pkglib.h>
+
+/*
+ * local pkg command library includes
+ */
+
+#include "install.h"
+#include "libinst.h"
+#include "libadm.h"
+#include "messages.h"
+
+/*
+ * pkgrm local includes
+ */
+
+#include "quit.h"
+
+/*
+ * exported global variables
+ */
+
+/* these globals are set by ckreturn and used by quit.c */
+
+int	admnflag = 0;	/* != 0 if any pkg op admin setting failure (4) */
+int	doreboot = 0;	/* != 0 if reboot required after installation */
+int	failflag = 0;	/* != 0 if fatal error has occurred (1) */
+int	intrflag = 0;	/* != 0 if user selected quit (3) */
+int	ireboot = 0;	/* != 0 if immediate reboot required */
+int	nullflag = 0;	/* != 0 if admin interaction required (5) */
+int	warnflag = 0;	/* != 0 if non-fatal error has occurred (2) */
+
+/* imported by quit.c */
+int	npkgs = 0;	/* the number of packages yet to be installed */
+
+/* imported by presvr4.c */
+int	started = 0;
+char	*tmpdir = NULL;	/* location to place temporary files */
+
+/* imported by various (many) */
+struct admin	adm;	/* holds info about installation admin */
+struct pkgdev	pkgdev;	/* holds info about the installation device */
+
+/*
+ * internal global variables
+ */
+
+static char	*admnfile = NULL;	/* file to use for installation admin */
+static char	*pkginst = NULL;	/* current pkg/src instance 2 process */
+static char	*vfstab_file = NULL;
+static char	*zoneTempDir = (char *)NULL;
+
+/* set by ckreturn() */
+
+static int	interrupted = 0;	/* last pkg op was quit (1,2,3,4,5) */
+
+static int	nointeract = 0;		/* non-zero - no user interaction */
+static int	pkgrmremote = 0;	/* remove pkg objs stored remotely  */
+static int	pkgverbose = 0;		/* non-zero if verbose mode selected */
+
+/*
+ * Assume the package complies with the standards as regards user
+ * interaction during procedure scripts.
+ */
+
+static int	old_pkg = 0;
+static int	old_symlinks = 0;
+static int	no_map_client = 0;
+
+/* Set by -O nozones: do not process any zones */
+
+static boolean_t	noZones = B_FALSE;
+
+/* Set by -O zonelist=<names...>: process only named zones */
+
+static boolean_t	usedZoneList = B_FALSE;
+
+/* Set by -O debug: debug output is enabled? */
+
+static boolean_t	debugFlag = B_FALSE;
+
+/*
+ * imported (external) functions
+ */
+
+/* presvr4.c */
+
+extern int	presvr4(char *pkg, int a_nointeract);
+
+/* check.c */
+
+extern int	preremove_verify(char **a_pkgList, zoneList_t a_zlst,
+			char *a_zoneTempDir);
+/* quit.c */
+
+extern void	quitSetZonelist(zoneList_t a_zlst);
+
+/*
+ * imported (external) variables
+ */
+
+extern char	*pkgdir;
+
+/* printable string - if string is null results in ??? */
+
+#define	PSTR(STR) (((STR) == (char *)NULL) ? "???" : (STR))
+
+#define	MAX_FDS	20
+
+#if !defined(TEXT_DOMAIN)	/* Should be defined by cc -D */
+#define	TEXT_DOMAIN "SYS_TEST"
+#endif
+
+#define	INHERITFS	"inherited-filesystem="
+#define	INHERITFS_LEN	((sizeof (INHERITFS))-1)
+
+/*
+ * forward declarations
+ */
+
+static void		ckreturn(int retcode);
+static void		create_zone_adminfile(char **r_zoneAdminFile,
+				char *a_zoneTempDir, char *a_admnfile);
+static void		create_zone_tempdir(char **r_zoneTempDir,
+				char *a_tmpdir);
+static int		doRemove(int a_nodelete, char *a_altBinDir,
+				int a_longestPkg, char *a_adminFile,
+				char *a_zoneAdminFile, zoneList_t zlst);
+static int		pkgRemove(int a_nodelete, char *a_altBinDir,
+				char *a_adminFile, char **a_inheritedPkgDirs);
+static int		pkgZoneCheckRemove(char *a_zoneName,
+				char **a_inheritedPkgDirs, char *a_altBinDir,
+				char *a_adminFile, char *a_stdoutPath,
+				zone_state_t a_zoneState);
+static int		pkgZoneRemove(char *a_zoneName,
+				char **a_inheritedPkgDirs, int a_nodelete,
+				char *a_altBinDir, char *a_adminFile,
+				zone_state_t a_zoneState);
+static void		resetreturn();
+static void		usage(void);
+static boolean_t	check_applicability(char *a_packageDir,
+				char *a_pkgInst, char *a_rootPath,
+				CAF_T a_flags);
+static boolean_t	check_packages(char **a_pkgList, char *a_packageDir);
+static boolean_t	path_valid(char *path);
+static boolean_t	remove_packages(char **a_pkgList, int a_nodelete,
+				int a_longestPkg, int a_repeat,
+				char *a_altBinDir, char *a_pkgdir,
+				char *a_spoolDir, boolean_t a_noZones);
+static boolean_t	remove_packages_from_spool_directory(char **a_pkgList,
+				int a_nodelete, int a_longestPkg, int a_repeat,
+				char *a_altBinDir);
+static boolean_t	remove_packages_in_global_no_zones(char **a_pkgList,
+				int a_nodelete, int a_longestPkg, int a_repeat,
+				char *a_altBinDir);
+static boolean_t	remove_packages_in_global_with_zones(char **a_pkgList,
+				int a_nodelete, int a_longestPkg, int a_repeat,
+				char *a_altBinDir, char *a_pkgdir,
+				zoneList_t a_zlst);
+static boolean_t	remove_packages_in_nonglobal_zone(char **a_pkgList,
+				int a_nodelete, int a_longestPkg, int a_repeat,
+				char *a_altBinDir, char *a_pkgdir);
+static boolean_t	shall_we_continue(char *a_pkgInst, int a_npkgs);
+
+/*
+ * *****************************************************************************
+ * global external (public) functions
+ * *****************************************************************************
+ */
+
+/*
+ * Name:	main
+ * Description:	main entry point for pkgrm
+ * Returns:	int
+ *   0        Successful completion
+ *   1        Fatal error.
+ *   2        Warning.
+ *   3        Interruption.
+ *   4        Administration.
+ *   5        Administration. Interaction is required. Do not use pkgrm -n.
+ *  10       Reboot after removal of all packages.
+ *  20       Reboot after removal of this package.
+ */
+
+int
+main(int argc, char **argv)
+{
+	char			**category = NULL;
+	char			*altBinDir = (char *)NULL;
+	char			*catg_arg = NULL;
+	char			*p;
+	char			*prog_full_name = NULL;
+	char			*spoolDir = 0;
+	int			c;
+	int			longestPkg = 0;
+	int			n;
+	int			nodelete = 0;	/* dont rm files/run scripts */
+	int			pkgLgth = 0;
+	int			repeat;
+	struct sigaction	nact;
+	struct sigaction	oact;
+
+	/* initialize locale environment */
+
+	(void) setlocale(LC_ALL, "");
+	(void) textdomain(TEXT_DOMAIN);
+
+	/* initialize program name */
+
+	prog_full_name = argv[0];
+	(void) set_prog_name(argv[0]);
+
+	/* tell spmi zones interface how to access package output functions */
+
+	z_set_output_functions(echo, echoDebug, progerr);
+
+	/* tell quit which ckreturn function to call */
+
+	quitSetCkreturnFunc(&ckreturn);
+
+	/* Read PKG_INSTALL_ROOT from the environment, if it's there. */
+
+	if (!set_inst_root(getenv("PKG_INSTALL_ROOT"))) {
+		progerr(ERR_ROOT_SET);
+		exit(1);
+	}
+
+	if (z_running_in_global_zone() && !enable_local_fs()) {
+		progerr(ERR_CANNOT_ENABLE_LOCAL_FS);
+	}
+
+	/*
+	 * ********************************************************************
+	 * parse command line options
+	 * ********************************************************************
+	 */
+
+	while ((c = getopt(argc, argv, "?Aa:b:FMnO:R:s:V:vY:Z")) != EOF) {
+		switch (c) {
+		/*
+		 * Public interface: Allow admin to remove objects
+		 * from a service area via a reference client.
+		 * Remove the package files from the client's file
+		 * system, absolutely. If a file is shared with other
+		 * packages, the default behavior is to not remove
+		 * the file from the client's file system.
+		 */
+		case 'A':
+		    pkgrmremote++;
+		    break;
+
+		/*
+		 * Public interface: Use the installation
+		 * administration file, admin, in place of the
+		 * default admin file. pkgrm first looks in the
+		 * current working directory for the administration
+		 * file.  If the specified administration file is not
+		 * in the current working directory, pkgrm looks in
+		 * the /var/sadm/install/admin directory for the
+		 * administra- tion file.
+		 */
+		case 'a':
+		    admnfile = flex_device(optarg, 0);
+		    break;
+
+		/*
+		 * Not a public interface:  location where package executables
+		 * can be found - default is /usr/sadm/install/bin.
+		 */
+		case 'b':
+			if (!path_valid(optarg)) {
+				progerr(ERR_PATH, optarg);
+				quit(1);
+			}
+			if (isdir(optarg) != 0) {
+				p = strerror(errno);
+				progerr(ERR_CANNOT_USE_DIR, optarg, p);
+				quit(1);
+			}
+			altBinDir = optarg;
+			break;
+
+		/*
+		 * Not a public interface: pass -F option to
+		 * pkgremove which suppresses the removal of any
+		 * files and any class action scripts, and suppresses
+		 * the running of any class action scripts.  The
+		 * package files remain but the package looks like it
+		 * is not installed. This is mainly for use by the
+		 * upgrade process.
+		 */
+		case 'F':
+		    nodelete++;
+		    break;
+
+		/*
+		 * Public interface: Instruct pkgrm not to use the
+		 * $root_path/etc/vfstab file for determining the
+		 * client's mount points. This option assumes the
+		 * mount points are correct on the server and it
+		 * behaves consistently with Solaris 2.5 and earlier
+		 * releases.
+		 */
+		case 'M':
+		    no_map_client = 1;
+		    break;
+
+		/*
+		 * Public interface: package removal occurs in
+		 * non-interactive mode.  Suppress output of the list of
+		 * removed files. The default mode is interactive.
+		 */
+		case 'n':
+		    nointeract++;
+		    (void) echoSetFlag(B_FALSE);
+		    break;
+
+		/*
+		 * Not a public interface: the -O option allows the behavior
+		 * of the package tools to be modified. Recognized options:
+		 * -> debug
+		 * ---> enable debugging output
+		 * -> nozones
+		 * ---> act as though in global zone with no non-global zones
+		 * -> inherited-filesystems
+		 * ---> add specified file system to list of file systems
+		 * ---> that are inherited from the global zone
+		 * -> enable-hollow-package-support
+		 * --> Enable hollow package support. When specified, for any
+		 * --> package that has SUNW_PKG_HOLLOW=true:
+		 * --> Do not calculate and verify package size against target
+		 * --> Do not run any package procedure or class action scripts
+		 * --> Do not create or remove any target directories
+		 * --> Do not perform any script locking
+		 * --> Do not install or uninstall any components of any package
+		 * --> Do not output any status or database update messages
+		 * -> zonelist="<names...>"
+		 * ---> add package to space-separated list of zones only
+		 */
+
+		case 'O':
+			for (p = strtok(optarg, ","); p != (char *)NULL;
+				p = strtok(NULL, ",")) {
+
+				if (strcmp(p, "nozones") == 0) {
+					noZones = B_TRUE;
+					continue;
+				}
+
+				if (strncmp(p, INHERITFS, INHERITFS_LEN) == 0) {
+					if (z_add_inherited_file_system(
+						p+INHERITFS_LEN) == B_FALSE) {
+						progerr(ERR_NOSUCH_INHERITED,
+							p+INHERITFS_LEN);
+						quit(1);
+						/* NOTREACHED */
+					}
+					continue;
+				}
+
+				if (strcmp(p,
+					"enable-hollow-package-support") == 0) {
+					set_depend_pkginfo_DB(B_TRUE);
+					continue;
+				}
+
+				if (strcmp(p, "debug") == 0) {
+					/* set debug flag/enable debug output */
+					debugFlag = B_TRUE;
+					(void) echoDebugSetFlag(debugFlag);
+
+					/* debug info on arguments to pkgadd */
+					for (n = 0; n < argc && argv[n]; n++) {
+						echoDebug(DBG_ARG, n, argv[n]);
+					}
+
+					continue;
+				}
+
+				if (strncmp(p, "zonelist=", 9) == 0) {
+					if (z_set_zone_spec(p + 9) == -1)
+						quit(1);
+					usedZoneList = B_TRUE;
+					continue;
+				}
+
+				/* -O option not recognized - issue warning */
+
+				progerr(ERR_INVALID_O_OPTION, p);
+				continue;
+			}
+			break;
+
+		/*
+		 * Public interface: defines the full path name of a
+		 * directory to use as the root_path.  All files,
+		 * including package system information files, are
+		 * relocated to a directory tree starting in the
+		 * specified root_path.
+		 */
+		case 'R':
+		    if (!set_inst_root(optarg)) {
+			    progerr(ERR_ROOT_CMD);
+			    exit(1);
+		    }
+		    break;
+
+		/*
+		 * Public interface: remove the specified package(s)
+		 * from the directory spool.  The default directory
+		 * for spooled packages is /var/sadm/pkg.
+		 */
+		case 's':
+		    spoolDir = flex_device(optarg, 1);
+		    break;
+
+		/*
+		 * Public interface: Allow admin to establish the client
+		 * filesystem using a vfstab-like file of stable format.
+		 */
+		case 'V':
+		    vfstab_file = flex_device(optarg, 2);
+		    no_map_client = 0;
+		    break;
+
+		/*
+		 * Public interface: trace all of the scripts that
+		 * get executed by pkgrm, located in the
+		 * pkginst/install directory. This option is used for
+		 * debugging the procedural and non- procedural
+		 * scripts.
+		 */
+		case 'v':
+		    pkgverbose++;
+		    break;
+
+		/*
+		 * Public interface: remove packages based on the
+		 * CATEGORY variable from the installed/spooled
+		 * pkginfo file
+		 */
+		case 'Y':
+		    catg_arg = strdup(optarg);
+
+		    if ((category = get_categories(catg_arg)) == NULL) {
+			    progerr(ERR_CAT_INV, catg_arg);
+			    exit(1);
+		    } else if (is_not_valid_category(category,
+				    get_prog_name())) {
+			    progerr(ERR_CAT_SYS);
+			    exit(1);
+		    } else if (is_not_valid_length(category)) {
+			    progerr(ERR_CAT_LNGTH);
+			    exit(1);
+		    }
+
+		    break;
+
+		/*
+		 * unrecognized option
+		 */
+		default:
+		    usage();
+		    /* NOTREACHED */
+		}
+	}
+
+	/*
+	 * ********************************************************************
+	 * validate command line options
+	 * ********************************************************************
+	 */
+
+	/* set "debug echo" flag according to setting of "-O debug" option */
+
+	(void) echoDebugSetFlag(debugFlag);
+
+	/* output entry debugging information */
+
+	if (z_running_in_global_zone()) {
+		echoDebug(DBG_ENTRY_IN_GZ, prog_full_name);
+	} else {
+		echoDebug(DBG_ENTRY_IN_LZ, prog_full_name, getzoneid(),
+			z_get_zonename());
+	}
+
+	/* -s cannot be used with several */
+
+	if (spoolDir != (char *)NULL) {
+		if (admnfile != (char *)NULL) {
+			progerr(ERR_SPOOLDIR_AND_ADMNFILE);
+			usage();
+			/* NOTREACHED */
+		}
+
+		if (pkgrmremote != 0) {
+			progerr(ERR_SPOOLDIR_AND_PKGRMREMOTE);
+			usage();
+			/* NOTREACHED */
+		}
+
+		if (pkgverbose != 0) {
+			progerr(ERR_SPOOLDIR_AND_PKGVERBOSE);
+			usage();
+			/* NOTREACHED */
+		}
+
+		if (is_an_inst_root() != 0) {
+			progerr(ERR_SPOOLDIR_AND_INST_ROOT);
+			usage();
+			/* NOTREACHED */
+		}
+	}
+
+	/* -V cannot be used with -A */
+
+	if (no_map_client && pkgrmremote) {
+		progerr(ERR_V_USED_AND_PKGRMREMOTE);
+		usage();
+		/* NOTREACHED */
+	}
+
+	/* -n used without pkg names or category */
+
+	if (nointeract && (optind == argc) && (catg_arg == NULL)) {
+		progerr(ERR_BAD_N_PKGRM);
+		usage();
+		/* NOTREACHED */
+	}
+
+	/* Error if specified zone list isn't valid on target */
+	if (usedZoneList && z_verify_zone_spec() == -1)
+		usage();
+
+	/*
+	 * hook SIGINT and SIGHUP interrupts into quit.c's trap handler
+	 */
+
+	/* hold SIGINT/SIGHUP interrupts */
+
+	(void) sighold(SIGHUP);
+	(void) sighold(SIGINT);
+
+	/* connect quit.c:trap() to SIGINT */
+
+	nact.sa_handler = quitGetTrapHandler();
+	nact.sa_flags = SA_RESTART;
+	(void) sigemptyset(&nact.sa_mask);
+
+	(void) sigaction(SIGINT, &nact, &oact);
+
+	/* connect quit.c:trap() to SIGHUP */
+
+	nact.sa_handler = quitGetTrapHandler();
+	nact.sa_flags = SA_RESTART;
+	(void) sigemptyset(&nact.sa_mask);
+
+	(void) sigaction(SIGHUP, &nact, &oact);
+
+	/* release hold on signals */
+
+	(void) sigrelse(SIGHUP);
+	(void) sigrelse(SIGINT);
+
+	/* establish temporary directory to use */
+
+	tmpdir = getenv("TMPDIR");
+	if (tmpdir == NULL) {
+		tmpdir = P_tmpdir;
+	}
+
+	echoDebug(DBG_PKGRM_TMPDIR, tmpdir);
+
+	/* initialize path parameters */
+
+	set_PKGpaths(get_inst_root());
+
+	/*
+	 * initialize installation admin parameters - if removing from a spool
+	 * directory then the admin file is ignore.
+	 */
+
+	if (spoolDir == NULL) {
+		echoDebug(DBG_PKGRM_ADMINFILE, admnfile ? admnfile : "");
+		setadminFile(admnfile);
+	}
+
+	/*
+	 * if running in the global zone, and non-global zones exist, then
+	 * enable hollow package support so that any packages that are marked
+	 * SUNW_PKG_HOLLOW=true will be correctly removed in non-global zones
+	 * when removed directly in the global zone by the global zone admin.
+	 */
+
+	if (is_depend_pkginfo_DB()) {
+		echoDebug(DBG_PKGRM_HOLLOW_ENABLED);
+	} else if ((z_running_in_global_zone() == B_TRUE) &&
+		(z_non_global_zones_exist() == B_TRUE)) {
+		echoDebug(DBG_PKGRM_ENABLING_HOLLOW);
+		set_depend_pkginfo_DB(B_TRUE);
+	}
+
+	/*
+	 * See if user wants this to be handled as an old style pkg.
+	 * NOTE : the ``exception_pkg()'' stuff is to be used only
+	 * through on495. This function comes out for on1095. See
+	 * PSARC 1993-546. -- JST
+	 */
+	if (getenv("NONABI_SCRIPTS") != NULL) {
+		old_pkg = 1;
+	}
+
+	/*
+	 * See if the user wants to process symlinks consistent with
+	 * the old behavior.
+	 */
+
+	if (getenv("PKG_NONABI_SYMLINKS") != NULL) {
+		old_symlinks = 1;
+	}
+
+	if (devtype((spoolDir ? spoolDir : get_PKGLOC()), &pkgdev) ||
+	    pkgdev.dirname == NULL) {
+		progerr(ERR_BAD_DEVICE, spoolDir ? spoolDir : get_PKGLOC());
+		quit(1);
+		/* NOTREACHED */
+	}
+
+	pkgdir = pkgdev.dirname;
+	repeat = ((optind >= argc) && pkgdev.mount);
+
+	/*
+	 * error if there are packages on the command line and a category
+	 * was specified
+	 */
+
+	if (optind < argc && catg_arg != NULL) {
+		progerr(ERR_PKGS_AND_CAT_PKGRM);
+		usage();
+		/* NOTREACHED */
+	}
+
+	/*
+	 * ********************************************************************
+	 * main package processing "loop"
+	 * ********************************************************************
+	 */
+
+	for (;;) {
+		boolean_t	b;
+		char		**pkglist;	/* points to array of pkgs */
+
+		/*
+		 * mount the spool device if required
+		 */
+
+		if (pkgdev.mount) {
+			if (n = pkgmount(&pkgdev, NULL, 0, 0, 1)) {
+				quit(n);
+				/* NOTREACHED */
+			}
+		}
+
+		if (chdir(pkgdev.dirname)) {
+			progerr(ERR_CHDIR, pkgdev.dirname);
+			quit(1);
+			/* NOTREACHED */
+		}
+
+		/*
+		 * spool device mounted/available - get the list of the
+		 * packages to remove
+		 */
+
+		n = pkgGetPackageList(&pkglist, argv, optind,
+			catg_arg, category, &pkgdev);
+
+		switch (n) {
+			case -1:	/* no packages found */
+				echoDebug(DBG_PKGLIST_RM_NONFOUND,
+					PSTR(pkgdev.dirname));
+				progerr(ERR_NOPKGS, pkgdev.dirname);
+				quit(1);
+				/* NOTREACHED */
+
+			case 0:		/* packages found */
+				break;
+
+			default:	/* "quit" error */
+				echoDebug(DBG_PKGLIST_RM_ERROR,
+					pkgdev.dirname, n);
+				quit(n);
+				/* NOTREACHED */
+		}
+
+		/*
+		 * count the number of packages to remove
+		 * NOTE: npkgs is a global variable that is referenced by quit.c
+		 * when error messages are generated - it is referenced directly
+		 * by the other functions called below...
+		 */
+
+		for (npkgs = 0; pkglist[npkgs] != (char *)NULL; /* void */) {
+			pkgLgth = strlen(pkglist[npkgs]);
+			if (pkgLgth > longestPkg) {
+				longestPkg = pkgLgth;
+			}
+			echoDebug(DBG_PKG_SELECTED, npkgs, pkglist[npkgs]);
+			npkgs++;
+		}
+
+		/* output number of packages to be removed */
+
+		echoDebug(DBG_NUM_PKGS_TO_REMOVE, npkgs, longestPkg);
+
+		/*
+		 * package list generated - remove packages
+		 */
+
+		b = remove_packages(pkglist, nodelete, longestPkg, repeat,
+			altBinDir, pkgdev.dirname, spoolDir, noZones);
+
+		/*
+		 * unmount the spool directory if necessary
+		 */
+
+		if (pkgdev.mount) {
+			(void) chdir("/");
+			if (pkgumount(&pkgdev)) {
+				progerr(ERR_PKGUNMOUNT, pkgdev.bdevice);
+				quit(99);
+				/* NOTREACHED */
+
+			}
+		}
+
+		/*
+		 * continue with next sequence of packages if continue set
+		 */
+
+		if (b == B_TRUE) {
+			continue;
+		}
+
+		/*
+		 * not continuing - quit with 0 exit code
+		 */
+
+		quit(0);
+		/* NOTREACHED */
+#ifdef lint
+		return (0);
+#endif	/* lint */
+	}
+}
+
+/*
+ * *****************************************************************************
+ * static internal (private) functions
+ * *****************************************************************************
+ */
+
+/*
+ * Name:	doRemove
+ * Description:	Remove a package from the global zone, and optionally from one
+ *		or more non-global zones.
+ * Arguments:	a_nodelete: should the files and scripts remain installed?
+ *			- if != 0 pass -F flag to pkgremove - suppress
+ *			the removal of any files and any class action scripts
+ *			and suppress the running of any class action scripts.
+ *			The package files remain but the package looks like it
+ *			is not installed. This is mainly for use by upgrade.
+ *			- if == 0 do not pass -F flag to pkgremove - all
+ *			files and class action scripts are removed, and any
+ *			appropriate class action scripts are run.
+ *		a_altBinDir - pointer to string representing location of the
+ *			pkgremove executable to run. If not NULL, then pass
+ *			the path specified to the -b option to pkgremove.
+ *		a_longestPkg - length of the longest package "name" (for
+ *			output format alignment)
+ *		a_adminFile - pointer to string representing the admin
+ *			file to pass to pkgremove when removing a package from
+ *			the global zone only. Typically the admin file used for
+ *			the global zone is the admin file passed in by the user.
+ *			If this is == NULL no admin file is given to pkgremove.
+ *		a_zoneAdminFile - pointer to string representing the admin
+ *			file to pass to pkgremove when removing the package
+ *			from a non-global zone only. Typically the admin file
+ *			used for non-global zones supresses all checks since
+ *			the dependency checking is done for all zones first
+ *			before proceeding.
+ *			A zoneAdminFile MUST be specified if a_zlst != NULL.
+ *			A zoneAdminFile must NOT be specified if a_zlst == NULL.
+ *		a_zlst - list of zones to process; NULL if no zones to process.
+ * Returns:	int	(see ckreturn() function for details)
+ *		0 - success
+ *		1 - package operation failed (fatal error)
+ *		2 - non-fatal error (warning)
+ *		3 - user selected quit (operation interrupted)
+ *		4 - admin settings prevented operation
+ *		5 - interaction required and -n (non-interactive) specified
+ *		"10" will be added to indicate "immediate reboot required"
+ *		"20" will be added to indicate "reboot after install required"
+ */
+
+static int
+doRemove(int a_nodelete, char *a_altBinDir, int a_longestPkg, char *a_adminFile,
+	char *a_zoneAdminFile, zoneList_t a_zlst)
+{
+	boolean_t	b;
+	char		**inheritedPkgDirs;
+	char		*zoneName;
+	char		ans[MAX_INPUT];
+	int		n;
+	int		zoneIndex;
+	int		zonesSkipped;
+	struct pkginfo	*pinfo = (struct pkginfo *)NULL;
+	zone_state_t	zst;
+
+	/* entry assertions */
+
+	if (a_zlst != (zoneList_t)NULL) {
+		/* zone list specified - zone admin file required */
+		assert(a_zoneAdminFile != (char *)NULL);
+		assert(*a_zoneAdminFile != '\0');
+	} else {
+		/* no zone list specified - no zone admin file needed */
+		assert(a_zoneAdminFile == (char *)NULL);
+	}
+
+	/* NOTE: required 'pkgdir' set to spool directory or NULL */
+	b = pkginfoIsPkgInstalled(&pinfo, pkginst);
+	if (b == B_FALSE) {
+		progerr(ERR_NO_SUCH_INSTANCE, pkginst);
+		pkginfoFree(&pinfo);
+		return (2);
+	}
+
+	/* entry debugging info */
+
+	echoDebug(DBG_DOREMOVE_ENTRY);
+	echoDebug(DBG_DOREMOVE_ARGS, PSTR(pinfo->pkginst), PSTR(pinfo->name),
+		PSTR(pinfo->arch), PSTR(pinfo->version), PSTR(pinfo->basedir),
+		PSTR(pinfo->catg), pinfo->status);
+
+	if (!nointeract) {
+		char	fmt1[100];
+
+		/* create format based on max pkg name length */
+
+		(void) snprintf(fmt1, sizeof (fmt1), "   %%-%d.%ds  %%s",
+				a_longestPkg, a_longestPkg);
+
+		if (pinfo->status == PI_SPOOLED) {
+			echo(INFO_SPOOLED);
+		} else {
+			if (getuid()) {
+				progerr(ERR_NOT_ROOT, get_prog_name());
+				exit(1);
+			}
+			echo(INFO_INSTALL);
+		}
+
+		echo(fmt1, pinfo->pkginst, pinfo->name);
+
+		if (pinfo->arch || pinfo->version) {
+			char	fmt2[100];
+
+			/* create format based on max pkg name length */
+
+			(void) snprintf(fmt2, sizeof (fmt2), "   %%%d.%ds  ",
+					a_longestPkg, a_longestPkg);
+
+			/* LINTED variable format specifier to fprintf() */
+			(void) fprintf(stderr, fmt2, "");
+
+			if (pinfo->arch) {
+				(void) fprintf(stderr, "(%s) ", pinfo->arch);
+			}
+
+			if (pinfo->version) {
+				(void) fprintf(stderr, "%s", pinfo->version);
+			}
+
+			(void) fprintf(stderr, "\n");
+		}
+
+		n = ckyorn(ans, NULL, NULL, NULL, ASK_CONFIRM);
+		if (n != 0) {
+			quit(n);
+			/* NOTREACHED */
+		}
+
+		if (strchr("yY", *ans) == NULL) {
+			pkginfoFree(&pinfo);
+			return (0);
+		}
+	}
+
+	if (pinfo->status == PI_PRESVR4) {
+		pkginfoFree(&pinfo);
+		return (presvr4(pkginst, nointeract));
+	}
+
+	if (pinfo->status == PI_SPOOLED) {
+		/* removal from a directory */
+		echo(INFO_RMSPOOL, pkginst);
+		pkginfoFree(&pinfo);
+		return (rrmdir(pkginst));
+	}
+
+	/* exit if not root */
+
+	if (getuid()) {
+		progerr(ERR_NOT_ROOT, get_prog_name());
+		exit(1);
+	}
+
+	pkginfoFree(&pinfo);
+
+	zonesSkipped = 0;
+
+	if (interrupted != 0) {
+		echo(MSG_DOREMOVE_INTERRUPTED_B4_Z, pkginst);
+		echoDebug(MSG_DOREMOVE_INTERRUPTED_B4_Z, pkginst);
+		return (n);
+	}
+
+	echoDebug(DBG_REMOVE_FLAG_VALUES, "before pkgZoneRemove",
+		admnflag, doreboot, failflag, interrupted,
+		intrflag, ireboot, nullflag, warnflag);
+
+	for (zoneIndex = 0;
+	    a_zlst != NULL &&
+	    (zoneName = z_zlist_get_zonename(a_zlst, zoneIndex)) != NULL;
+	    zoneIndex++) {
+
+		/* skip the zone if it is NOT running */
+
+		zst = z_zlist_get_current_state(a_zlst, zoneIndex);
+		if (zst != ZONE_STATE_RUNNING && zst != ZONE_STATE_MOUNTED) {
+			zonesSkipped++;
+			echoDebug(DBG_SKIPPING_ZONE, zoneName);
+			continue;
+		}
+
+		echo(MSG_REMOVE_PKG_FROM_ZONE, pkginst, zoneName);
+		echoDebug(DBG_REMOVE_PKG_FROM_ZONE, pkginst, zoneName);
+
+		/* determine list of directories inherited from global zone */
+
+		inheritedPkgDirs = z_zlist_get_inherited_pkg_dirs(a_zlst,
+					zoneIndex);
+
+		/*
+		 * remove package from zone; use the zone admin file which
+		 * suppresses all checks.
+		 */
+
+		n = pkgZoneRemove(z_zlist_get_scratch(a_zlst, zoneIndex),
+			inheritedPkgDirs, a_nodelete, a_altBinDir,
+			a_zoneAdminFile, zst);
+
+		/* set success/fail condition variables */
+
+		ckreturn(n);
+
+		echoDebug(DBG_REMOVE_FLAG_VALUES, "after pkgZoneRemove",
+			admnflag, doreboot, failflag, interrupted, intrflag,
+			ireboot, nullflag, warnflag);
+	}
+
+	if (zonesSkipped > 0) {
+		echoDebug(DBG_ZONES_SKIPPED, zonesSkipped);
+
+		for (zoneIndex = 0;
+			(zoneName = z_zlist_get_zonename(a_zlst, zoneIndex)) !=
+				(char *)NULL; zoneIndex++) {
+
+			/* skip the zone if it IS running */
+
+			zst = z_zlist_get_current_state(a_zlst, zoneIndex);
+			if (zst == ZONE_STATE_RUNNING ||
+			    zst == ZONE_STATE_MOUNTED) {
+				zonesSkipped++;
+				echoDebug(DBG_SKIPPING_ZONE_BOOT, zoneName);
+				continue;
+			}
+
+			/* skip the zone if it is NOT bootable */
+
+			if (z_zlist_is_zone_runnable(a_zlst,
+						zoneIndex) == B_FALSE) {
+				echo(MSG_SKIPPING_ZONE_NOT_RUNNABLE, zoneName);
+				echoDebug(DBG_SKIPPING_ZONE_NOT_RUNNABLE,
+					zoneName);
+				continue;
+			}
+
+			/* mount up the zone */
+
+			echo(MSG_BOOTING_ZONE, zoneName);
+			echoDebug(DBG_BOOTING_ZONE, zoneName);
+
+			b = z_zlist_change_zone_state(a_zlst, zoneIndex,
+				ZONE_STATE_MOUNTED);
+			if (b == B_FALSE) {
+				progerr(ERR_CANNOT_BOOT_ZONE, zoneName);
+				/* set fatal error return condition */
+				ckreturn(1);
+				continue;
+			}
+
+			echo(MSG_REMOVE_PKG_FROM_ZONE, pkginst, zoneName);
+
+			/* determine list of dirs inherited from global zone */
+
+			inheritedPkgDirs =
+				z_zlist_get_inherited_pkg_dirs(a_zlst,
+						zoneIndex);
+
+			/*
+			 * remove package from zone; use the zone admin file
+			 * which suppresses all checks.
+			 */
+
+			n = pkgZoneRemove(z_zlist_get_scratch(a_zlst,
+				zoneIndex), inheritedPkgDirs,
+				a_nodelete, a_altBinDir, a_zoneAdminFile,
+				ZONE_STATE_MOUNTED);
+
+			/* set success/fail condition variables */
+
+			ckreturn(n);
+
+			echoDebug(DBG_REMOVE_FLAG_VALUES, "after pkgZoneRemove",
+				admnflag, doreboot, failflag, interrupted,
+				intrflag, ireboot, nullflag, warnflag);
+
+			/* restore original state of zone */
+
+			echo(MSG_RESTORE_ZONE_STATE, zoneName);
+			echoDebug(DBG_RESTORE_ZONE_STATE, zoneName);
+
+			b = z_zlist_restore_zone_state(a_zlst, zoneIndex);
+		}
+	}
+
+	/*
+	 * Process global zone if it was either the only possible
+	 * target (no list of zones specified) or it appears in the list
+	 */
+	if (a_zlst == NULL || z_on_zone_spec(GLOBAL_ZONENAME)) {
+		/* reset interrupted flag before calling pkgremove */
+		interrupted = 0;	/* last action was NOT quit */
+
+		/*
+		 * call pkgremove for this package for the global zone;
+		 * use the admin file passed in by the user via -a.
+		 */
+		n = pkgRemove(a_nodelete, a_altBinDir, a_adminFile,
+		    z_get_inherited_file_systems());
+
+		/* set success/fail condition variables */
+		ckreturn(n);
+	}
+
+	return (n);
+}
+
+/*
+ *  function to clear out any exisiting error return conditions that may have
+ *  been set by previous calls to ckreturn()
+ */
+static void
+resetreturn()
+{
+	admnflag = 0;	/* != 0 if any pkg op admin setting failure (4) */
+	doreboot = 0;	/* != 0 if reboot required after installation (>= 10) */
+	failflag = 0;	/* != 0 if fatal error has occurred (1) */
+	intrflag = 0;	/* != 0 if user selected quit (3) */
+	ireboot = 0;	/* != 0 if immediate reboot required (>= 20) */
+	nullflag = 0;	/* != 0 if admin interaction required (5) */
+	warnflag = 0;	/* != 0 if non-fatal error has occurred (2) */
+	interrupted = 0;	/* last pkg op was quit (1,2,3,4,5) */
+}
+
+/*
+ *  function which checks the indicated return value
+ *  and indicates disposition of installation
+ */
+static void
+ckreturn(int retcode)
+{
+	/*
+	 * entry debugging info
+	 */
+
+	echoDebug(DBG_PKGRM_CKRETURN, retcode, PSTR(pkginst));
+
+	switch (retcode) {
+	    case  0:		/* successful */
+	    case 10:
+	    case 20:
+		break; /* empty case */
+
+	    case  1:		/* package operation failed (fatal error) */
+	    case 11:
+	    case 21:
+		failflag++;
+		interrupted++;
+		break;
+
+	    case  2:		/* non-fatal error (warning) */
+	    case 12:
+	    case 22:
+		warnflag++;
+		interrupted++;
+		break;
+
+	    case  3:		/* user selected quit; operation interrupted */
+	    case 13:
+	    case 23:
+		intrflag++;
+		interrupted++;
+		break;
+
+	    case  4:		/* admin settings prevented operation */
+	    case 14:
+	    case 24:
+		admnflag++;
+		interrupted++;
+		break;
+
+	    case  5:		/* administration: interaction req (no -n) */
+	    case 15:
+	    case 25:
+		nullflag++;
+		interrupted++;
+		break;
+
+	    default:
+		failflag++;
+		interrupted++;
+		return;
+	}
+
+	if (retcode >= 20) {
+		ireboot++;
+	} else if (retcode >= 10) {
+		doreboot++;
+	}
+}
+
+static int
+pkgZoneCheckRemove(char *a_zoneName, char **a_inheritedPkgDirs,
+	char *a_altBinDir, char *a_adminFile, char *a_stdoutPath,
+	zone_state_t a_zoneState)
+{
+	char	*arg[MAXARGS];
+	char	*p;
+	char	adminfd_path[PATH_MAX];
+	char	path[PATH_MAX];
+	int	fds[MAX_FDS];
+	int	maxfds;
+	int	n;
+	int	nargs;
+
+	/* entry assertions */
+
+	assert(a_zoneName != (char *)NULL);
+	assert(*a_zoneName != '\0');
+
+	/* entry debugging info */
+
+	echoDebug(DBG_PKGZONECHECKREMOVE_ENTRY);
+	echoDebug(DBG_PKGZONECHECKREMOVE_ARGS, a_zoneName, PSTR(pkginst),
+		PSTR(pkgdev.dirname), PSTR(a_adminFile), PSTR(a_stdoutPath));
+
+	/* generate path to pkgremove */
+
+	(void) snprintf(path, sizeof (path), "%s/pkgremove",
+		a_altBinDir == (char *)NULL ? PKGBIN : a_altBinDir);
+
+	/* start at first file descriptor */
+
+	maxfds = 0;
+
+	/*
+	 * generate argument list for call to pkgremove
+	 */
+
+	/* start at argument 0 */
+
+	nargs = 0;
+
+	/* first argument is path to executable */
+
+	arg[nargs++] = strdup(path);
+
+	/* second argument is always: pass -O debug to pkgremove: debug mode */
+
+	if (debugFlag == B_TRUE) {
+		arg[nargs++] = "-O";
+		arg[nargs++] = "debug";
+	}
+
+	/* pkgrm -b dir: pass -b to pkgremove */
+
+	if (a_altBinDir != (char *)NULL) {
+		arg[nargs++] = "-b";
+		arg[nargs++] = a_altBinDir;
+	}
+
+	/*
+	 * NONABI_SCRIPTS defined: pass -o to pkgremove; refers to a
+	 * pkg requiring operator interaction during a procedure script
+	 * (common before on1093)
+	 */
+
+	if (old_pkg) {
+		arg[nargs++] = "-o";
+	}
+
+	/*
+	 * PKG_NONABI_SYMLINKS defined: pass -y to pkgremove; process
+	 * symlinks consistent with old behavior
+	 */
+
+	if (old_symlinks) {
+		arg[nargs++] = "-y";
+	}
+
+	/* pkgrm -M: pass -M to pkgremove: don't mount client file systems */
+
+	arg[nargs++] = "-M";
+
+	/* pkgrm -A: pass -A to pkgremove */
+
+	if (pkgrmremote) {
+		arg[nargs++] = "-A";
+	}
+
+	/* pkgrm -v: pass -v to pkgremove: never trace scripts */
+
+	/* pass "-O enable-hollow-package-support" */
+
+	if (is_depend_pkginfo_DB()) {
+		arg[nargs++] = "-O";
+		arg[nargs++] = "enable-hollow-package-support";
+	}
+
+	/* pass -n to pkgremove: always in noninteractive mode */
+
+	arg[nargs++] = "-n";
+
+	/* pkgrm -a admin: pass -a admin to pkgremove: admin file */
+
+	if (a_adminFile) {
+		int fd;
+		fd = openLocal(a_adminFile, O_RDONLY, tmpdir);
+		if (fd < 0) {
+			progerr(ERR_CANNOT_COPY_LOCAL, a_adminFile,
+				errno, strerror(errno));
+			return (1);
+		}
+		(void) snprintf(adminfd_path, sizeof (adminfd_path),
+			"/proc/self/fd/%d", fd);
+		fds[maxfds++] = fd;
+		arg[nargs++] = "-a";
+		arg[nargs++] = strdup(adminfd_path);
+	}
+
+	/*
+	 * pkgadd -R root: pass -R /a to pkgremove in mounted zone
+	 */
+	if (a_zoneState == ZONE_STATE_MOUNTED) {
+		arg[nargs++] = "-R";
+		arg[nargs++] = "/a";
+	}
+
+	/* pkgrm -F: pass -F to pkgremove: always update DB only */
+
+	arg[nargs++] = "-F";
+
+	/* pass "-O preremovecheck" */
+
+	arg[nargs++] = "-O";
+	arg[nargs++] = "preremovecheck";
+
+	/* add "-O addzonename" */
+
+	arg[nargs++] = "-O";
+	arg[nargs++] = "addzonename";
+
+	/* add all inherited file systems */
+
+	if (a_inheritedPkgDirs != (char **)NULL) {
+		for (n = 0; a_inheritedPkgDirs[n] != (char *)NULL; n++) {
+			char	ifs[MAXPATHLEN+22];
+			(void) snprintf(ifs, sizeof (ifs),
+				"inherited-filesystem=%s",
+				a_inheritedPkgDirs[n]);
+			arg[nargs++] = "-O";
+			arg[nargs++] = strdup(ifs);
+		}
+	}
+
+	/*
+	 * add parent zone info/type
+	 */
+
+	p = z_get_zonename();
+	if ((p != NULL) && (*p != '\0')) {
+			char	zn[MAXPATHLEN];
+			(void) snprintf(zn, sizeof (zn),
+				"parent-zone-name=%s", p);
+			arg[nargs++] = "-O";
+			arg[nargs++] = strdup(zn);
+	}
+
+	/* current zone type */
+
+	arg[nargs++] = "-O";
+	if (z_running_in_global_zone() == B_TRUE) {
+			char	zn[MAXPATHLEN];
+			(void) snprintf(zn, sizeof (zn),
+				"parent-zone-type=%s",
+				TAG_VALUE_GLOBAL_ZONE);
+			arg[nargs++] = strdup(zn);
+	} else {
+			char	zn[MAXPATHLEN];
+			(void) snprintf(zn, sizeof (zn),
+				"parent-zone-type=%s",
+				TAG_VALUE_NONGLOBAL_ZONE);
+			arg[nargs++] = strdup(zn);
+	}
+
+	/* pass -N to pkgremove: program name to report */
+
+	arg[nargs++] = "-N";
+	arg[nargs++] = get_prog_name();
+
+	/* add package instance name */
+
+	arg[nargs++] = pkginst;
+
+	/* terminate argument list */
+
+	arg[nargs++] = NULL;
+
+	/* execute pkgremove command */
+
+	if (debugFlag == B_TRUE) {
+		echoDebug(DBG_ZONE_EXEC_ENTER, a_zoneName, arg[0]);
+		for (n = 0; arg[n]; n++) {
+			echoDebug(DBG_ARG, n, arg[n]);
+		}
+	}
+
+	/* terminate file descriptor list */
+
+	fds[maxfds] = -1;
+
+	/* exec command in zone */
+
+	n = z_zone_exec(a_zoneName, path, arg, a_stdoutPath, (char *)NULL, fds);
+
+	echoDebug(DBG_ZONE_EXEC_EXIT, a_zoneName, arg[0], n,
+			PSTR(a_stdoutPath));
+
+	/*
+	 * close any files that were opened for use by the
+	 * /proc/self/fd interface so they could be passed to programs
+	 * via the z_zone_exec() interface
+	 */
+
+	for (; maxfds > 0; maxfds--) {
+		(void) close(fds[maxfds-1]);
+	}
+
+	/* return results of pkgremove in zone execution */
+
+	return (n);
+}
+
+static int
+pkgZoneRemove(char *a_zoneName, char **a_inheritedPkgDirs,
+	int a_nodelete, char *a_altBinDir, char *a_adminFile,
+	zone_state_t a_zoneState)
+{
+	char	*arg[MAXARGS];
+	char	*p;
+	char	adminfd_path[PATH_MAX];
+	char	path[PATH_MAX];
+	int	fds[MAX_FDS];
+	int	maxfds;
+	int	n;
+	int	nargs;
+
+	/* entry assertions */
+
+	assert(a_zoneName != (char *)NULL);
+	assert(*a_zoneName != '\0');
+
+	/* entry debugging info */
+
+	echoDebug(DBG_PKGZONEREMOVE_ENTRY);
+	echoDebug(DBG_PKGZONEREMOVE_ARGS, a_zoneName, PSTR(pkginst),
+		PSTR(pkgdev.dirname), a_nodelete, PSTR(a_adminFile));
+
+	/* generate path to pkgremove */
+
+	(void) snprintf(path, sizeof (path), "%s/pkgremove",
+		a_altBinDir == (char *)NULL ? PKGBIN : a_altBinDir);
+
+	/* start at first file descriptor */
+
+	maxfds = 0;
+
+	/*
+	 * generate argument list for call to pkgremove
+	 */
+
+	/* start at argument 0 */
+
+	nargs = 0;
+
+	/* first argument is path to executable */
+
+	arg[nargs++] = strdup(path);
+
+	/* second argument is always: pass -O debug to pkgremove: debug mode */
+
+	if (debugFlag == B_TRUE) {
+		arg[nargs++] = "-O";
+		arg[nargs++] = "debug";
+	}
+
+	/* pkgrm -b dir: pass -b to pkgremove */
+
+	if (a_altBinDir != (char *)NULL) {
+		arg[nargs++] = "-b";
+		arg[nargs++] = a_altBinDir;
+	}
+
+	/*
+	 * NONABI_SCRIPTS defined: pass -o to pkgremove; refers to a
+	 * pkg requiring operator interaction during a procedure script
+	 * (common before on1093)
+	 */
+
+	if (old_pkg) {
+		arg[nargs++] = "-o";
+	}
+
+	/*
+	 * PKG_NONABI_SYMLINKS defined: pass -y to pkgremove; process
+	 * symlinks consistent with old behavior
+	 */
+
+	if (old_symlinks) {
+		arg[nargs++] = "-y";
+	}
+
+	/* pkgrm -M: pass -M to pkgremove: don't mount client file systems */
+
+	arg[nargs++] = "-M";
+
+	/* pkgrm -A: pass -A to pkgremove */
+
+	if (pkgrmremote) {
+		arg[nargs++] = "-A";
+	}
+
+	/* pkgrm -v: pass -v to pkgremove: trace scripts */
+
+	if (pkgverbose) {
+		arg[nargs++] = "-v";
+	}
+
+	/* pass "-O enable-hollow-package-support" */
+
+	if (is_depend_pkginfo_DB()) {
+		arg[nargs++] = "-O";
+		arg[nargs++] = "enable-hollow-package-support";
+	}
+
+	/* pkgrm -n: pass -n to pkgremove: noninteractive mode */
+
+	if (nointeract) {
+		arg[nargs++] = "-n";
+	}
+
+	/* pkgrm -a admin: pass -a admin to pkgremove: admin file */
+
+	if (a_adminFile) {
+		int fd;
+		fd = openLocal(a_adminFile, O_RDONLY, tmpdir);
+		if (fd < 0) {
+			progerr(ERR_CANNOT_COPY_LOCAL, a_adminFile,
+				errno, strerror(errno));
+			return (1);
+		}
+		(void) snprintf(adminfd_path, sizeof (adminfd_path),
+			"/proc/self/fd/%d", fd);
+		fds[maxfds++] = fd;
+		arg[nargs++] = "-a";
+		arg[nargs++] = adminfd_path;
+	}
+
+	/*
+	 * pkgadd -R root: pass -R /a to pkgremove in mounted zone
+	 */
+	if (a_zoneState == ZONE_STATE_MOUNTED) {
+		arg[nargs++] = "-R";
+		arg[nargs++] = "/a";
+	}
+
+	/* pkgrm -F: pass -F to pkgremove: update DB only */
+
+	if (a_nodelete) {
+		arg[nargs++] = "-F";
+	}
+
+	/* add "-O addzonename" */
+
+	arg[nargs++] = "-O";
+	arg[nargs++] = "addzonename";
+
+	/* add all inherited file systems */
+
+	if (a_inheritedPkgDirs != (char **)NULL) {
+		for (n = 0; a_inheritedPkgDirs[n] != (char *)NULL; n++) {
+			char	ifs[MAXPATHLEN+22];
+
+			(void) snprintf(ifs, sizeof (ifs),
+				"inherited-filesystem=%s",
+				a_inheritedPkgDirs[n]);
+			arg[nargs++] = "-O";
+			arg[nargs++] = strdup(ifs);
+		}
+	}
+
+	/*
+	 * add parent zone info/type
+	 */
+
+	p = z_get_zonename();
+	if ((p != NULL) && (*p != '\0')) {
+			char	zn[MAXPATHLEN];
+			(void) snprintf(zn, sizeof (zn),
+				"parent-zone-name=%s", p);
+			arg[nargs++] = "-O";
+			arg[nargs++] = strdup(zn);
+	}
+
+	/* current zone type */
+
+	arg[nargs++] = "-O";
+	if (z_running_in_global_zone() == B_TRUE) {
+			char	zn[MAXPATHLEN];
+			(void) snprintf(zn, sizeof (zn),
+				"parent-zone-type=%s",
+				TAG_VALUE_GLOBAL_ZONE);
+			arg[nargs++] = strdup(zn);
+	} else {
+			char	zn[MAXPATHLEN];
+			(void) snprintf(zn, sizeof (zn),
+				"parent-zone-type=%s",
+				TAG_VALUE_NONGLOBAL_ZONE);
+			arg[nargs++] = strdup(zn);
+	}
+
+	/* pass -N to pkgremove: program name to report */
+
+	arg[nargs++] = "-N";
+	arg[nargs++] = get_prog_name();
+
+	/* add package instance name */
+
+	arg[nargs++] = pkginst;
+
+	/* terminate argument list */
+
+	arg[nargs++] = NULL;
+
+	/* execute pkgremove command */
+
+	if (debugFlag == B_TRUE) {
+		echoDebug(DBG_ZONE_EXEC_ENTER, a_zoneName, arg[0]);
+		for (n = 0; arg[n]; n++) {
+			echoDebug(DBG_ARG, n, arg[n]);
+		}
+	}
+
+	/* terminate file descriptor list */
+
+	fds[maxfds] = -1;
+
+	/* exec command in zone */
+
+	n = z_zone_exec(a_zoneName, path, arg, (char *)NULL, (char *)NULL, fds);
+
+	/*
+	 * close any files that were opened for use by the
+	 * /proc/self/fd interface so they could be passed to programs
+	 * via the z_zone_exec() interface
+	 */
+
+	for (; maxfds > 0; maxfds--) {
+		(void) close(fds[maxfds-1]);
+	}
+
+	return (n);
+}
+
+/*
+ * Name:	pkgRemove
+ * Description:	Invoke pkgremove in the current zone to perform a remove
+ *		of a single package from the current zone or standalone system
+ * Arguments:	a_nodelete: should the files and scripts remain installed?
+ *			- if != 0 pass -F flag to pkgremove - suppress
+ *			the removal of any files and any class action scripts
+ *			and suppress the running of any class action scripts.
+ *			The package files remain but the package looks like it
+ *			is not installed. This is mainly for use by upgrade.
+ *			- if == 0 do not pass -F flag to pkgremove - all
+ *			files and class action scripts are removed, and any
+ *			appropriate class action scripts are run.
+ *		a_altBinDir - pointer to string representing location of the
+ *			pkgremove executable to run. If not NULL, then pass
+ *			the path specified to the -b option to pkgremove.
+ *		a_adminFile - pointer to string representing the admin
+ *			file to pass to pkgremove when removing the package.
+ *			If this is == NULL no admin file is given to pkgremove.
+ *		a_inheritedPkgDirs - pointer to array of strings, each one
+ *			representing the non-global zones full path of a
+ *			directory that is inherited from the global zone.
+ * Returns:	int	(see ckreturn() function for details)
+ *		0 - success
+ *		1 - package operation failed (fatal error)
+ *		2 - non-fatal error (warning)
+ *		3 - user selected quit (operation interrupted)
+ *		4 - admin settings prevented operation
+ *		5 - interaction required and -n (non-interactive) specified
+ *		"10" will be added to indicate "immediate reboot required"
+ *		"20" will be added to indicate "reboot after install required"
+ */
+
+static int
+pkgRemove(int a_nodelete, char *a_altBinDir, char *a_adminFile,
+	char **a_inheritedPkgDirs)
+{
+	char	*arg[MAXARGS];
+	char	*p;
+	char	path[PATH_MAX];
+	int	n;
+	int	nargs;
+
+	/* entry debugging info */
+
+	echoDebug(DBG_PKGREMOVE_ENTRY);
+	echoDebug(DBG_PKGREMOVE_ARGS, PSTR(pkginst), PSTR(pkgdev.dirname),
+		a_nodelete, PSTR(a_adminFile));
+
+	(void) snprintf(path, sizeof (path), "%s/pkgremove",
+		a_altBinDir == (char *)NULL ? PKGBIN : a_altBinDir);
+
+	nargs = 0;
+
+	/* first argument is path to executable */
+
+	arg[nargs++] = strdup(path);
+
+	/* second argument is always: pass -O debug to pkgremove: debug mode */
+
+	if (debugFlag == B_TRUE) {
+		arg[nargs++] = "-O";
+		arg[nargs++] = "debug";
+	}
+
+	/* pkgrm -b dir: pass -b to pkgremove */
+
+	if (a_altBinDir != (char *)NULL) {
+		arg[nargs++] = "-b";
+		arg[nargs++] = a_altBinDir;
+	}
+
+	/*
+	 * NONABI_SCRIPTS defined: pass -o to pkgremove; refers to a
+	 * pkg requiring operator interaction during a procedure script
+	 * (common before on1093)
+	 */
+
+	if (old_pkg) {
+		arg[nargs++] = "-o";
+	}
+
+	/*
+	 * PKG_NONABI_SYMLINKS defined: pass -y to pkgremove; process
+	 * symlinks consistent with old behavior
+	 */
+
+	if (old_symlinks) {
+		arg[nargs++] = "-y";
+	}
+
+	/* pkgrm -M: pass -M to pkgrm: dont mount client file systems */
+
+	if (no_map_client) {
+		arg[nargs++] = "-M";
+	}
+
+	/* pkgrm -A: pass -A to pkgrm */
+
+	if (pkgrmremote) {
+		arg[nargs++] = "-A";
+	}
+
+	/* pkgrm -v: pass -v to pkgremove: trace scripts */
+
+	if (pkgverbose) {
+		arg[nargs++] = "-v";
+	}
+
+	/* pkgrm -n: pass -n to pkgremove: noninteractive mode */
+
+	if (nointeract) {
+		arg[nargs++] = "-n";
+	}
+
+	/* pkgrm -a admin: pass -a admin to pkgremove: admin file */
+
+	if (a_adminFile) {
+		arg[nargs++] = "-a";
+		arg[nargs++] = strdup(a_adminFile);
+	}
+
+	/* pkgrm -V vfstab: pass -V vfstab to pkgremove: alternate vfstab */
+
+	if (vfstab_file) {
+		arg[nargs++] = "-V";
+		arg[nargs++] = vfstab_file;
+	}
+
+	/* pkgrm -R root: pass -R root to pkgremove: alternative root */
+
+	if (is_an_inst_root()) {
+		arg[nargs++] = "-R";
+		arg[nargs++] = get_inst_root();
+	}
+
+	/* pkgrm -F: pass -F to pkgremove: update DB only */
+
+	if (a_nodelete) {
+		arg[nargs++] = "-F";
+	}
+
+	/* add all inherited file systems */
+
+	if (a_inheritedPkgDirs != (char **)NULL) {
+		for (n = 0; a_inheritedPkgDirs[n] != (char *)NULL; n++) {
+			char	ifs[MAXPATHLEN+22];
+			(void) snprintf(ifs, sizeof (ifs),
+				"inherited-filesystem=%s",
+				a_inheritedPkgDirs[n]);
+			arg[nargs++] = "-O";
+			arg[nargs++] = strdup(ifs);
+		}
+	}
+
+	/*
+	 * add parent zone info/type
+	 */
+
+	p = z_get_zonename();
+	if ((p != NULL) && (*p != '\0')) {
+			char	zn[MAXPATHLEN];
+			(void) snprintf(zn, sizeof (zn),
+				"parent-zone-name=%s", p);
+			arg[nargs++] = "-O";
+			arg[nargs++] = strdup(zn);
+	}
+
+	/* current zone type */
+
+	arg[nargs++] = "-O";
+	if (z_running_in_global_zone() == B_TRUE) {
+			char	zn[MAXPATHLEN];
+			(void) snprintf(zn, sizeof (zn),
+				"parent-zone-type=%s",
+				TAG_VALUE_GLOBAL_ZONE);
+			arg[nargs++] = strdup(zn);
+	} else {
+			char	zn[MAXPATHLEN];
+			(void) snprintf(zn, sizeof (zn),
+				"parent-zone-type=%s",
+				TAG_VALUE_NONGLOBAL_ZONE);
+			arg[nargs++] = strdup(zn);
+	}
+
+	/* pass -N to pkgremove: program name to report */
+
+	arg[nargs++] = "-N";
+	arg[nargs++] = get_prog_name();
+
+	/* add package instance name */
+
+	arg[nargs++] = pkginst;
+
+	/* terminate argument list */
+
+	arg[nargs++] = NULL;
+
+	/*
+	 * run the appropriate pkgremove command in the specified zone
+	 */
+
+	if (debugFlag == B_TRUE) {
+		echoDebug(DBG_ZONE_EXEC_ENTER, "global", arg[0]);
+		for (n = 0; arg[n]; n++) {
+			echoDebug(DBG_ARG, n, arg[n]);
+		}
+	}
+
+	/* execute pkgremove command */
+
+	n = pkgexecv(NULL, NULL, NULL, NULL, arg);
+
+	/* return results of pkgrm in this zone */
+
+	return (n);
+}
+
+static void
+usage(void)
+{
+	char	*prog = get_prog_name();
+
+	(void) fprintf(stderr, ERR_USAGE_PKGRM, prog, prog);
+	exit(1);
+}
+
+/*
+ * Name:	remove_packages_in_global_with_zones
+ * Description:	Remove packages from the global zone and from non-global zones
+ *		when run from the global zone and when non-global zones are
+ *		present.
+ * Arguments:	a_pkgList - pointer to array of strings, each string specifying
+ *			the name of one package to be removed.
+ *		a_nodelete: should the files and scripts remain installed?
+ *			- if != 0 pass -F flag to pkgremove - suppress
+ *			the removal of any files and any class action scripts
+ *			and suppress the running of any class action scripts.
+ *			The package files remain but the package looks like it
+ *			is not installed. This is mainly for use by upgrade.
+ *			- if == 0 do not pass -F flag to pkgremove - all
+ *			files and class action scripts are removed, and any
+ *			appropriate class action scripts are run.
+ *		a_longestPkg - length of the longest package "name" (for
+ *			output format alignment)
+ *		a_repeat - are there more packages avialable in "optind"
+ *			- B_TRUE - process packages from optind
+ *			- B_FALSE - do not process packages from optind
+ *		a_altBinDir - pointer to string representing location of the
+ *			pkgremove executable to run. If not NULL, then pass
+ *			the path specified to the -b option to pkgremove.
+ *		a_pkgdir - pointer to string representing the directory
+ *			where the packages to be removed are located.
+ *		a_zlst - list of zones to process; NULL if no zones to process.
+ * Returns:	int	(see ckreturn() function for details)
+ *		0 - success
+ *		1 - package operation failed (fatal error)
+ *		2 - non-fatal error (warning)
+ *		3 - user selected quit (operation interrupted)
+ *		4 - admin settings prevented operation
+ *		5 - interaction required and -n (non-interactive) specified
+ *		"10" will be added to indicate "immediate reboot required"
+ *		"20" will be added to indicate "reboot after install required"
+ */
+
+static boolean_t
+remove_packages_in_global_with_zones(char **a_pkgList, int a_nodelete,
+	int a_longestPkg, int a_repeat, char *a_altBinDir, char *a_pkgdir,
+	zoneList_t a_zlst)
+{
+static	char		*zoneAdminFile = (char *)NULL;
+
+	boolean_t	b;
+	char		**inheritedPkgDirs;
+	char		*zoneName;
+	char		*scratchName;
+	char		preremovecheckPath[PATH_MAX+1];
+	int		i;
+	int		n;
+	int		savenpkgs = npkgs;
+	int		zoneIndex;
+	int		zonesSkipped;
+	zone_state_t	zst;
+
+	/* entry assertions */
+
+	assert(a_zlst != (zoneList_t)NULL);
+	assert(a_pkgList != (char **)NULL);
+	assert(a_longestPkg > 0);
+	assert(a_pkgdir != (char *)NULL);
+	assert(*a_pkgdir != '\0');
+
+	/* entry debugging info */
+
+	echoDebug(DBG_PKGREMPKGSGZWNGZ_ENTRY);
+	echoDebug(DBG_PKGREMPKGSGZWNGZ_ARGS, a_nodelete, a_longestPkg,
+		a_repeat, PSTR(a_altBinDir), PSTR(a_pkgdir));
+
+	/* check all packages */
+
+	if (check_packages(a_pkgList, a_pkgdir) != B_TRUE) {
+		quit(1);
+	}
+
+	/* create temporary directory for use by zone operations */
+
+	create_zone_tempdir(&zoneTempDir, tmpdir);
+
+	/* create hands off settings admin file for use in a non-global zone */
+
+	create_zone_adminfile(&zoneAdminFile, zoneTempDir, admnfile);
+
+	/*
+	 * all of the packages (as listed in the package list) are
+	 * removed one at a time from all non-global zones and then
+	 * from the global zone.
+	 */
+
+	for (i = 0; (pkginst = a_pkgList[i]) != NULL; i++) {
+		/* reset interrupted flag before calling pkgremove */
+
+		interrupted = 0;	/* last action was NOT quit */
+
+		/* skip package if it is "in the global zone only" */
+
+		if (pkgIsPkgInGzOnly(get_inst_root(), pkginst) == B_TRUE) {
+			continue;
+		}
+
+		/*
+		 * if operation failed in global zone do not propagate to
+		 * non-global zones
+		 */
+
+		zonesSkipped = 0;
+
+		if (interrupted != 0) {
+			echo(MSG_DOREMOVE_INTERRUPTED, pkginst);
+			echoDebug(DBG_DOREMOVE_INTERRUPTED, pkginst);
+			break;
+		}
+
+		echoDebug(DBG_REMOVE_FLAG_VALUES, "before loop",
+			admnflag, doreboot, failflag, interrupted,
+			intrflag, ireboot, nullflag, warnflag);
+
+		for (zoneIndex = 0;
+			(zoneName = z_zlist_get_zonename(a_zlst, zoneIndex)) !=
+				(char *)NULL; zoneIndex++) {
+
+			/* skip the zone if it is NOT running */
+
+			zst = z_zlist_get_current_state(a_zlst, zoneIndex);
+			if (zst != ZONE_STATE_RUNNING &&
+			    zst != ZONE_STATE_MOUNTED) {
+				zonesSkipped++;
+				echoDebug(DBG_SKIPPING_ZONE, zoneName);
+				continue;
+			}
+
+			echo(MSG_CHECKREMOVE_PKG_IN_ZONE, pkginst, zoneName);
+			echoDebug(DBG_CHECKREMOVE_PKG_IN_ZONE, pkginst,
+				zoneName);
+
+			scratchName = z_zlist_get_scratch(a_zlst, zoneIndex);
+
+			(void) snprintf(preremovecheckPath,
+				sizeof (preremovecheckPath),
+				"%s/%s.%s.preremovecheck.txt",
+				zoneTempDir, pkginst, scratchName);
+
+			/* determine list of dirs inherited from global zone */
+
+			inheritedPkgDirs =
+				z_zlist_get_inherited_pkg_dirs(a_zlst,
+					zoneIndex);
+
+			/*
+			 * dependency check this package this zone; use the
+			 * user supplied admin file so that the appropriate
+			 * level of dependency checking is (or is not) done.
+			 */
+
+			n = pkgZoneCheckRemove(scratchName, inheritedPkgDirs,
+				a_altBinDir, admnfile, preremovecheckPath,
+				zst);
+
+			/* set success/fail condition variables */
+
+			ckreturn(n);
+
+			echoDebug(DBG_REMOVE_FLAG_VALUES,
+				"after pkgzonecheckremove",
+				admnflag, doreboot, failflag, interrupted,
+				intrflag, ireboot, nullflag, warnflag);
+		}
+
+		if (zonesSkipped == 0) {
+			continue;
+		}
+
+		echoDebug(DBG_ZONES_SKIPPED, zonesSkipped);
+
+		for (zoneIndex = 0;
+			(zoneName = z_zlist_get_zonename(a_zlst, zoneIndex)) !=
+				(char *)NULL; zoneIndex++) {
+
+			/* skip the zone if it IS running */
+
+			zst = z_zlist_get_current_state(a_zlst, zoneIndex);
+			if (zst == ZONE_STATE_RUNNING ||
+			    zst == ZONE_STATE_MOUNTED) {
+				zonesSkipped++;
+				echoDebug(DBG_SKIPPING_ZONE_BOOT, zoneName);
+				continue;
+			}
+
+			/* skip the zone if it is NOT bootable */
+
+			if (z_zlist_is_zone_runnable(a_zlst,
+						zoneIndex) == B_FALSE) {
+				echo(MSG_SKIPPING_ZONE_NOT_RUNNABLE, zoneName);
+				echoDebug(DBG_SKIPPING_ZONE_NOT_RUNNABLE,
+					zoneName);
+				continue;
+			}
+
+			/* mount up the zone */
+
+			echo(MSG_BOOTING_ZONE, zoneName);
+			echoDebug(DBG_BOOTING_ZONE, zoneName);
+
+			b = z_zlist_change_zone_state(a_zlst, zoneIndex,
+				ZONE_STATE_MOUNTED);
+			if (b == B_FALSE) {
+				progerr(ERR_CANNOT_BOOT_ZONE, zoneName);
+				/* set fatal error return condition */
+				ckreturn(1);
+				continue;
+			}
+
+			echo(MSG_CHECKREMOVE_PKG_IN_ZONE, pkginst, zoneName);
+			echoDebug(DBG_CHECKREMOVE_PKG_IN_ZONE, pkginst,
+					zoneName);
+
+			scratchName = z_zlist_get_scratch(a_zlst, zoneIndex);
+
+			(void) snprintf(preremovecheckPath,
+				sizeof (preremovecheckPath),
+				"%s/%s.%s.preremovecheck.txt",
+				zoneTempDir, pkginst, scratchName);
+
+			/* determine list of dirs inherited from global zone */
+
+			inheritedPkgDirs =
+				z_zlist_get_inherited_pkg_dirs(a_zlst,
+					zoneIndex);
+
+			/*
+			 * dependency check this package this zone; use the
+			 * user supplied admin file so that the appropriate
+			 * level of dependency checking is (or is not) done.
+			 */
+
+			n = pkgZoneCheckRemove(scratchName, inheritedPkgDirs,
+				a_altBinDir, admnfile, preremovecheckPath,
+				ZONE_STATE_MOUNTED);
+
+			/* set success/fail condition variables */
+
+			ckreturn(n);
+
+			echoDebug(DBG_REMOVE_FLAG_VALUES,
+				"after pkgzonecheckremove",
+				admnflag, doreboot, failflag, interrupted,
+				intrflag, ireboot, nullflag, warnflag);
+
+			/* restore original state of zone */
+
+			echo(MSG_RESTORE_ZONE_STATE, zoneName);
+			echoDebug(DBG_RESTORE_ZONE_STATE, zoneName);
+
+			b = z_zlist_restore_zone_state(a_zlst, zoneIndex);
+		}
+		npkgs--;
+	}
+
+	/*
+	 * look at all pre-remove check files
+	 */
+
+	i = preremove_verify(a_pkgList, a_zlst, zoneTempDir);
+	if (i != 0) {
+		quit(i);
+	}
+
+	npkgs = savenpkgs;
+
+	/*
+	 * reset all error return condition variables that may have been
+	 * set during package removal dependency checking so that they
+	 * do not reflect on the success/failure of the actual package
+	 * removal operations
+	 */
+
+	resetreturn();
+
+	/*
+	 * all of the packages (as listed in the package list) are
+	 * removed one at a time.
+	 */
+
+	interrupted = 0;
+	for (i = 0; (pkginst = a_pkgList[i]) != NULL; i++) {
+		boolean_t	in_gz_only;
+		started = 0;
+
+		if (shall_we_continue(pkginst, npkgs) == B_FALSE) {
+			continue;
+		}
+
+		in_gz_only = pkgIsPkgInGzOnly(get_inst_root(), pkginst);
+
+		/* reset interrupted flag before calling pkgremove */
+
+		interrupted = 0;
+
+		/*
+		 * pkgrm invoked from within the global zone and there are
+		 * non-global zones configured:
+		 * Remove the package from the global zone.
+		 * If not removing the package from the global zone only,
+		 * then remove the package from the list of zones specified.
+		 */
+
+		if (in_gz_only) {
+			/* global zone only */
+			n = doRemove(a_nodelete, a_altBinDir, a_longestPkg,
+				admnfile, (char *)NULL, (zoneList_t)NULL);
+		} else {
+			/* global zone and non-global zones */
+			n = doRemove(a_nodelete, a_altBinDir, a_longestPkg,
+				zoneAdminFile, zoneAdminFile, a_zlst);
+		}
+
+		/* set success/fail condition variables */
+
+		ckreturn(n);
+
+		npkgs--;
+	}
+
+	/*
+	 * all packages in the package list have been removed.
+	 * Continue with removal if:
+	 * -- immediate reboot is NOT required
+	 * -- there are more packages to remove
+	 * else return do NOT continue.
+	 */
+
+	if ((ireboot == 0) && (a_repeat != 0)) {
+		return (B_TRUE);
+	}
+
+	/* return 'dont continue' */
+
+	return (B_FALSE);
+}
+
+/*
+ * Name:	remove_packages_in_nonglobal_zone
+ * Description:	Remove packages in a non-global zone when run from a
+ *		non-global zone.
+ * Arguments:	a_pkgList - pointer to array of strings, each string specifying
+ *			the name of one package to be removed.
+ *		a_nodelete: should the files and scripts remain installed?
+ *			- if != 0 pass -F flag to pkgremove - suppress
+ *			the removal of any files and any class action scripts
+ *			and suppress the running of any class action scripts.
+ *			The package files remain but the package looks like it
+ *			is not installed. This is mainly for use by upgrade.
+ *			- if == 0 do not pass -F flag to pkgremove - all
+ *			files and class action scripts are removed, and any
+ *			appropriate class action scripts are run.
+ *		a_longestPkg - length of the longest package "name" (for
+ *			output format alignment)
+ *		a_repeat - are there more packages avialable in "optind"
+ *			- B_TRUE - process packages from optind
+ *			- B_FALSE - do not process packages from optind
+ *		a_altBinDir - pointer to string representing location of the
+ *			pkgremove executable to run. If not NULL, then pass
+ *			the path specified to the -b option to pkgremove.
+ *		a_pkgdir - pointer to string representing the directory
+ *			where the packages to be removed are located.
+ * Returns:	int	(see ckreturn() function for details)
+ *		0 - success
+ *		1 - package operation failed (fatal error)
+ *		2 - non-fatal error (warning)
+ *		3 - user selected quit (operation interrupted)
+ *		4 - admin settings prevented operation
+ *		5 - interaction required and -n (non-interactive) specified
+ *		"10" will be added to indicate "immediate reboot required"
+ *		"20" will be added to indicate "reboot after install required"
+ */
+
+static boolean_t
+remove_packages_in_nonglobal_zone(char **a_pkgList, int a_nodelete,
+	int a_longestPkg, int a_repeat, char *a_altBinDir, char *a_pkgdir)
+{
+static	char		*zoneAdminFile = (char *)NULL;
+
+	int		n;
+	int		i;
+
+	/* entry assertions */
+
+	assert(a_pkgList != (char **)NULL);
+	assert(a_longestPkg > 0);
+	assert(a_pkgdir != (char *)NULL);
+	assert(*a_pkgdir != '\0');
+
+	/* entry debugging info */
+
+	echoDebug(DBG_PKGREMPKGSNGZ_ENTRY);
+	echoDebug(DBG_PKGREMPKGSNGZ_ARGS, a_nodelete, a_longestPkg,
+		a_repeat, PSTR(a_altBinDir), PSTR(a_pkgdir));
+
+	/* check all package */
+
+	if (check_packages(a_pkgList, a_pkgdir) != B_TRUE) {
+		quit(1);
+	}
+
+	/* create temporary directory for use by zone operations */
+
+	create_zone_tempdir(&zoneTempDir, tmpdir);
+
+	/* create hands off settings admin file for use in a non-global zone */
+
+	create_zone_adminfile(&zoneAdminFile, zoneTempDir, admnfile);
+
+	/*
+	 * all of the packages (as listed in the package list) are
+	 * removed one at a time.
+	 */
+
+	interrupted = 0;
+	for (i = 0; (pkginst = a_pkgList[i]) != NULL; i++) {
+		started = 0;
+
+		if (shall_we_continue(pkginst, npkgs) == B_FALSE) {
+			continue;
+		}
+
+		interrupted = 0;
+
+		/*
+		 * pkgrm invoked from within a non-global zone: remove
+		 * the package from the current zone only - no non-global
+		 * zones are possible.
+		 */
+
+		n = doRemove(a_nodelete, a_altBinDir, a_longestPkg,
+			admnfile, (char *)NULL, (zoneList_t)NULL);
+
+		/* set success/fail condition variables */
+
+		ckreturn(n);
+
+		npkgs--;
+	}
+
+	/*
+	 * all packages in the package list have been removed.
+	 * Continue with removal if:
+	 * -- immediate reboot is NOT required
+	 * -- there are more packages to remove
+	 * else return do NOT continue.
+	 */
+
+	if ((ireboot == 0) && (a_repeat != 0)) {
+		return (B_TRUE);
+	}
+
+	/* return 'dont continue' */
+
+	return (B_FALSE);
+}
+
+/*
+ * Name:	remove_packages_in_global_no_zones
+ * Description:	Remove packages from the global zone only when run in the
+ *		global zone and no non-global zones are installed.
+ * Arguments:	a_pkgList - pointer to array of strings, each string specifying
+ *			the name of one package to be removed.
+ *		a_nodelete: should the files and scripts remain installed?
+ *			- if != 0 pass -F flag to pkgremove - suppress
+ *			the removal of any files and any class action scripts
+ *			and suppress the running of any class action scripts.
+ *			The package files remain but the package looks like it
+ *			is not installed. This is mainly for use by upgrade.
+ *			- if == 0 do not pass -F flag to pkgremove - all
+ *			files and class action scripts are removed, and any
+ *			appropriate class action scripts are run.
+ *		a_longestPkg - length of the longest package "name" (for
+ *			output format alignment)
+ *		a_repeat - are there more packages avialable in "optind"
+ *			- B_TRUE - process packages from optind
+ *			- B_FALSE - do not process packages from optind
+ *		a_altBinDir - pointer to string representing location of the
+ *			pkgremove executable to run. If not NULL, then pass
+ *			the path specified to the -b option to pkgremove.
+ * Returns:	int	(see ckreturn() function for details)
+ *		0 - success
+ *		1 - package operation failed (fatal error)
+ *		2 - non-fatal error (warning)
+ *		3 - user selected quit (operation interrupted)
+ *		4 - admin settings prevented operation
+ *		5 - interaction required and -n (non-interactive) specified
+ *		"10" will be added to indicate "immediate reboot required"
+ *		"20" will be added to indicate "reboot after install required"
+ */
+
+static boolean_t
+remove_packages_in_global_no_zones(char **a_pkgList, int a_nodelete,
+	int a_longestPkg, int a_repeat, char *a_altBinDir)
+{
+	int	n;
+	int	i;
+
+	/* entry assertions */
+
+	assert(a_pkgList != (char **)NULL);
+	assert(a_longestPkg > 0);
+
+	/* entry debugging info */
+
+	echoDebug(DBG_PKGREMPKGSGZNNGZ_ENTRY);
+	echoDebug(DBG_PKGREMPKGSGZNNGZ_ARGS, a_nodelete, a_longestPkg,
+		a_repeat, PSTR(a_altBinDir));
+
+	/*
+	 * all of the packages (as listed in the package list) are
+	 * removed one at a time.
+	 */
+
+	interrupted = 0;
+	for (i = 0; (pkginst = a_pkgList[i]) != NULL; i++) {
+		started = 0;
+
+		if (shall_we_continue(pkginst, npkgs) == B_FALSE) {
+			continue;
+		}
+
+		interrupted = 0;
+
+		/*
+		 * pkgrm invoked from within the global zone and there are
+		 * NO non-global zones configured:
+		 * Remove the package from the global zone only.
+		 */
+
+		n = doRemove(a_nodelete, a_altBinDir, a_longestPkg,
+				admnfile, (char *)NULL, (zoneList_t)NULL);
+
+		/* set success/fail condition variables */
+
+		ckreturn(n);
+
+		npkgs--;
+	}
+
+	/*
+	 * all packages in the package list have been removed.
+	 * Continue with removal if:
+	 * -- immediate reboot is NOT required
+	 * -- there are more packages to remove
+	 * else return do NOT continue.
+	 */
+
+	if ((ireboot == 0) && (a_repeat != 0)) {
+		return (B_TRUE);
+	}
+
+	/* return 'dont continue' */
+
+	return (B_FALSE);
+}
+
+/*
+ * Name:	remove_packages_from_spool_directory
+ * Description:	Remove packages from a spool directory only.
+ * Arguments:	a_pkgList - pointer to array of strings, each string specifying
+ *			the name of one package to be removed.
+ *		a_nodelete: should the files and scripts remain installed?
+ *			- if != 0 pass -F flag to pkgremove - suppress
+ *			the removal of any files and any class action scripts
+ *			and suppress the running of any class action scripts.
+ *			The package files remain but the package looks like it
+ *			is not installed. This is mainly for use by upgrade.
+ *			- if == 0 do not pass -F flag to pkgremove - all
+ *			files and class action scripts are removed, and any
+ *			appropriate class action scripts are run.
+ *		a_longestPkg - length of the longest package "name" (for
+ *			output format alignment)
+ *		a_repeat - are there more packages avialable in "optind"
+ *			- B_TRUE - process packages from optind
+ *			- B_FALSE - do not process packages from optind
+ *		a_altBinDir - pointer to string representing location of the
+ *			pkgremove executable to run. If not NULL, then pass
+ *			the path specified to the -b option to pkgremove.
+ * Returns:	int	(see ckreturn() function for details)
+ *		0 - success
+ *		1 - package operation failed (fatal error)
+ *		2 - non-fatal error (warning)
+ *		3 - user selected quit (operation interrupted)
+ *		4 - admin settings prevented operation
+ *		5 - interaction required and -n (non-interactive) specified
+ *		"10" will be added to indicate "immediate reboot required"
+ *		"20" will be added to indicate "reboot after install required"
+ */
+
+static boolean_t
+remove_packages_from_spool_directory(char **a_pkgList, int a_nodelete,
+	int a_longestPkg, int a_repeat, char *a_altBinDir)
+{
+	int	n;
+	int	i;
+
+	/* entry assertions */
+
+	assert(a_pkgList != (char **)NULL);
+	assert(a_longestPkg > 0);
+
+	/*
+	 * all of the packages (as listed in the package list) are
+	 * removed one at a time.
+	 */
+
+	interrupted = 0;
+	for (i = 0; (pkginst = a_pkgList[i]) != NULL; i++) {
+		started = 0;
+
+		if (shall_we_continue(pkginst, npkgs) == B_FALSE) {
+			continue;
+		}
+
+		interrupted = 0;
+
+		/*
+		 * pkgrm invoked from any type of zone BUT the target
+		 * to be removed is a local spool directory: remove the
+		 * packages from the spool directory only.
+		 */
+
+		n = doRemove(a_nodelete, a_altBinDir, a_longestPkg,
+			admnfile, (char *)NULL, (zoneList_t)NULL);
+
+		/* set success/fail condition variables */
+
+		ckreturn(n);
+
+		npkgs--;
+	}
+
+	/*
+	 * all packages in the package list have been removed.
+	 * Continue with removal if:
+	 * -- immediate reboot is NOT required
+	 * -- there are more packages to remove
+	 * else return do NOT continue.
+	 */
+
+	if ((ireboot == 0) && (a_repeat != 0)) {
+		return (B_TRUE);
+	}
+
+	/* return 'dont continue' */
+
+	return (B_FALSE);
+}
+
+/*
+ * Name:	remove_packages
+ * Description:	Remove packages from the global zone, and optionally from one
+ *		or more non-global zones, or from a specified spool directory.
+ * Arguments:	a_pkgList - pointer to array of strings, each string specifying
+ *			the name of one package to be removed.
+ *		a_nodelete: should the files and scripts remain installed?
+ *			- if != 0 pass -F flag to pkgremove - suppress
+ *			the removal of any files and any class action scripts
+ *			and suppress the running of any class action scripts.
+ *			The package files remain but the package looks like it
+ *			is not installed. This is mainly for use by upgrade.
+ *			- if == 0 do not pass -F flag to pkgremove - all
+ *			files and class action scripts are removed, and any
+ *			appropriate class action scripts are run.
+ *		a_longestPkg - length of the longest package "name" (for
+ *			output format alignment)
+ *		a_repeat - are there more packages avialable in "optind"
+ *			- B_TRUE - process packages from optind
+ *			- B_FALSE - do not process packages from optind
+ *		a_altBinDir - pointer to string representing location of the
+ *			pkgremove executable to run. If not NULL, then pass
+ *			the path specified to the -b option to pkgremove.
+ *		a_pkgdir - pointer to string representing the directory
+ *			where the packages to be removed are located.
+ *		a_spoolDir - pointer to string specifying spool directory
+ *			to remove packages from. If != NULL then all zones
+ *			processing is bypassed and the packages are removed
+ *			from the specified spool directory only.
+ *		a_noZones - if non-global zones are configured, should the
+ *			packages be removed from the non-global zones?
+ *			- B_TRUE - do NOT remove packages from non-global zones
+ *			- B_FALSE - remove packages from non-global zones
+ * Returns:	int	(see ckreturn() function for details)
+ *		0 - success
+ *		1 - package operation failed (fatal error)
+ *		2 - non-fatal error (warning)
+ *		3 - user selected quit (operation interrupted)
+ *		4 - admin settings prevented operation
+ *		5 - interaction required and -n (non-interactive) specified
+ *		"10" will be added to indicate "immediate reboot required"
+ *		"20" will be added to indicate "reboot after install required"
+ */
+
+static boolean_t
+remove_packages(char **a_pkgList, int a_nodelete, int a_longestPkg,
+	int a_repeat, char *a_altBinDir, char *a_pkgdir, char *a_spoolDir,
+	boolean_t a_noZones)
+{
+	zoneList_t	zlst;
+	boolean_t	b;
+
+	/* entry assertions */
+
+	assert(a_pkgList != (char **)NULL);
+
+	echoDebug(DBG_REMOVEPKGS_ENTRY);
+	echoDebug(DBG_REMOVEPKGS_ARGS, npkgs, a_nodelete, a_longestPkg,
+		a_repeat, PSTR(a_pkgdir), PSTR(a_spoolDir));
+
+	/*
+	 * if removing from spool directory, bypass all zones checks
+	 */
+
+	if (a_spoolDir != (char *)NULL) {
+		/* in non-global zone */
+
+		echoDebug(DBG_REMOVE_PKGS_FROM_SPOOL, a_spoolDir);
+
+		b = remove_packages_from_spool_directory(a_pkgList, a_nodelete,
+			a_longestPkg, a_repeat, a_altBinDir);
+
+		return (B_FALSE);
+	}
+
+	/* exit if not root */
+
+	if (getuid()) {
+		progerr(ERR_NOT_ROOT, get_prog_name());
+		exit(1);
+	}
+
+	/*
+	 * if running in the global zone AND one or more non-global
+	 * zones exist, add packages in a 'zones aware' manner, else
+	 * add packages in the standard 'non-zones aware' manner.
+	 */
+
+	if ((a_noZones == B_FALSE) && (z_running_in_global_zone() == B_FALSE)) {
+		/* in non-global zone */
+
+		echoDebug(DBG_IN_LZ);
+
+		b = z_lock_this_zone(ZLOCKS_PKG_ADMIN);
+		if (b != B_TRUE) {
+			progerr(ERR_CANNOT_LOCK_THIS_ZONE);
+			/* set fatal error return condition */
+			ckreturn(1);
+			return (B_FALSE);
+		}
+
+		b = remove_packages_in_nonglobal_zone(a_pkgList, a_nodelete,
+			a_longestPkg, a_repeat, a_altBinDir, a_pkgdir);
+
+		(void) z_unlock_this_zone(ZLOCKS_ALL);
+
+		return (B_FALSE);
+	}
+
+	/* running in the global zone */
+
+	b = z_non_global_zones_exist();
+	if ((a_noZones == B_FALSE) && (b == B_TRUE)) {
+
+		echoDebug(DBG_IN_GZ_WITH_LZ);
+
+		/* get a list of all non-global zones */
+		zlst = z_get_nonglobal_zone_list();
+		if (zlst == (zoneList_t)NULL) {
+			progerr(ERR_CANNOT_GET_ZONE_LIST);
+			quit(1);
+		}
+
+		/* need to lock all of the zones */
+
+		quitSetZonelist(zlst);
+		b = z_lock_zones(zlst, ZLOCKS_PKG_ADMIN);
+		if (b == B_FALSE) {
+			z_free_zone_list(zlst);
+			progerr(ERR_CANNOT_LOCK_ZONES);
+			/* set fatal error return condition */
+			ckreturn(1);
+			return (B_FALSE);
+		}
+
+		/* add packages to all zones */
+
+		b = remove_packages_in_global_with_zones(a_pkgList, a_nodelete,
+			a_longestPkg, a_repeat, a_altBinDir, a_pkgdir, zlst);
+
+		/* unlock all zones */
+
+		(void) z_unlock_zones(zlst, ZLOCKS_ALL);
+		quitSetZonelist((zoneList_t)NULL);
+
+		/* free list of all non-global zones */
+
+		z_free_zone_list(zlst);
+
+		return (B_FALSE);
+	}
+
+	/* in global zone no non-global zones */
+
+	echoDebug(DBG_IN_GZ_NO_LZ);
+
+	b = z_lock_this_zone(ZLOCKS_PKG_ADMIN);
+	if (b != B_TRUE) {
+		progerr(ERR_CANNOT_LOCK_THIS_ZONE);
+		/* set fatal error return condition */
+		ckreturn(1);
+		return (B_FALSE);
+	}
+
+	b = remove_packages_in_global_no_zones(a_pkgList, a_nodelete,
+			a_longestPkg, a_repeat, a_altBinDir);
+
+	(void) z_unlock_this_zone(ZLOCKS_ALL);
+
+	return (B_FALSE);
+}
+
+/*
+ * Name:		path_valid
+ * Description:	Checks a string for being a valid path
+ *
+ * Arguments:	path - path to validate
+ *
+ * Returns :	B_TRUE - success, B_FALSE otherwise.
+ *		B_FALSE means path was null, too long (>PATH_MAX),
+ *		or too short (<1)
+ */
+static boolean_t
+path_valid(char *path)
+{
+	if (path == NULL) {
+		return (B_FALSE);
+	} else if (strlen(path) > PATH_MAX) {
+		return (B_FALSE);
+	} else if (strlen(path) >= 1) {
+		return (B_TRUE);
+	} else {
+		/* path < 1 */
+		return (B_FALSE);
+	}
+}
+
+/*
+ */
+
+static boolean_t
+check_packages(char **a_pkgList, char *a_packageDir)
+{
+	int	savenpkgs = npkgs;
+	int	i;
+	CAF_T	flags = 0;
+
+	/* set flags for applicability check */
+
+	if (z_running_in_global_zone() == B_TRUE) {
+		flags |= CAF_IN_GLOBAL_ZONE;
+	}
+
+	/*
+	 * for each package to remove, verify that the package is installed
+	 * and is removable.
+	 */
+
+	for (i = 0; (pkginst = a_pkgList[i]) != NULL; i++) {
+		/* check package applicability */
+		if (check_applicability(a_packageDir, pkginst, get_inst_root(),
+			flags) == B_FALSE) {
+			progerr(ERR_PKG_NOT_REMOVABLE, pkginst);
+			npkgs = savenpkgs;
+			return (B_FALSE);
+		}
+		npkgs--;
+	}
+
+	npkgs = savenpkgs;
+	return (B_TRUE);
+}
+
+/*
+ * - is this package removable from this zone?
+ * - does the scope of remove conflict with existing installation
+ */
+
+static boolean_t
+check_applicability(char *a_packageDir, char *a_pkgInst,
+	char *a_rootPath, CAF_T a_flags)
+{
+	FILE		*pkginfoFP;
+	boolean_t	all_zones;	/* pkg is "all zones" only */
+	char		pkginfoPath[PATH_MAX];
+	char		pkgpath[PATH_MAX];
+	int		len;
+
+	/* entry assertions */
+
+	assert(a_packageDir != (char *)NULL);
+	assert(*a_packageDir != '\0');
+	assert(a_pkgInst != (char *)NULL);
+	assert(*a_pkgInst != '\0');
+
+	/* normalize root path */
+
+	if (a_rootPath == (char *)NULL) {
+		a_rootPath = "";
+	}
+
+	/*
+	 * determine if this package is currently installed
+	 * if not installed return success - operation will fail
+	 * when the removal is attempted
+	 */
+
+	if (pkginfoIsPkgInstalled((struct pkginfo **)NULL, a_pkgInst) !=
+		B_TRUE) {
+		return (B_TRUE);
+	}
+
+	/*
+	 * calculate paths to various objects
+	 */
+
+	len = snprintf(pkgpath, sizeof (pkgpath), "%s/%s", a_packageDir,
+			a_pkgInst);
+	if (len > sizeof (pkgpath)) {
+		progerr(ERR_CREATE_PATH_2, a_packageDir, a_pkgInst);
+		return (B_FALSE);
+	}
+
+	/* if not installed then just return */
+
+	if (isdir(pkgpath) != 0) {
+		progerr(ERR_NO_PKGDIR, pkgpath, a_pkgInst, strerror(errno));
+		return (B_TRUE);
+	}
+
+	len = snprintf(pkginfoPath, sizeof (pkginfoPath),
+			"%s/pkginfo", pkgpath);
+	if (len > sizeof (pkgpath)) {
+		progerr(ERR_CREATE_PATH_2, pkgpath, "pkginfo");
+		return (B_FALSE);
+	}
+
+	/*
+	 * gather information from this packages pkginfo file
+	 */
+
+	pkginfoFP = fopen(pkginfoPath, "r");
+
+	if (pkginfoFP == (FILE *)NULL) {
+		progerr(ERR_NO_PKG_INFOFILE, a_pkgInst, pkginfoPath,
+							strerror(errno));
+		return (B_FALSE);
+	}
+
+	/* determine "ALLZONES" setting for this package */
+
+	all_zones = pkginfoParamTruth(pkginfoFP, PKG_ALLZONES_VARIABLE,
+			"true", B_FALSE);
+
+	/* close pkginfo file */
+
+	(void) fclose(pkginfoFP);
+
+	/* gather information from the global zone only file */
+
+	/*
+	 * verify package applicability based on information gathered;
+	 * the package IS currently installed....
+	 */
+
+	/* pkg ALLZONES=true & not running in global zone */
+
+	if ((all_zones == B_TRUE) && (!(a_flags & CAF_IN_GLOBAL_ZONE))) {
+		progerr(ERR_ALLZONES_AND_IN_LZ_PKGRM, a_pkgInst);
+		return (B_FALSE);
+	}
+
+	return (B_TRUE);
+}
+
+/*
+ * Name:	shall_we_continue
+ * Description: Called from within a loop that is installing packages,
+ *		this function examines various global variables and decides
+ *		whether or not to ask an appropriate question, and wait for
+ *		and appropriate reply.
+ * Arguments:	<<global variables>>
+ * Returns:	B_TRUE - continue processing with next package
+ *		B_FALSE - do not continue processing with next package
+ */
+
+static boolean_t
+shall_we_continue(char *a_pkgInst, int a_npkgs)
+{
+	char	ans[MAX_INPUT];
+	int	n;
+
+	/* return FALSE if immediate reboot required */
+
+	if (ireboot) {
+		ptext(stderr, MSG_SUSPEND_RM, a_pkgInst);
+		return (B_FALSE);
+	}
+
+	/* return TRUE if not interrupted */
+
+	if (!interrupted) {
+		return (B_TRUE);
+	}
+
+	/* output appropriate interrupt message */
+
+	echo(a_npkgs == 1 ? MSG_1MORETODO : MSG_MORETODO, a_npkgs);
+
+	/* if running with no interaction (-n) do not ask question */
+
+	if (nointeract) {
+		quit(0);
+		/* NOTREACHED */
+	}
+
+	/* interaction possible: ask question */
+
+	n = ckyorn(ans, NULL, NULL, NULL, ASK_CONTINUE_RM);
+	if (n != 0) {
+		quit(n);
+		/* NOTREACHED */
+	}
+
+	if (strchr("yY", *ans) == NULL) {
+		quit(0);
+		/* NOTREACHED */
+	}
+	return (B_TRUE);
+}
+
+/*
+ * Name:	create_zone_adminfile
+ * Description: Given a zone temporary directory and optionally an existing
+ *		administration file, generate an administration file that
+ *		can be used to perform "non-interactive" operations in a
+ *		non-global zone.
+ * Arguments:	r_zoneAdminFile - pointer to handle that will contain a
+ *			string representing the path to the temporary
+ *			administration file created - this must be NULL
+ *			before the first call to this function - on
+ *			subsequent calls if the pointer is NOT null then
+ *			the existing string will NOT be overwritten.
+ *		a_zoneTempDir - pointer to string representing the path
+ *			to the zone temporary directory to create the
+ *			temporary administration file in
+ *		a_admnfile - pointer to string representing the path to
+ *			an existing "user" administration file - the
+ *			administration file created will contain the
+ *			settings contained in this file, modified as
+ *			appropriate to supress any interaction;
+ *			If this is == NULL then the administration file
+ *			created will not contain any extra settings
+ * Returns:	void
+ * NOTE:	Any string returned is placed in new storage for the
+ *		calling method. The caller must use 'free' to dispose
+ *		of the storage once the string is no longer needed.
+ * NOTE:	On any error this function will call 'quit(1)'
+ */
+
+static void
+create_zone_adminfile(char **r_zoneAdminFile, char *a_zoneTempDir,
+	char *a_admnfile)
+{
+	boolean_t	b;
+
+	/* entry assertions */
+
+	assert(r_zoneAdminFile != (char **)NULL);
+	assert(a_zoneTempDir != (char *)NULL);
+	assert(*a_zoneTempDir != '\0');
+
+	/* entry debugging info */
+
+	echoDebug(DBG_CREATE_ZONE_ADMINFILE, a_zoneTempDir, PSTR(a_admnfile));
+
+	/* if temporary name already exists, do not overwrite */
+
+	if (*r_zoneAdminFile != (char *)NULL) {
+		return;
+	}
+
+	/* create temporary name */
+
+	*r_zoneAdminFile = tempnam(a_zoneTempDir, "zadmn");
+	b = z_create_zone_admin_file(*r_zoneAdminFile, a_admnfile);
+	if (b == B_FALSE) {
+		progerr(ERR_CREATE_TMPADMIN, *r_zoneAdminFile,
+			strerror(errno));
+		quit(1);
+		/* NOTREACHED */
+	}
+
+	echoDebug(DBG_CREATED_ZONE_ADMINFILE, *r_zoneAdminFile);
+}
+
+/*
+ * Name:	create_zone_tempdir
+ * Description: Given a system temporary directory, create a "zone" specific
+ *		temporary directory and return the path to the directory
+ *		created.
+ * Arguments:	r_zoneTempDir - pointer to handle that will contain a
+ *			string representing the path to the temporary
+ *			directory created - this must be NULL before the
+ *			first call to this function - on subsequent calls
+ *			if the pointer is NOT null then the existing string
+ *			will NOT be overwritten.
+ *		a_zoneTempDir - pointer to string representing the path
+ *			to the system temporary directory to create the
+ *			temporary zone directory in
+ * Returns:	void
+ * NOTE:	Any string returned is placed in new storage for the
+ *		calling method. The caller must use 'free' to dispose
+ *		of the storage once the string is no longer needed.
+ * NOTE:	On any error this function will call 'quit(1)'
+ * NOTE:	This function calls "quitSetZoneTmpdir" on success to
+ *		register the directory created with quit() so that the
+ *		directory will be automatically deleted on exit.
+ */
+
+static void
+create_zone_tempdir(char **r_zoneTempDir, char *a_tmpdir)
+{
+	boolean_t	b;
+
+	/* entry assertions */
+
+	assert(r_zoneTempDir != (char **)NULL);
+	assert(a_tmpdir != (char *)NULL);
+	assert(*a_tmpdir != '\0');
+
+	/* entry debugging info */
+
+	echoDebug(DBG_CREATE_ZONE_TEMPDIR, a_tmpdir);
+
+	/* if temporary directory already exists, do not overwrite */
+
+	if (*r_zoneTempDir != (char *)NULL) {
+		return;
+	}
+
+	/* create temporary directory */
+
+	b = setup_temporary_directory(r_zoneTempDir, a_tmpdir, "ztemp");
+	if (b == B_FALSE) {
+		progerr(ERR_ZONETEMPDIR, a_tmpdir, strerror(errno));
+		quit(1);
+		/* NOTREACHED */
+	}
+
+	/* register with quit() to directory is removed on exit */
+
+	quitSetZoneTmpdir(*r_zoneTempDir);
+
+	/* exit debugging info */
+
+	echoDebug(DBG_CREATED_ZONE_TEMPDIR, *r_zoneTempDir);
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/usr/src/cmd/svr4pkg/pkgrm/presvr4.c	Fri Jun 05 10:28:40 2009 -0400
@@ -0,0 +1,184 @@
+/*
+ * 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
+ * or http://www.opensolaris.org/os/licensing.
+ * 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 2004 Sun Microsystems, Inc.  All rights reserved.
+ * Use is subject to license terms.
+ */
+
+/* Copyright (c) 1984, 1986, 1987, 1988, 1989 AT&T */
+/* All Rights Reserved */
+
+
+#include <stdio.h>
+#include <signal.h>
+#include <limits.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <sys/stat.h>	/* chmod()? definition */
+#include <valtools.h>
+#include <locale.h>
+#include <libintl.h>
+#include <pkgdev.h>
+#include <pkglocs.h>
+#include "install.h"
+#include <pkglib.h>
+#include "libadm.h"
+#include "libinst.h"
+
+/*
+ * pkgadd local includes
+ */
+
+#include "quit.h"
+
+extern struct	admin adm;
+extern struct	pkgdev pkgdev;
+extern char	*tmpdir;
+extern int	started;
+
+static void	intf_reloc(void);
+
+#define	PATH_FLAGS	P_EXIST|P_ABSOLUTE|P_BLK
+
+#define	MSG_DEVICE	"Removal of a pre-SVR4 package requires the original " \
+			"medium from which the package was installed."
+
+#define	ASK_DEVICE	"Enter the alias or pathname for the device to be " \
+			"used (e.g., diskette1 or /dev/diskette)"
+
+#define	ASK_INSERT	 "Insert the first volume for package <%s> into %s"
+
+#define	ERR_NOCOPY	 "unable to create copy of UNINSTALL script in <%s>"
+
+#define	ERR_NOINT	"-n option cannot be used when removing pre-SVR4 " \
+			"packages"
+
+#define	ERR_BADDEV	"Unknown or bad device <%s> specified"
+
+#define	MSG_MAIL	"An attempt to remove the <%s> pre-SVR4 package on " \
+			"<%s> completed with exit status <%d>."
+
+#define	INFO_P4RMOK	"\nPre-SVR4 package reported successful removal.\n"
+
+int
+presvr4(char *pkg, int a_nointeract)
+{
+	char	alias[PATH_MAX];
+	char	path[PATH_MAX];
+	char	*tmpcmd;
+	int	n, retcode;
+	void	(*tmpfunc)();
+
+	echo(gettext("*** Removing Pre-SVR4 Package ***"));
+	if (a_nointeract != 0) {
+		progerr(gettext(ERR_NOINT));
+		quit(1);
+	}
+
+	/* should accept device alias?? */
+
+	echo(gettext(MSG_DEVICE));
+	for (;;) {
+		if (n = ckstr(alias, NULL, PATH_MAX, NULL, NULL, NULL,
+		    gettext(ASK_DEVICE)))
+			return (n);
+
+		if (devtype(alias, &pkgdev))
+			continue;
+		if (!pkgdev.mount || !pkgdev.bdevice) {
+			logerr(gettext(ERR_BADDEV), alias);
+			continue;
+		}
+		break;
+	}
+	pkgdev.mount = pkgdev.dirname = "/install";
+	pkgdev.rdonly = 1;
+
+	if (n = pkgmount(&pkgdev, pkg, 1, 0, 1))
+		quit(n);
+
+	psvr4pkg(&pkg);
+
+	/*
+	 * check to see if we can guess (via Rlist) what
+	 * pathnames this package is likely to remove;
+	 * if we can, check these against the 'contents'
+	 * file and warn the administrator that these
+	 * pathnames might be modified in some manner
+	 */
+	psvr4cnflct();
+
+	if (chdir(tmpdir)) {
+		progerr(gettext("unable to change directory to <%s>"), tmpdir);
+		quit(99);
+	}
+
+	(void) snprintf(path, sizeof (path), "%s/install/UNINSTALL",
+			"/install");
+	tmpcmd = tempnam(tmpdir, "UNINSTALL");
+	if (!tmpcmd || copyf(path, tmpcmd, 0) || chmod(tmpcmd, 0500)) {
+		progerr(gettext(ERR_NOCOPY), tmpdir);
+		quit(99);
+	}
+
+	started++;
+
+	echo(gettext("## Executing package UNINSTALL script"));
+
+	retcode = pkgexecl(NULL, NULL, NULL, NULL, SHELL, "-c", tmpcmd, NULL);
+
+	(void) unlink(tmpcmd);
+	if (retcode) {
+		echo(gettext("\nPre-SVR4 package reported failed removal.\n"));
+	} else {
+		echo(gettext(INFO_P4RMOK));
+	}
+
+	psvr4mail(adm.mail, gettext(MSG_MAIL), retcode, pkg);
+	(void) pkgumount(&pkgdev);
+
+	/* tell quit to call intf_reloc on exit */
+
+	quitSetIntfReloc(&intf_reloc);
+
+	return (retcode);
+}
+
+/*
+ * *****************************************************************************
+ * static internal (private) functions
+ * *****************************************************************************
+ */
+
+/*
+ * When quit() gains control this function will be invoked if quitSetIntfReloc()
+ * is called specifying this function - see presvr4() above for details.
+ */
+
+static void
+intf_reloc(void)
+{
+	char	path[PATH_MAX];
+
+	(void) snprintf(path, sizeof (path), "%s/intf_reloc", PKGBIN);
+	(void) pkgexecl(NULL, NULL, NULL, NULL, SHELL, "-c", path, NULL);
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/usr/src/cmd/svr4pkg/pkgrm/quit.c	Fri Jun 05 10:28:40 2009 -0400
@@ -0,0 +1,340 @@
+/*
+ * 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
+ * or http://www.opensolaris.org/os/licensing.
+ * 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 2009 Sun Microsystems, Inc.  All rights reserved.
+ * Use is subject to license terms.
+ */
+
+/* Copyright (c) 1984, 1986, 1987, 1988, 1989 AT&T */
+/* All Rights Reserved */
+
+
+#include <stdio.h>
+#include <signal.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <locale.h>
+#include <libintl.h>
+#include <pkgdev.h>
+#include <pkglib.h>
+#include <instzones_api.h>
+#include <libadm.h>
+#include <libinst.h>
+#include <messages.h>
+#include "quit.h"
+
+/*
+ * imported global variables
+ */
+
+/* imported from main.c */
+
+extern struct pkgdev pkgdev;	/* holds info about the installation device */
+
+extern int	npkgs;		/* the number of packages yet to be installed */
+extern int	admnflag;	/* != 0 if any pkgop admin setting failed (4) */
+extern int	doreboot;	/* != 0 if reboot required after installation */
+extern int	failflag;	/* != 0 if fatal error has occurred (1) */
+extern int	intrflag;	/* != 0 if user selected quit (3) */
+extern int	ireboot;	/* != 0 if immediate reboot required */
+extern int	nullflag;	/* != 0 if admin interaction required (5) */
+extern int	warnflag;	/* != 0 if non-fatal error has occurred (2) */
+
+/*
+ * forward declarations
+ */
+
+static ckreturnFunc_t	*ckreturnFunc = (ckreturnFunc_t *)NULL;
+static intfRelocFunc_t	*intfRelocFunc = (intfRelocFunc_t *)NULL;
+static char		*zoneTempDir = (char *)NULL;
+static void		trap(int signo);
+static zoneList_t	zoneList = (zoneList_t)NULL;
+static int		trapEntered = 0;
+
+/*
+ * exported functions
+ */
+
+void		quit(int retcode);
+void		quitSetCkreturnFunc(ckreturnFunc_t *a_ckreturnFunc);
+void		quitSetZoneName(char *a_zoneName);
+void		quitSetZoneTmpdir(char *z_zoneTempDir);
+void		quitSetZonelist(zoneList_t a_zlst);
+sighdlrFunc_t	*quitGetTrapHandler(void);
+
+/*
+ * *****************************************************************************
+ * global external (public) functions
+ * *****************************************************************************
+ */
+
+/*
+ * Name:	quitGetTrapHandler
+ * Description:	return address of this modules "signal trap" handler
+ * Arguments:	void
+ * Returns:	sighdlrFunc_t
+ *			The address of the trap handler that can be passed to
+ *			the signal() type system calls
+ */
+
+sighdlrFunc_t *
+quitGetTrapHandler()
+{
+	return (&trap);
+}
+
+/*
+ * Name:	quitSetIntfReloc
+ * Description:	set the "intf_reloc" interface to run when quit() is called
+ * Arguments:	a_intfReloc - pointer to function to call when quit() is called
+ * Returns:	void
+ * NOTE:	When quit() is called, if an "intf_reloc" function is set, quit
+ *		will call that function to perform whatever operations it needs
+ *		to perform - typically this is needed to run "intf_reloc" when
+ *		pre-SVR4 packages have been removed
+ */
+
+void
+quitSetIntfReloc(intfRelocFunc_t *a_intfReloc)
+{
+	intfRelocFunc = a_intfReloc;
+}
+
+/*
+ * Name:	quitSetCkreturnFunc
+ * Description:	set the ckreturn() interface to call when quit() is called
+ * Arguments:	a_ckreturnFunc - pointer to function to call when quit() is
+ *			called
+ * Returns:	void
+ * NOTE:	When quit() is called if a "ckreturnfunc" is set, then the first
+ *		action quit() takes is to call the "ckreturnfunc" specified with
+ *		the value passed to quit as the first argument. Quit will then
+ *		set the final return code to be used when exit() is called based
+ *		on the contents of these global variables:
+ *		 - admnflag - != 0 if any pkgop admin setting failed (4)
+ *		 - doreboot - != 0 if reboot required after installation
+ *		 - failflag - != 0 if fatal error has occurred (1)
+ *		 - intrflag - != 0 if user selected quit (3)
+ *		 - ireboot - != 0 if immediate reboot required
+ *		 - nullflag - != 0 if admin interaction required (5)
+ *		 - warnflag - != 0 if non-fatal error has occurred (2)
+ */
+
+void
+quitSetCkreturnFunc(ckreturnFunc_t *a_ckreturnFunc)
+{
+	ckreturnFunc = a_ckreturnFunc;
+}
+
+/*
+ * Name:	quitSetZonelist
+ * Description:	set the list of zones that are "locked" so that the zones can
+ *		be unlocked if quit() is called to exit
+ * Arguments:	a_zlst - list of zones that are "locked"
+ * Returns:	void
+ * NOTE:	When quit() is called, if this list is set, then z_unlock_zones
+ *		is called to unlock all of the zones in the list. If this list
+ *		is NOT set, then z_unlock_this_zone is called to unlock this
+ *		zone.
+ */
+
+void
+quitSetZonelist(zoneList_t a_zlst)
+{
+	zoneList = a_zlst;
+}
+
+/*
+ * Name:	quitSetZoneName
+ * Description:	set the zone name the program is running in
+ * Arguments:	a_zoneName - pointer to string representing the name of the zone
+ *			that the program is running in
+ * Returns:	void
+ */
+
+/* ARGSUSED */
+void
+quitSetZoneName(char *a_zoneName)
+{
+}
+
+/*
+ * Name:	quitSetZoneTmpdir
+ * Description:	set the path to the "zone temporary directory" in use
+ * Arguments:	a_zoneTempDir - pointer to string representing the full path to
+ *			the temporary directory used to hold files used during
+ *			zone operations
+ * Returns:	void
+ * NOTE:	If a zone temporary directory is set when quit() is called, the
+ *		directory is recursively removed before quit() calls exit
+ */
+
+void
+quitSetZoneTmpdir(char *a_zoneTempDir)
+{
+	zoneTempDir = a_zoneTempDir;
+}
+
+/*
+ * Name:	quit
+ * Description:	cleanup and exit
+ * Arguments:	a_retcode - the code to use to determine final exit status;
+ *			if this is NOT "99" and if a "ckreturnFunc" is
+ *			set, then that function is called with a_retcode
+ *			to set the final exit status.
+ *		Valid values are:
+ *		0 - success
+ *		1 - package operation failed (fatal error)
+ *		2 - non-fatal error (warning)
+ *		3 - user selected quit (operation interrupted)
+ *		4 - admin settings prevented operation
+ *		5 - interaction required and -n (non-interactive) specified
+ *		"10" is added to indicate "immediate reboot required"
+ *		"20" is be added to indicate "reboot after install required"
+ *		99 - do not interpret the code - just exit "99"
+ * Returns:	<<this function does not return - calls exit()>>
+ */
+
+void
+quit(int retcode)
+{
+	/* disable interrupts */
+
+	(void) signal(SIGINT, SIG_IGN);
+	(void) signal(SIGHUP, SIG_IGN);
+
+	if (!(restore_local_fs())) {
+		progerr(ERR_CANNOT_RESTORE_LOCAL_FS);
+	}
+
+	/* process return code if not quit(99) */
+
+	if (retcode != 99) {
+		if (ckreturnFunc != (ckreturnFunc_t *)NULL) {
+			(ckreturnFunc)(retcode);
+		}
+		if (failflag) {
+			retcode = 1;
+		} else if (warnflag) {
+			retcode = 2;
+		} else if (intrflag) {
+			retcode = 3;
+		} else if (admnflag) {
+			retcode = 4;
+		} else if (nullflag) {
+			retcode = 5;
+		} else {
+			retcode = 0;
+		}
+		if (ireboot) {
+			retcode += 20;
+		}
+		if (doreboot) {
+			retcode += 10;
+		}
+	}
+
+	if (doreboot || ireboot) {
+		ptext(stderr, gettext(MSG_REBOOT));
+	}
+
+	if (pkgdev.mount) {
+		(void) chdir("/");
+		(void) pkgumount(&pkgdev);
+	}
+
+	/* if set remove zone temporary directory */
+
+	if (zoneTempDir != (char *)NULL) {
+		echoDebug(DBG_REMOVING_ZONE_TMPDIR, zoneTempDir);
+		(void) rrmdir(zoneTempDir);
+		zoneTempDir = (char *)NULL;
+	}
+
+	/*
+	 * issue final exit message depending on number of packages left
+	 * to process
+	 */
+
+	if (npkgs == 1) {
+		echo(MSG_1_PKG_NOT_PROCESSED);
+	} else if (npkgs) {
+		echo(MSG_N_PKGS_NOT_PROCESSED, npkgs);
+	}
+
+	/* call intf_reloc function if registered */
+
+	if (intfRelocFunc != (intfRelocFunc_t *)NULL) {
+		(intfRelocFunc)();
+	}
+
+	/* if a zone list exists, unlock all zones */
+
+	if (zoneList != (zoneList_t)NULL) {
+		(void) z_unlock_zones(zoneList, ZLOCKS_ALL);
+	} else {
+		(void) z_unlock_this_zone(ZLOCKS_ALL);
+	}
+
+	/* final exit debugging message */
+
+	echoDebug(DBG_EXIT_WITH_CODE, retcode);
+
+	exit(retcode);
+	/* NOTREACHED */
+}
+
+/*
+ * *****************************************************************************
+ * static internal (private) functions
+ * *****************************************************************************
+ */
+
+/*
+ * Name:	trap
+ * Description:	signal handler connected via quitGetTrapHandler()
+ * Arguments:	signo - [RO, *RO] - (int)
+ *			Integer representing the signal that caused the trap
+ *			to this function to occur
+ * Returns:	<< NONE >>
+ * NOTE:	This function exits the program after doing mandatory cleanup.
+ * NOTE:	Even though quit() should NOT return, there is a call to _exit()
+ *		put after each call to quit() just in case quit() ever returned
+ *		by mistake.
+ */
+
+static void
+trap(int signo)
+{
+	/* prevent reentrance */
+
+	if (trapEntered++ != 0) {
+		return;
+	}
+
+	if ((signo == SIGINT) || (signo == SIGHUP)) {
+		quit(3);
+		_exit(3);
+	}
+	quit(1);
+	_exit(1);
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/usr/src/cmd/svr4pkg/pkgrm/quit.h	Fri Jun 05 10:28:40 2009 -0400
@@ -0,0 +1,65 @@
+/*
+ * 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
+ * or http://www.opensolaris.org/os/licensing.
+ * 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 2004 Sun Microsystems, Inc.  All rights reserved.
+ * Use is subject to license terms.
+ */
+
+/*
+ * Header:	pkgrm: quit.c
+ *
+ * Function:	external definitions for references to the quit.c module
+ *
+ */
+
+#ifndef	__PKGRM_QUIT_H__
+#define	__PKGRM_QUIT_H__
+
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/*
+ * required include files
+ */
+
+#include "libinst.h"
+
+/*
+ * exported (global) functions
+ */
+
+typedef void (intfRelocFunc_t)(void);
+
+extern void	quit(int retcode);
+extern void	quitSetCkreturnFunc(ckreturnFunc_t *a_ckreturnFunc);
+extern void	quitSetZoneName(char *a_zoneName);
+extern void	quitSetZoneTmpdir(char *z_zoneTempDir);
+extern sighdlrFunc_t *quitGetTrapHandler(void);
+extern void	quitSetIntfReloc(intfRelocFunc_t *a_intfReloc);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif	/* __PKGRM_QUIT_H__ */
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/usr/src/cmd/svr4pkg/pkgscripts/Makefile	Fri Jun 05 10:28:40 2009 -0400
@@ -0,0 +1,54 @@
+#
+# 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
+# or http://www.opensolaris.org/os/licensing.
+# 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 2009 Sun Microsystems, Inc.  All rights reserved.
+# Use is subject to license terms.
+#
+
+PROG=		cmdexec
+
+OBJS=		cmdexec.o
+SRCS=           $(OBJS:.o=.c)
+
+CLASS_ACTION_SCRIPTS =  i.awk		\
+			i.build		\
+			i.CompCpio	\
+			i.sed		\
+			r.awk		\
+			r.build		\
+			r.sed
+ADMINFILE =		default
+
+# cmdexec also installed in usr/sadm/install/scripts
+SCRIPTS = $(CLASS_ACTION_SCRIPTS) $(PROG)
+
+include $(SRC)/cmd/svr4pkg/Makefile.svr4pkg
+CLOBBERFILES += $(CLASS_ACTION_SCRIPTS)
+
+LDLIBS  +=      -lpkg
+
+.KEEP_STATE:
+all:	$(PROG) $(CLASS_ACTION_SCRIPTS)
+
+install: all $(ROOTCLASS_SCR_FILES) $(ROOTADMIN_SRC_FILE)
+
+include $(SRC)/cmd/svr4pkg/Makefile.svr4pkg.targ
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/usr/src/cmd/svr4pkg/pkgscripts/cmdexec.c	Fri Jun 05 10:28:40 2009 -0400
@@ -0,0 +1,155 @@
+/*
+ * 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
+ * or http://www.opensolaris.org/os/licensing.
+ * 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 2009 Sun Microsystems, Inc.  All rights reserved.
+ * Use is subject to license terms.
+ */
+
+/* Copyright (c) 1984, 1986, 1987, 1988, 1989 AT&T */
+/* All Rights Reserved */
+
+
+#include <stdio.h>
+#include <ctype.h>
+#include <string.h>
+#include <locale.h>
+#include <libintl.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <pkglib.h>
+
+#define	COMMAND '!'
+#define	LSIZE 256
+
+#define	ERR_NOTROOT	"You must be \"root\" for %s to execute properly."
+
+static void	usage(void);
+static int	docmd(char *cmd, char *file, char *input);
+
+int
+main(int argc, char *argv[])
+{
+	FILE	*fpout, *fp;
+	char	line[LSIZE],
+		*pt,
+		*keyword, 	/* keyword = install || remove */
+		*input, 	/* sed input file */
+		*cmd,
+		*srcfile, 	/* sed data file */
+		*destfile; 	/* target file to be updated */
+	int	flag;
+	char	*prog;
+
+	(void) setlocale(LC_ALL, "");
+
+#if !defined(TEXT_DOMAIN)	/* Should be defined by cc -D */
+#define	TEXT_DOMAIN "SYS_TEST"
+#endif
+	(void) textdomain(TEXT_DOMAIN);
+
+	prog = set_prog_name(argv[0]);
+
+	if (getuid()) {
+		progerr(gettext(ERR_NOTROOT), prog);
+		exit(1);
+	}
+
+	if (argc != 5)
+		usage();
+
+	cmd = argv[1];
+	keyword = argv[2];
+	srcfile = argv[3];
+	destfile = argv[4];
+
+	srcfile = argv[3];
+	if ((fp = fopen(srcfile, "r")) == NULL) {
+		progerr(gettext("unable to open %s"), srcfile);
+		exit(1);
+	}
+
+	input = tempnam(NULL, "sedinp");
+	if ((fpout = fopen(input, "w")) == NULL) {
+		progerr(gettext("unable to open %s"), input);
+		exit(2);
+	}
+
+	flag = (-1);
+	while (fgets(line, LSIZE, fp)) {
+		for (pt = line; isspace(*pt); /* void */)
+			++pt;
+		if (*pt == '#')
+			continue;
+		if (*pt == COMMAND) {
+			if (flag > 0)
+				break; /* no more lines to read */
+			pt = strtok(pt+1, " \t\n");
+			if (!pt) {
+				progerr(gettext("null token after '!'"));
+				exit(1);
+			}
+			flag = (strcmp(pt, keyword) ? 0 : 1);
+		} else if (flag == 1) { /* bug # 1083359 */
+			(void) fputs(line, fpout);
+		}
+	}
+	(void) fclose(fpout);
+	if (flag > 0) {
+		if (docmd(cmd, destfile, input)) {
+			progerr(gettext("command failed <%s>"), cmd);
+			exit(1);
+		}
+	}
+	(void) unlink(input);
+	return (0);
+}
+
+static int
+docmd(char *cmd, char *file, char *input)
+{
+	char *tempout;
+	char command[256];
+
+	tempout = tempnam(NULL, "temp1");
+	if (!tempout)
+		return (-1);
+
+	(void) sprintf(command, "%s -f %s <%s >%s", cmd, input, file, tempout);
+	if (system(command))
+		return (-1);
+
+	(void) sprintf(command, "cp %s %s", tempout, file);
+	if (system(command))
+		return (-1);
+
+	(void) unlink(tempout);
+	free(tempout);
+	return (0);
+}
+
+static void
+usage(void)
+{
+	(void) fprintf(stderr, gettext("usage: %s cmd keyword src dest\n"),
+	    get_prog_name());
+	exit(2);
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/usr/src/cmd/svr4pkg/pkgscripts/default	Fri Jun 05 10:28:40 2009 -0400
@@ -0,0 +1,41 @@
+#
+# 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
+# or http://www.opensolaris.org/os/licensing.
+# 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 2004 Sun Microsystems, Inc.  All rights reserved.
+# Use is subject to license terms.
+#
+mail=
+instance=unique
+partial=ask
+runlevel=ask
+idepend=ask
+rdepend=ask
+space=ask
+setuid=ask
+conflict=ask
+action=ask
+networktimeout=60
+networkretries=3
+authentication=quit
+keystore=/var/sadm/security
+proxy=
+basedir=default
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/usr/src/cmd/svr4pkg/pkgscripts/i.CompCpio.sh	Fri Jun 05 10:28:40 2009 -0400
@@ -0,0 +1,538 @@
+#!/bin/sh
+#
+# 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
+# or http://www.opensolaris.org/os/licensing.
+# 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 2005 Sun Microsystems, Inc.  All rights reserved.
+# Use is subject to license terms.
+#
+
+# i.CompCpio 
+#
+# This shell script uncompresses and installs files archived in
+# old-style WOS packages using the utilities cpio and compress. It
+# looks in the PKGSRC directory for the archives which may be called
+# out in one of eight ways :
+#
+#	reloc.cpio.Z	relocatable paths, less old style
+#	root.cpio.Z	absolute paths, less old style
+#	reloc.cpio	relocatable paths less old style, not compressed
+#	root.cpio	absolute paths, less old style, not compressed
+#	reloc.Z		relocatable paths, old style, compressed
+#	root.Z		absolute paths, old style, compressed
+#	reloc		relocatable paths, old style, not compressed
+#	root		absolute paths, old style, not compressed
+#
+# stdin carries the source directory as the first entry followed by the
+# paths of the files to be installed as indicated in the pkgmap. Since
+# all operations take place from the declared base directory, both relative
+# and absolute paths will install correctly. There are three methods and
+# since speed is of the essence, we skip straight to the right one :
+#
+#	If it's an initial install
+#		do a full cpio for each archive
+#	else
+#		If there's only the reloc archive
+#			make a file list, rm executables, do a selective cpio
+#		else
+#			rm executables, do a full cpio for each archive
+#
+# Since the old-style archives contain no execute permissions, this
+# script saves the executables it requires so it can clean up after
+# unloading the archive. If /usr/lib/ld.so or .so.1 is included in the
+# package, no cleanup will be possible (nothing will run) so we clean
+# up first and then unload the entire archive without a file list.
+#
+NAME="i.CompCpio"
+FILELIST=${PKGSAV:?undefined}/filelist
+BD=${BASEDIR:-/}
+IR=${PKG_INSTALL_ROOT:-/}
+MAXLIST=550	# This is arbitrary based upon 2.4 cpio
+count=0
+
+reloc_cpio_Z=0
+root_cpio_Z=0
+reloc_cpio=0
+root_cpio=0
+Reloc_Arch=""
+Root_Arch=""
+is_an_archive=0
+is_a_filelist=0
+mk_filelist=0
+list_empty=1
+local_install=0
+Spcl_init=0
+Rm_alt_sav=0
+
+# critical archived dynamic libraries and executables
+Spcl_lib=0
+Spcl_exec=0
+Movelist=""
+Ld_Preload=""
+Ld1=usr/lib/ld.so.1
+Ld=usr/lib/ld.so
+Libintl=usr/lib/libintl.so.1
+Libmalloc=usr/lib/libmapmalloc.so.1
+Libc=usr/lib/libc.so.1	
+Libw=usr/lib/libw.so.1
+Libdl=usr/lib/libdl.so.1
+Cpio=usr/bin/cpio
+Rm=usr/bin/rm
+Ln=usr/bin/ln
+Mv=usr/bin/mv
+Nawk=usr/bin/nawk
+Zcat=usr/bin/zcat
+
+# Set up the default paths
+MV_xpath=/usr/bin
+MV_cmd=$MV_xpath/mv
+CPIO_xpath=/usr/bin
+CPIO_cmd=$CPIO_xpath/cpio
+ZCAT_xpath=/usr/bin
+ZCAT_cmd=$ZCAT_xpath/zcat
+LN_xpath=/usr/bin
+LN_cmd=$LN_xpath/ln
+NAWK_xpath=/usr/bin
+NAWK_cmd=$NAWK_xpath/nawk
+RM_xpath=/usr/bin
+RM_cmd=$RM_xpath/rm
+Tmp_xpath=/usr/tmp$$dir
+Tmp_Creat=0
+rm_cpio=0
+rm_ln=0
+rm_zcat=0
+rm_nawk=0
+rm_rm=0
+rm_mv=0
+no_select=0
+
+# Functions
+
+#
+# This creates the temporary directory for holding the old dynamic
+# libraries and executables.
+#
+mktempdir() {
+	if [ ! -d $Tmp_xpath ]; then
+		mkdir $Tmp_xpath
+		if [ $? -ne 0 ]; then
+			echo `gettext "ERROR : $NAME cannot create $Tmp_xpath."`
+			exit 1
+		fi
+	fi
+	Tmp_Creat=1
+}
+
+#
+# Test a path to see if it represents a dynamic library or executable that
+# we use in this script. If it is, deal with the special case.
+#
+spclcase() {	# $1 is the pathname to special case
+	if [ $local_install -eq 1 ]; then
+		case $1 in
+			$Ld)		no_select=1;;
+			$Ld1)		no_select=1;;
+			$Libintl)	Spcl_lib=1; file=libintl.so.1;;
+			$Libmalloc)	Spcl_lib=1; file=libmapmalloc.so.1;;
+			$Libc)		Spcl_lib=1; file=libc.so.1;;
+			$Libw)		Spcl_lib=1; file=libw.so.1;;
+			$Libdl)		Spcl_lib=1; file=libdl.so.1;;
+			$Cpio)		rm_cpio=1; Spcl_exec=1;;
+			$Ln)		rm_ln=1; Spcl_exec=1;;
+			$Zcat)		rm_zcat=1; Spcl_exec=1;;
+			$Nawk)		rm_nawk=1; Spcl_exec=1;;
+			$Rm)		rm_rm=1; Spcl_exec=1;;
+			$Mv)		rm_mv=1; Spcl_exec=1;;
+		esac
+
+		if [ $no_select -eq 1 ]; then
+			is_a_filelist=0
+			list_empty=1
+			$RM_cmd $FILELIST
+			if [ $Rm_alt_sav -eq 1 ]; then
+				$RM_cmd -r $PKGSAV
+				Rm_alt_sav=0
+			fi
+			exec_clean 1
+			return 1
+		elif [ $Spcl_lib -eq 1 ]; then
+			if [ $Tmp_Creat -eq 0 ]; then
+				mktempdir
+			fi
+
+			if [ $Spcl_init -eq 0 ]; then
+				Org_LD_LIBRARY_PATH=${LD_LIBRARY_PATH}
+				LD_LIBRARY_PATH="$Org_LD_LIBRARY_PATH $Tmp_xpath"
+				export LD_LIBRARY_PATH
+				Spcl_init=1
+			fi
+			Ld_Preload="$Ld_Preload $Tmp_xpath/$file"
+			LD_PRELOAD=$Ld_Preload
+			export LD_PRELOAD
+			Movelist="$1 $file $Movelist"
+			$MV_cmd $1 $Tmp_xpath
+			$LN_cmd -s ../..$Tmp_xpath/$file $1
+			Spcl_lib=0
+		elif [ $Spcl_exec -eq 1 ]; then
+			if [ $Tmp_Creat -eq 0 ]; then
+				mktempdir
+			fi
+
+			$MV_cmd $1 $Tmp_xpath
+			if [ $rm_cpio -eq 1 ]; then
+				$LN_cmd -s ../..$Tmp_xpath/cpio $1
+				CPIO_cmd="$Tmp_xpath/cpio"
+				Movelist="$1 cpio $Movelist"
+				rm_cpio=0
+			elif [ $rm_ln -eq 1 ]; then
+				$Tmp_xpath/ln -s ../..$Tmp_xpath/ln $1
+				LN_cmd="$Tmp_xpath/ln"
+				Movelist="$1 ln $Movelist"
+				rm_ln=0
+			elif [ $rm_nawk -eq 1 ]; then
+				$LN_cmd -s ../..$Tmp_xpath/nawk $1
+				NAWK_cmd="$Tmp_xpath/nawk"
+				Movelist="$1 nawk $Movelist"
+				rm_nawk=0
+			elif [ $rm_zcat -eq 1 ]; then
+				$LN_cmd -s ../..$Tmp_xpath/zcat $1
+				ZCAT_cmd="$Tmp_xpath/zcat"
+				Movelist="$1 zcat $Movelist"
+				rm_zcat=0
+			elif [ $rm_rm -eq 1 ]; then
+				$LN_cmd -s ../..$Tmp_xpath/rm $1
+				RM_cmd="$Tmp_xpath/rm"
+				Movelist="$Movelist $1 rm"
+				rm_rm=0
+			elif [ $rm_mv -eq 1 ]; then
+				$LN_cmd -s ../..$Tmp_xpath/mv $1
+				MV_cmd="$Tmp_xpath/mv"
+				Movelist="$Movelist $1 mv"
+				rm_mv=0
+			fi
+			Spcl_exec=0
+		fi
+	fi
+
+	return 0
+}
+
+#
+# Clean up the libraries and executables that were moved.
+#
+exec_clean() {	# $1 =1 means be quiet
+	if [ ! -z "${Movelist}" ]; then
+		echo $Movelist | $NAWK_cmd '
+			{ split ($0, line)
+			for (n=1; n <= NF; n++) {
+				print line[n]
+			}
+		}' | while read path; do
+			read file
+			if [ -h $path ]; then	# If it's our slink
+				# then put the original back
+				if [ $1 -eq 0 ]; then
+					echo `gettext "WARNING : $path not found in archive."`
+				fi
+				$MV_cmd $Tmp_xpath/$file $path
+			else	# if the archive put something down
+				# remove the temporary copy
+				$RM_cmd $Tmp_xpath/$file
+			fi
+		done
+		for path in $Movelist; do
+			if [ -x $path ]; then
+				case $path in
+					$Cpio)	CPIO_cmd="$CPIO_xpath/cpio";;
+					$Ln)	LN_cmd="$LN_xpath/ln";;
+					$Zcat)	ZCAT_cmd="$ZCAT_xpath/zcat";;
+					$Nawk)	NAWK_cmd="$NAWK_xpath/nawk";;
+					$Rm)	RM_cmd="$RM_xpath/rm";;
+					$Mv)	MV_cmd="$MV_xpath/mv";;
+				esac
+			fi
+		done
+		Movelist=""
+
+		if [ $Tmp_Creat -eq 1 ]; then
+			$RM_cmd -r $Tmp_xpath
+			Tmp_Creat=0
+		fi
+	fi
+}
+
+#
+# Figure out what kind of package this is
+#
+eval_pkg() {
+
+	# Any archive, whether compressed or not needs to be handled
+	# the same. i.e. reloc.cpio.Z and root.cpio.Z should cause
+	# the global is_an_archive to be set to 1.
+
+	read path
+	if [ ${path:-NULL} != NULL ]; then # get the package source directory
+		PKGSRC=${path:?undefined}
+
+		if [ ${PKG_INSTALL_ROOT:-/} = "/" ]; then
+			local_install=1
+		fi
+
+		if [ -r $PKGSRC/reloc.cpio.Z ]; then
+			reloc_cpio_Z=1
+			Reloc_Arch=$PKGSRC/reloc.cpio.Z
+			is_an_archive=1
+		fi
+
+		if [ -r $PKGSRC/root.cpio.Z ]; then
+			root_cpio_Z=1
+			Root_Arch=$PKGSRC/root.cpio.Z
+			is_an_archive=1
+		fi
+
+		if [ -r $PKGSRC/reloc.cpio ]; then
+			reloc_cpio=1
+			Reloc_Arch=$PKGSRC/reloc.cpio
+			is_an_archive=1
+		fi
+
+		if [ -r $PKGSRC/root.cpio ]; then
+			root_cpio=1
+			Root_Arch=$PKGSRC/root.cpio
+			is_an_archive=1
+		fi
+
+		if [ -r $PKGSRC/reloc.Z ]; then
+			reloc_cpio_Z=1
+			Reloc_Arch=$PKGSRC/reloc.Z
+			is_an_archive=2
+		fi
+
+		if [ -r $PKGSRC/root.Z ]; then
+			root_cpio_Z=1
+			Root_Arch=$PKGSRC/root.Z
+			is_an_archive=2
+		fi
+
+		if [ -f $PKGSRC/reloc ]; then
+			reloc_cpio=1
+			Reloc_Arch=$PKGSRC/reloc
+			is_an_archive=2
+		fi
+
+		if [ -f $PKGSRC/root ]; then
+			root_cpio=1
+			Root_Arch=$PKGSRC/root
+			is_an_archive=2
+		fi
+	else
+		exit 0	# empty pipe, we're done
+	fi
+}
+
+#
+# main
+#
+
+eval_pkg
+
+if [ $BD = "/" ]; then
+	Client_BD=""
+else
+	Client_BD=`echo $BD | sed s@/@@`
+fi
+
+if [ $is_an_archive -eq 0 ]; then
+	echo `gettext "ERROR : $NAME cannot find archived files in $PKGSRC."`
+	exit 1
+fi
+
+if [ ! -d $PKGSAV ]; then
+	echo `gettext "WARNING : $NAME cannot find save directory $PKGSAV."`
+	PKGSAV=/tmp/$PKG.sav
+
+	if [ ! -d $PKGSAV ]; then
+		/usr/bin/mkdir $PKGSAV
+	fi
+
+	if [ $? -eq 0 ]; then
+		echo `gettext "  Using alternate save directory" $PKGSAV`
+		FILELIST=$PKGSAV/filelist
+		Rm_alt_sav=1
+	else
+		echo `gettext "ERROR : cannot create alternate save directory"` $PKGSAV
+		exit 1
+	fi
+fi
+
+if [ -f $FILELIST ]; then
+	rm $FILELIST
+fi
+
+cd $BD
+
+# If there's one old-style archive and it is relocatable and this is
+# not an initial install then make a file list for extraction.
+if [ $is_an_archive -eq 1 -a ${PKG_INIT_INSTALL:-null} = null ]; then
+	mk_filelist=1
+fi
+
+# If this is not an initial install then clear out potentially executing
+# files and libraries for cpio and create an extraction list if necessary
+if [ ${PKG_INIT_INSTALL:-null} = null ]; then
+	if [ $local_install -eq 1 ]; then
+		# If extraction list is desired, create it
+		if [ $mk_filelist -eq 1 ]; then
+			is_a_filelist=1
+			while	read path
+			do
+				echo $path >> $FILELIST
+				list_empty=0
+				if [ -x ${path:-NULL} ]; then
+					full_path=`echo $Client_BD/$path | sed s@//@/@g`
+					spclcase $full_path
+					if [ $? -eq 1 ]; then
+						break
+					fi
+				fi
+			done
+
+			# If there's a path containing a '$' then we can't
+			# use the extraction list because of the shell
+			if [ $list_empty -eq 0 ]; then
+				s=`LD_PRELOAD="$Ld_Preload" $NAWK_cmd ' /\\$/ { print } ' $FILELIST`
+
+				if [ ! -z "${s}" ]; then
+					is_a_filelist=0
+				fi
+			fi
+		else	# No extraction list is desired
+			while	read  path
+			do
+				if [ -x ${path:-NULL} ]; then
+					full_path=`echo $Client_BD/$path | sed s@//@/@g`
+					spclcase $full_path
+					if [ $? -eq 1 ]; then
+						break
+					fi
+				fi
+			done
+		fi	# $mk_filelist -eq 1
+	else	# ! ($local_install -eq 1)
+		# If extraction list is desired, create it
+		if [ $mk_filelist -eq 1 ]; then
+			is_a_filelist=1
+			while	read path
+			do
+				echo $path >> $FILELIST
+				list_empty=0
+			done
+
+			# If there's a path containing a '$' then we can't
+			# use the extraction list because of the shell
+			if [ $list_empty -eq 0 ]; then
+				s=`LD_PRELOAD="$Ld_Preload" $NAWK_cmd ' /\\$/ { print } ' $FILELIST`
+
+				if [ ! -z "${s}" ]; then
+					is_a_filelist=0
+				fi
+			fi
+		fi	# $mk_filelist -eq 1
+	fi	# $local_install -eq 1
+fi	# ${PKG_INIT_INSTALL:-null} = null
+
+# Now extract the data from the archive(s)
+# extract compressed cpio relocatable archive
+if [ $reloc_cpio_Z -eq 1 ]; then
+	cd $BD
+	if [ $is_a_filelist -eq 1 ]; then
+		if [ $list_empty -eq 0 ]; then
+			$ZCAT_cmd $Reloc_Arch | $CPIO_cmd -idukm -E $FILELIST
+			if [ $? -ne 0 ]; then
+				echo `gettext "cpio of $Reloc_Arch failed with error $?."`
+				exit 1
+		   	 fi
+
+		fi
+	else
+		$ZCAT_cmd $Reloc_Arch | $CPIO_cmd -idukm
+	fi
+fi
+
+# extract compressed cpio absolute archive
+if [ $root_cpio_Z -eq 1 ]; then
+	cd $IR
+	$ZCAT_cmd $Root_Arch | $CPIO_cmd -idukm
+		if [ $? -ne 0 ]; then
+			echo `gettext "cpio of $Root_Arch failed with error $?."`
+			exit 1
+		fi
+fi
+
+# extract cpio relocatable archive
+if [ $reloc_cpio -eq 1 ]; then
+	cd $BD
+	if [ $is_a_filelist -eq 1 ]; then
+		if [ $list_empty -eq 0 ]; then
+			$CPIO_cmd -idukm -I $Reloc_Arch -E $FILELIST
+
+			if [ $? -ne 0 ]; then
+				echo `gettext "cpio of $Reloc_Arch failed with error $?."`
+				exit 1
+			fi
+		fi
+	else
+		$CPIO_cmd -idukm -I $Reloc_Arch
+	fi
+fi
+
+# extract cpio absolute archive
+if [ $root_cpio -eq 1 ]; then
+	cd $IR
+	$CPIO_cmd -idukm -I $Root_Arch
+		if [ $? -ne 0 ]; then
+			echo `gettext "cpio of $Root_Arch failed with error $?."`
+			exit 1
+		fi
+fi
+
+if [ -f $FILELIST ]; then
+	$RM_cmd $FILELIST
+fi
+
+if [ $Rm_alt_sav -eq 1 ]; then
+	$RM_cmd -r $PKGSAV
+	Rm_alt_sav=0
+fi
+
+exec_clean 0
+
+if [ $Tmp_Creat -eq 1 ]; then
+	$RM_cmd -r $Tmp_xpath
+fi
+
+if [ $Spcl_init -eq 1 ]; then
+	LD_LIBRARY_PATH=$Org_LD_LIBRARY_PATH
+	export LD_LIBRARY_PATH
+	Spcl_init=0
+fi
+
+exit 0
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/usr/src/cmd/svr4pkg/pkgscripts/i.awk.sh	Fri Jun 05 10:28:40 2009 -0400
@@ -0,0 +1,70 @@
+#!/bin/sh
+#
+# 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
+# or http://www.opensolaris.org/os/licensing.
+# 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 2002 Sun Microsystems, Inc.  All rights reserved.
+# Use is subject to license terms.
+#
+
+error=no
+while read src dest
+do
+	[ "$src" = /dev/null ] && continue
+
+	echo "Modifying $dest"
+
+	# Strip PKG_INSTALL_ROOT from dest if installation is to an
+	# alternate root.
+
+	if [ -n "$PKG_INSTALL_ROOT" -a "$PKG_INSTALL_ROOT" != "/" ]; then
+		client_dest=`echo $dest | \
+			/usr/bin/nawk -v rootdir="$PKG_INSTALL_ROOT" '{
+				{ print substr($0, length(rootdir)+1)} }'`
+		savepath=$PKGSAV/awk${client_dest}
+	else
+		savepath=$PKGSAV/awk${dest}
+	fi
+
+	dirname=`dirname $savepath`
+	if [ $? -ne 0 ]
+	then
+		error=yes
+		continue
+	fi
+	if [ ! -d $dirname ]
+	then
+		# ignore return code since mkdir has bug
+		mkdir -p $dirname
+	fi
+
+	cp $src $savepath &&
+	/usr/sadm/install/scripts/cmdexec /usr/bin/awk install $savepath $dest
+
+	if [ $? -ne 0 ]
+	then
+		error=yes
+		continue
+	fi
+done
+[ "$error" = yes ] &&
+	exit 2
+exit 0
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/usr/src/cmd/svr4pkg/pkgscripts/i.build.sh	Fri Jun 05 10:28:40 2009 -0400
@@ -0,0 +1,81 @@
+#!/bin/sh
+#
+# 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
+# or http://www.opensolaris.org/os/licensing.
+# 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 2002 Sun Microsystems, Inc.  All rights reserved.
+# Use is subject to license terms.
+#
+
+error=no
+while read src dest
+do
+	[ "$src" = /dev/null ] && continue
+
+	echo "Modifying $dest"
+
+	# Strip PKG_INSTALL_ROOT from dest if installation is to an
+	# alternate root.
+
+	if [ -n "$PKG_INSTALL_ROOT" -a "$PKG_INSTALL_ROOT" != "/" ]; then
+		client_dest=`echo $dest | \
+			/usr/bin/nawk -v rootdir="$PKG_INSTALL_ROOT" '{
+				{ print substr($0, length(rootdir)+1)} }'`
+		savepath=$PKGSAV/build${client_dest}
+	else
+		savepath=$PKGSAV/build${dest}
+	fi
+
+	dirname=`dirname $savepath`
+	if [ $? -ne 0 ]
+	then
+		error=yes
+		continue
+	fi
+
+	if [ ! -d $dirname ]
+	then
+		# ignore return since mkdir has bug
+		mkdir -p $dirname
+	fi
+
+	cp $src $savepath &&
+		chmod 500 $savepath
+	if [ $? -ne 0 ]
+	then
+		error=yes
+		continue
+	fi
+
+	if $savepath install > /tmp/$$build
+	then
+		if [ -s /tmp/$$build ]
+		then
+			cp /tmp/$$build $dest || error=yes
+		fi
+	else
+		error=yes
+	fi
+	rm -f /tmp/$$build
+done
+[ "$error" = yes ] &&
+	exit 2
+exit 0
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/usr/src/cmd/svr4pkg/pkgscripts/i.sed.sh	Fri Jun 05 10:28:40 2009 -0400
@@ -0,0 +1,70 @@
+#!/bin/sh
+#
+# 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
+# or http://www.opensolaris.org/os/licensing.
+# 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 2002 Sun Microsystems, Inc.  All rights reserved.
+# Use is subject to license terms.
+#
+
+error=no
+while read src dest
+do
+	[ "$src" = /dev/null ] && continue
+
+	echo "Modifying $dest"
+
+	# Strip PKG_INSTALL_ROOT from dest if installation is to an
+	# alternate root.
+
+	if [ -n "$PKG_INSTALL_ROOT" -a "$PKG_INSTALL_ROOT" != "/" ]; then
+		client_dest=`echo $dest | \
+			/usr/bin/nawk -v rootdir="$PKG_INSTALL_ROOT" '{
+				{ print substr($0, length(rootdir)+1)} }'`
+		savepath=$PKGSAV/sed${client_dest}
+	else
+		savepath=$PKGSAV/sed${dest}
+	fi
+
+	dirname=`dirname $savepath`
+	if [ $? -ne 0 ]
+	then
+		error=yes
+		continue
+	fi
+
+	if [ ! -d $dirname ]
+	then
+		# ignore return since mkdir has bug
+		mkdir -p $dirname
+	fi
+
+	cp $src $savepath &&
+		/usr/sadm/install/scripts/cmdexec /bin/sed install $savepath $dest 
+
+	if [ $? -ne 0 ]
+	then
+		error=yes
+	fi
+done
+[ "$error" = yes ] &&
+	exit 2
+exit 0
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/usr/src/cmd/svr4pkg/pkgscripts/r.awk.sh	Fri Jun 05 10:28:40 2009 -0400
@@ -0,0 +1,60 @@
+#!/bin/sh
+#
+# 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
+# or http://www.opensolaris.org/os/licensing.
+# 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 2002 Sun Microsystems, Inc.  All rights reserved.
+# Use is subject to license terms.
+#
+
+error=no
+while read dest
+do
+	if [ -d $dest ]
+	then
+		echo "$dest"
+		rmdir $dest || error=yes
+	elif [ -f $dest ]
+	then
+		echo "Modifying $dest"
+
+		# Strip PKG_INSTALL_ROOT from dest if installation is to an
+		# alternate root.
+
+		if [ -n "$PKG_INSTALL_ROOT" -a "$PKG_INSTALL_ROOT" != "/" ]; then
+			client_dest=`echo $dest | \
+				/usr/bin/nawk -v rootdir="$PKG_INSTALL_ROOT" '{
+				    { print substr($0, length(rootdir)+1)} }'`
+			savepath=$PKGSAV/awk${client_dest}
+		else
+			savepath=$PKGSAV/awk${dest}
+		fi
+
+		/usr/sadm/install/scripts/cmdexec /usr/bin/awk remove $savepath $dest ||
+			error=yes
+	else
+		[ -r $dest ] && echo "$dest"
+		rm -f $dest || error=yes
+	fi
+done
+[ "$error" = yes ] &&
+	exit 2
+exit 0
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/usr/src/cmd/svr4pkg/pkgscripts/r.build.sh	Fri Jun 05 10:28:40 2009 -0400
@@ -0,0 +1,71 @@
+#!/bin/sh
+#
+# 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
+# or http://www.opensolaris.org/os/licensing.
+# 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 2002 Sun Microsystems, Inc.  All rights reserved.
+# Use is subject to license terms.
+#
+
+error=no
+while read dest
+do
+	if [ -d $dest ]
+	then
+		echo "$dest"
+		rmdir $dest || error=yes
+	elif [ -f $dest ]
+	then
+		echo "Modifying $dest"
+
+		# Strip PKG_INSTALL_ROOT from dest if installation is to an
+		# alternate root.
+
+		if [ -n "$PKG_INSTALL_ROOT" -a "$PKG_INSTALL_ROOT" != "/" ]; then
+			client_dest=`echo $dest | \
+				/usr/bin/nawk -v rootdir="$PKG_INSTALL_ROOT" '{
+				    { print substr($0, length(rootdir)+1)} }'`
+			savepath=$PKGSAV/build${client_dest}
+		else
+			savepath=$PKGSAV/build${dest}
+		fi
+
+		chmod +x $savepath
+		if $savepath remove > /tmp/$$build
+		then
+			if [ ! -s /tmp/$$build ]
+			then
+				rm -f $dest
+			else
+				cp /tmp/$$build $dest || error=yes
+			fi
+		else
+			error=yes
+		fi
+		rm -f /tmp/$$build
+	else
+		[ -r $dest ] && echo "$dest"
+		rm -f $dest || error=yes
+	fi
+done
+[ "$error" = yes ] &&
+	exit 2
+exit 0
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/usr/src/cmd/svr4pkg/pkgscripts/r.sed.sh	Fri Jun 05 10:28:40 2009 -0400
@@ -0,0 +1,60 @@
+#!/bin/sh
+#
+# 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
+# or http://www.opensolaris.org/os/licensing.
+# 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 2002 Sun Microsystems, Inc.  All rights reserved.
+# Use is subject to license terms.
+#
+
+error=no
+while read dest
+do
+	if [ -d $dest ]
+	then
+		echo "$dest"
+		rmdir $dest || error=yes
+	elif [ -f $dest ]
+	then
+		echo "Modifying $dest"
+
+		# Strip PKG_INSTALL_ROOT from dest if installation is to an
+		# alternate root.
+
+		if [ -n "$PKG_INSTALL_ROOT" -a "$PKG_INSTALL_ROOT" != "/" ]; then
+			client_dest=`echo $dest | \
+				/usr/bin/nawk -v rootdir="$PKG_INSTALL_ROOT" '{
+				    { print substr($0, length(rootdir)+1)} }'`
+			savepath=$PKGSAV/sed${client_dest}
+		else
+			savepath=$PKGSAV/sed${dest}
+		fi
+
+		/usr/sadm/install/scripts/cmdexec /bin/sed remove $savepath $dest ||
+			error=yes
+	else
+		[ -r $dest ] && echo "$dest"
+		rm -f $dest || error=yes
+	fi
+done
+[ "$error" = yes ] &&
+	exit 2
+exit 0
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/usr/src/cmd/svr4pkg/pkgtrans/Makefile	Fri Jun 05 10:28:40 2009 -0400
@@ -0,0 +1,41 @@
+#
+# 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
+# or http://www.opensolaris.org/os/licensing.
+# 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 2009 Sun Microsystems, Inc.  All rights reserved.
+# Use is subject to license terms.
+#
+
+PROG=		pkgtrans	
+
+OBJS=		main.o
+
+include $(SRC)/cmd/svr4pkg/Makefile.svr4pkg
+
+LDLIBS	+=	-lpkg -ladm
+
+
+.KEEP_STATE:
+all:            $(PROG)
+
+install:	all $(ROOTPROG)
+
+include $(SRC)/cmd/svr4pkg/Makefile.svr4pkg.targ
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/usr/src/cmd/svr4pkg/pkgtrans/main.c	Fri Jun 05 10:28:40 2009 -0400
@@ -0,0 +1,250 @@
+/*
+ * 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
+ * or http://www.opensolaris.org/os/licensing.
+ * 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 2009 Sun Microsystems, Inc.  All rights reserved.
+ * Use is subject to license terms.
+ */
+
+/* Copyright (c) 1984, 1986, 1987, 1988, 1989 AT&T */
+/* All Rights Reserved */
+
+
+#include <locale.h>
+#include <libintl.h>
+#include <stdio.h>
+#include <signal.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <string.h>
+#include <pkgtrans.h>
+#include <pkglib.h>
+#include <pkglocs.h>
+#include <libadm.h>
+#include <libinst.h>
+
+static int	options;
+static keystore_handle_t	keystore = NULL;
+
+static void	usage(void);
+static void	trap(int signo);
+
+#define	PASSWD_CMDLINE \
+		"## WARNING: USING <%s> MAKES PASSWORD " \
+		"VISIBLE TO ALL USERS."
+
+#define	PASSPHRASE_PROMPT	"Enter keystore password:"
+#define	KEYSTORE_OPEN	"Retrieving signing certificates from keystore <%s>"
+#define	PARAM_LEN		"Parameter <%s> too long"
+
+int
+main(int argc, char *argv[])
+{
+	int	c;
+	void	(*func)();
+	extern char	*optarg;
+	extern int	optind;
+	char		*keystore_alias = NULL;
+	char		*keystore_file = NULL;
+	boolean_t	create_sig = B_FALSE;
+	char		*homedir = NULL;
+	PKG_ERR		*err;
+	int		ret, len, homelen;
+
+	(void) setlocale(LC_ALL, "");
+
+#if !defined(TEXT_DOMAIN)	/* Should be defined by cc -D */
+#define	TEXT_DOMAIN "SYS_TEST"
+#endif
+	(void) textdomain(TEXT_DOMAIN);
+
+	(void) set_prog_name(argv[0]);
+
+	while ((c = getopt(argc, argv, "ga:P:k:snio?")) != EOF) {
+		switch (c) {
+		case 'n':
+			options |= PT_RENAME;
+			break;
+
+		case 'i':
+			options |= PT_INFO_ONLY;
+			break;
+
+		case 'o':
+			options |= PT_OVERWRITE;
+			break;
+
+		case 's':
+			options |= PT_ODTSTREAM;
+			break;
+
+		case 'g':
+			/* this should eventually be a PT_ option */
+			create_sig = B_TRUE;
+			break;
+
+		case 'k':
+			keystore_file = optarg;
+			break;
+
+		case 'a':
+			keystore_alias = optarg;
+			break;
+
+		case 'P':
+			set_passphrase_passarg(optarg);
+			if (ci_strneq(optarg, "pass:", 5)) {
+				/*
+				 * passwords on the command line are highly
+				 * insecure.  complain.
+				 */
+				logerr(gettext(PASSWD_CMDLINE), "pass:<pass>");
+			}
+			break;
+
+		default:
+			usage();
+			return (1);
+		}
+	}
+	func = signal(SIGINT, trap);
+	if (func != SIG_DFL)
+		(void) signal(SIGINT, func);
+	(void) signal(SIGHUP, trap);
+	(void) signal(SIGQUIT, trap);
+	(void) signal(SIGTERM, trap);
+	(void) signal(SIGPIPE, trap);
+#ifndef SUNOS41
+	(void) signal(SIGPWR, trap);
+#endif
+
+	if ((argc-optind) < 2) {
+		usage();
+		return (1);
+	}
+
+	if (create_sig) {
+		sec_init();
+		err = pkgerr_new();
+
+		/* figure out which keystore to use */
+		if (keystore_file == NULL) {
+			if (geteuid() == 0) {
+				/* we are superuser, so use their keystore */
+				keystore_file = PKGSEC;
+			} else {
+				if ((homedir = getenv("HOME")) == NULL) {
+				/*
+				 * not superuser, but no home dir, so
+				 * use superuser's keystore
+				 */
+					keystore_file = PKGSEC;
+				} else {
+				/* $HOME/.pkg/security\0 */
+					homelen = strlen(homedir) + 15;
+					keystore_file =
+					    malloc(strlen(homedir) + 15);
+					if (((len = snprintf(keystore_file,
+					    homelen, "%s/%s", homedir,
+					    ".pkg/security")) < 0) ||
+					    (len >= homelen)) {
+						logerr(gettext(PARAM_LEN),
+						    "$HOME");
+						quit(1);
+					}
+				}
+			}
+		}
+
+		logerr(gettext(KEYSTORE_OPEN), keystore_file);
+
+		set_passphrase_prompt(gettext(PASSPHRASE_PROMPT));
+
+		/* open keystore for reading */
+		if (open_keystore(err, keystore_file, get_prog_name(),
+		    pkg_passphrase_cb, KEYSTORE_DFLT_FLAGS, &keystore) != 0) {
+			pkgerr(err);
+			pkgerr_free(err);
+			quit(1);
+		}
+
+	} else {
+		/* no signature, so don't use a keystore */
+		keystore = NULL;
+	}
+
+	ret = pkgtrans(flex_device(argv[optind], 1),
+	    flex_device(argv[optind+1], 1), &argv[optind+2], options,
+	    keystore, keystore_alias);
+
+	if (create_sig) {
+		/* close keystore */
+		if (close_keystore(err, keystore, NULL) != 0) {
+			pkgerr(err);
+			pkgerr_free(err);
+			quit(1);
+		}
+		keystore = NULL;
+	}
+
+	quit(ret);
+	/*NOTREACHED*/
+}
+
+void
+quit(int retcode)
+{
+	PKG_ERR	*err;
+
+	err = pkgerr_new();
+	(void) signal(SIGINT, SIG_IGN);
+	(void) signal(SIGHUP, SIG_IGN);
+	(void) ds_close(1);
+	(void) pkghead(NULL);
+	if (keystore != NULL) {
+		(void) close_keystore(err, keystore, NULL);
+		pkgerr_free(err);
+	}
+	exit(retcode);
+}
+
+static void
+trap(int signo)
+{
+	(void) signal(SIGINT, SIG_IGN);
+	(void) signal(SIGHUP, SIG_IGN);
+
+	if (signo == SIGINT) {
+		progerr(gettext("aborted at user request.\n"));
+		quit(3);
+	}
+	progerr(gettext("aborted by signal %d\n"), signo);
+	quit(1);
+}
+
+static void
+usage(void)
+{
+	(void) fprintf(stderr,
+	    gettext("usage: %s [-ionsg] [-k keystore] " \
+	    "[-a alias] [-P password] srcdev dstdev [pkg [pkg...]]\n"),
+	    get_prog_name());
+}
--- a/usr/src/cmd/truss/print.c	Fri Jun 05 10:27:16 2009 -0400
+++ b/usr/src/cmd/truss/print.c	Fri Jun 05 10:28:40 2009 -0400
@@ -2275,6 +2275,7 @@
 		case PRIV_DEBUG:	s = "PRIV_DEBUG";	break;
 		case PRIV_AWARE:	s = "PRIV_AWARE";	break;
 		case PRIV_XPOLICY:	s = "PRIV_XPOLICY";	break;
+		case PRIV_AWARE_RESET:  s = "PRIV_AWARE_RESET"; break;
 		case NET_MAC_AWARE:	s =  "NET_MAC_AWARE";	break;
 		case NET_MAC_AWARE_INHERIT:
 			s = "NET_MAC_AWARE_INHERIT";
--- a/usr/src/common/atomic/sparcv9/atomic.s	Fri Jun 05 10:27:16 2009 -0400
+++ b/usr/src/common/atomic/sparcv9/atomic.s	Fri Jun 05 10:28:40 2009 -0400
@@ -20,7 +20,7 @@
  */
 
 /*
- * Copyright 2008 Sun Microsystems, Inc.  All rights reserved.
+ * Copyright 2009 Sun Microsystems, Inc.  All rights reserved.
  * Use is subject to license terms.
  */
 
@@ -82,6 +82,13 @@
 	mov	tmp2, %o7	/* restore callee's return address */	; \
 label/**/1:
 
+#ifdef	ATOMIC_SIMPLE_BO_ENABLE
+/*
+ * For some processors, simple limit has proved benefical
+ */
+#define ATOMIC_BACKOFF_CPU(val, limit, ncpu, cas_cnt, label)		\
+	set	1 << ATOMIC_BO_ENABLE_SHIFT, limit
+#else
 /*
  * For the kernel, we take into consideration of cas failures
  * and also scale the backoff limit w.r.t. the number of cpus.
@@ -104,6 +111,7 @@
 	mov	%g0, cas_cnt						; \
 	mov	1, val							; \
 label/**/1:
+#endif	/* ATOMIC_SIMPLE_BO_ENABLE */
 #endif	/* ATOMIC_BO_ENABLE_SHIFT */
 
 #else	/* _KERNEL */
@@ -129,11 +137,18 @@
  * The cas_cnt counts the cas instruction failure and is
  * initialized to 0.
  */
+#ifdef	ATOMIC_SIMPLE_BO_ENABLE
+#define ATOMIC_BACKOFF_INIT(val, ncpu, cas_cnt)	\
+	mov	1, val
+
+#else /* If not defined ATOMIC_SIMPLE_BO_ENABLE */
 #define ATOMIC_BACKOFF_INIT(val, ncpu, cas_cnt)	\
 	mov	1, val				; \
 	mov	%g0, ncpu			; \
 	mov	%g0, cas_cnt
 
+#endif	/* ATOMIC_SIMPLE_BO_ENABLE */
+
 #define ATOMIC_BACKOFF_BRANCH(cr, backoff, loop) \
 	bne,a,pn cr, backoff
 
@@ -152,7 +167,8 @@
 	bgu,pn	%xcc, label/**/_20 /* branch to middle of DELAY_SPIN */	; \
 	  nop								; \
 	ba	retlabel						; \
-	  sllx  val, 1, val
+	sllx	val, 1, val
+
 #else	/* ATOMIC_BO_ENABLE_SHIFT */
 #define ATOMIC_BACKOFF_INIT(val, ncpu, cas_cnt)
 
--- a/usr/src/grub/grub-0.97/lib/device.c	Fri Jun 05 10:27:16 2009 -0400
+++ b/usr/src/grub/grub-0.97/lib/device.c	Fri Jun 05 10:28:40 2009 -0400
@@ -236,7 +236,8 @@
     geom->cylinders = dkg.dkg_ncyl;
     geom->heads = dkg.dkg_nhead;
     geom->sectors = dkg.dkg_nsect;
-    geom->total_sectors = dkg.dkg_ncyl * dkg.dkg_nhead * dkg.dkg_nsect;
+    geom->total_sectors = (unsigned long long)dkg.dkg_ncyl * dkg.dkg_nhead
+	* dkg.dkg_nsect;
 
     goto success;
   }
@@ -270,7 +271,8 @@
     if (! fstat (fd, &st) && st.st_blocks)
       geom->total_sectors = st.st_blocks >> SECTOR_BITS;
     else
-      geom->total_sectors = geom->cylinders * geom->heads * geom->sectors;
+      geom->total_sectors = (unsigned long long)geom->cylinders *
+	geom->heads * geom->sectors;
   }
 
  success:
--- a/usr/src/grub/grub-0.97/stage2/bios.c	Fri Jun 05 10:27:16 2009 -0400
+++ b/usr/src/grub/grub-0.97/stage2/bios.c	Fri Jun 05 10:28:40 2009 -0400
@@ -96,7 +96,7 @@
 	    return err;
 	  
 	  geometry->flags &= ~BIOSDISK_FLAG_LBA_EXTENSION;
-	  geometry->total_sectors = (geometry->cylinders
+	  geometry->total_sectors = ((unsigned long long)geometry->cylinders
 				     * geometry->heads
 				     * geometry->sectors);
 	  return biosdisk (read, drive, geometry, sector, nsec, segment);
@@ -174,7 +174,7 @@
           geometry->heads = cdrp.heads;
           geometry->sectors = cdrp.sectors & 0x3F;
           geometry->sector_size = SECTOR_SIZE;
-          geometry->total_sectors = (geometry->cylinders
+          geometry->total_sectors = ((unsigned long long)geometry->cylinders
 				     * geometry->heads
 				     * geometry->sectors);
           return -1;
@@ -216,7 +216,7 @@
     {
       /* hard disk or CD-ROM */
       int version;
-      unsigned long total_sectors = 0;
+      unsigned long long total_sectors = 0;
       
       version = check_int13_extensions (drive);
 
@@ -278,16 +278,14 @@
 		 so I omit the check for now. - okuji  */
 	      /* if (drp.flags & (1 << 1)) */
 	       
-	      /* FIXME: when the 2TB limit becomes critical, we must
-		 change the type of TOTAL_SECTORS to unsigned long
-		 long.  */
 	      if (drp.total_sectors)
-		total_sectors = drp.total_sectors & ~0L;
+		total_sectors = drp.total_sectors;
 	      else
 		/* Some buggy BIOSes doesn't return the total sectors
 		   correctly but returns zero. So if it is zero, compute
 		   it by C/H/S returned by the LBA BIOS call.  */
-		total_sectors = drp.cylinders * drp.heads * drp.sectors;
+		total_sectors = (unsigned long long)drp.cylinders *
+		    drp.heads * drp.sectors;
 	    }
 	}
 
@@ -302,7 +300,7 @@
 
       if (! total_sectors)
 	{
-	  total_sectors = (geometry->cylinders
+	  total_sectors = ((unsigned long long)geometry->cylinders
 			   * geometry->heads
 			   * geometry->sectors);
 	}
@@ -331,7 +329,7 @@
       if (err)
 	return err;
 
-      geometry->total_sectors = (geometry->cylinders
+      geometry->total_sectors = ((unsigned long long)geometry->cylinders
 				 * geometry->heads
 				 * geometry->sectors);
       geometry->sector_size = SECTOR_SIZE;
--- a/usr/src/grub/grub-0.97/stage2/builtins.c	Fri Jun 05 10:27:16 2009 -0400
+++ b/usr/src/grub/grub-0.97/stage2/builtins.c	Fri Jun 05 10:28:40 2009 -0400
@@ -1619,7 +1619,7 @@
         grub_sprintf(bootsign, "%s/%s", BOOTSIGN_DIR, arg);
 	filename = bootsign;
 	goto harddisk;
-  } else if (for_root) {
+  } else if (for_root && !grub_strchr(arg, '/')) {
 	/* Boot signature without partition/slice information */
         grub_sprintf(bootsign, "%s/%s", BOOTSIGN_DIR, arg);
 	filename = bootsign;
@@ -3721,13 +3721,6 @@
   if (! rawread (current_drive, 0, 0, SECTOR_SIZE, mbr))
     return 1;
 
-  /* Check if the new partition will fit in the disk.  */
-  if (new_start + new_len > buf_geom.total_sectors)
-    {
-      errnum = ERR_GEOM;
-      return 1;
-    }
-
   /* Store the partition information in the MBR.  */
   lba_to_chs (new_start, &start_cl, &start_ch, &start_dh);
   lba_to_chs (new_start + new_len - 1, &end_cl, &end_ch, &end_dh);
@@ -4170,11 +4163,6 @@
 	return 1;
   }
 
-  if (grub_strchr(arg, '/')) {
-  	errnum = ERR_BAD_ARGUMENT;
-	return 1;
-  }
-
   find_best_root = 1;
   best_drive = 0;
   best_part = 0;
--- a/usr/src/grub/grub-0.97/stage2/disk_io.c	Fri Jun 05 10:27:16 2009 -0400
+++ b/usr/src/grub/grub-0.97/stage2/disk_io.c	Fri Jun 05 10:28:40 2009 -0400
@@ -185,13 +185,6 @@
 	  sector_size_bits = log2 (buf_geom.sector_size);
 	}
 
-      /* Make sure that SECTOR is valid.  */
-      if (sector >= buf_geom.total_sectors)
-	{
-	  errnum = ERR_GEOM;
-	  return 0;
-	}
-      
       slen = ((byte_offset + byte_len + buf_geom.sector_size - 1)
 	      >> sector_size_bits);
       
@@ -874,7 +867,8 @@
       buf_drive = current_drive;
       buf_track = BUF_CACHE_INVALID;
     }
-  part_length = buf_geom.total_sectors;
+  part_length =
+    (buf_geom.total_sectors > MAXUINT) ? MAXUINT : buf_geom.total_sectors;
 
   /* If this is the whole disk, return here.  */
   if (! flags && current_partition == 0xFFFFFF)
--- a/usr/src/grub/grub-0.97/stage2/fsys_zfs.c	Fri Jun 05 10:27:16 2009 -0400
+++ b/usr/src/grub/grub-0.97/stage2/fsys_zfs.c	Fri Jun 05 10:28:40 2009 -0400
@@ -1140,11 +1140,12 @@
 }
 
 /*
- * Get a list of valid vdev pathname from the boot device.
+ * Get a valid vdev pathname/devid from the boot device.
  * The caller should already allocate MAXPATHLEN memory for bootpath and devid.
  */
-int
-vdev_get_bootpath(char *nv, uint64_t inguid, char *devid, char *bootpath)
+static int
+vdev_get_bootpath(char *nv, uint64_t inguid, char *devid, char *bootpath,
+    int is_spare)
 {
 	char type[16];
 
@@ -1165,6 +1166,15 @@
 		if (guid != inguid)
 			return (ERR_NO_BOOTPATH);
 
+		/* for a spare vdev, pick the disk labeled with "is_spare" */
+		if (is_spare) {
+			uint64_t spare = 0;
+			(void) nvlist_lookup_value(nv, ZPOOL_CONFIG_IS_SPARE,
+			    &spare, DATA_TYPE_UINT64, NULL);
+			if (!spare)
+				return (ERR_NO_BOOTPATH);
+		}
+
 		if (nvlist_lookup_value(nv, ZPOOL_CONFIG_PHYS_PATH,
 		    bootpath, DATA_TYPE_STRING, NULL) != 0)
 			bootpath[0] = '\0';
@@ -1179,7 +1189,9 @@
 
 		return (0);
 
-	} else if (strcmp(type, VDEV_TYPE_MIRROR) == 0) {
+	} else if (strcmp(type, VDEV_TYPE_MIRROR) == 0 ||
+	    strcmp(type, VDEV_TYPE_REPLACING) == 0 ||
+	    (is_spare = (strcmp(type, VDEV_TYPE_SPARE) == 0))) {
 		int nelm, i;
 		char *child;
 
@@ -1192,7 +1204,7 @@
 
 			child_i = nvlist_array(child, i);
 			if (vdev_get_bootpath(child_i, inguid, devid,
-			    bootpath) == 0)
+			    bootpath, is_spare) == 0)
 				return (0);
 		}
 	}
@@ -1259,7 +1271,7 @@
 	if (nvlist_lookup_value(nvlist, ZPOOL_CONFIG_GUID, &diskguid,
 	    DATA_TYPE_UINT64, NULL))
 		return (ERR_FSYS_CORRUPT);
-	if (vdev_get_bootpath(nv, diskguid, outdevid, outpath))
+	if (vdev_get_bootpath(nv, diskguid, outdevid, outpath, 0))
 		return (ERR_NO_BOOTPATH);
 	return (0);
 }
--- a/usr/src/grub/grub-0.97/stage2/shared.h	Fri Jun 05 10:27:16 2009 -0400
+++ b/usr/src/grub/grub-0.97/stage2/shared.h	Fri Jun 05 10:28:40 2009 -0400
@@ -69,6 +69,7 @@
  */
 
 #define MAXINT     0x7FFFFFFF
+#define	MAXUINT		0xFFFFFFFF
 
 /* Maximum command line size. Before you blindly increase this value,
    see the comment in char_io.c (get_cmdline).  */
@@ -705,7 +706,7 @@
   /* The number of sectors */
   unsigned long sectors;
   /* The total number of sectors */
-  unsigned long total_sectors;
+  unsigned long long total_sectors;
   /* Device sector size */
   unsigned long sector_size;
   /* Flags */
--- a/usr/src/lib/Makefile	Fri Jun 05 10:27:16 2009 -0400
+++ b/usr/src/lib/Makefile	Fri Jun 05 10:28:40 2009 -0400
@@ -157,7 +157,10 @@
 	libnsctl	\
 	libunistat	\
 	libdscfg	\
-	librdc
+	librdc		\
+	libinstzones	\
+	libpkg
+
 $(CLOSED_BUILD)SUBDIRS += \
 	$(CLOSED)/lib/smartcard
 SUBDIRS += \
@@ -324,10 +327,12 @@
 	libinetcfg	\
 	libipmp		\
 	libinetutil	\
+	libinstzones	\
 	libnsl		\
 	libpam		\
 	libpicl		\
 	libpool		\
+	libpkg		\
 	libpp		\
 	libscf		\
 	libsasl		\
@@ -576,6 +581,8 @@
 librdc:		libsocket libnsl libnsctl libunistat libdscfg
 libuuid:	libdlpi
 libinetutil:	libsocket
+libinstzones:	libzonecfg libcontract
+libpkg:		libwanboot libscf libadm
 libsecdb:	libnsl
 libsasl:	libgss libsocket pkcs11 libmd
 sasl_plugins:	pkcs11 libgss libsocket libsasl
--- a/usr/src/lib/auditd_plugins/binfile/Makefile	Fri Jun 05 10:27:16 2009 -0400
+++ b/usr/src/lib/auditd_plugins/binfile/Makefile	Fri Jun 05 10:28:40 2009 -0400
@@ -19,10 +19,9 @@
 # CDDL HEADER END
 #
 #
-# Copyright 2006 Sun Microsystems, Inc.  All rights reserved.
+# Copyright 2009 Sun Microsystems, Inc.  All rights reserved.
 # Use is subject to license terms.
 #
-# ident	"%Z%%M%	%I%	%E% SMI"
 #
 
 include $(SRC)/lib/Makefile.lib
@@ -30,8 +29,8 @@
 SUBDIRS = $(MACH)
 
 TEXT_DOMAIN=	SUNW_OST_OSCMD
-POFILE= audit_binfile.po
-MSGFILES=	`$(GREP) -l gettext *.[ch]`
+POFILE= 	audit_binfile.po
+MSGFILES=	binfile.c
 
 all	:=	TARGET= all
 clean	:=	TARGET= clean
@@ -43,13 +42,16 @@
 
 all clean clobber install lint: $(SUBDIRS)
 
-$(POFILE): pofile_MSGFILES
+$(POFILE): $(MSGFILES)
+	$(BUILDPO.msgfiles)
 
 _msg: $(MSGDOMAINPOFILE)
 
-include $(SRC)/Makefile.msg.targ
 
 $(SUBDIRS): FRC
 	@cd $@; pwd; $(MAKE) $(TARGET)
 
 FRC:
+
+include $(SRC)/lib/Makefile.targ
+include $(SRC)/Makefile.msg.targ
--- a/usr/src/lib/auditd_plugins/syslog/Makefile	Fri Jun 05 10:27:16 2009 -0400
+++ b/usr/src/lib/auditd_plugins/syslog/Makefile	Fri Jun 05 10:28:40 2009 -0400
@@ -19,10 +19,9 @@
 # CDDL HEADER END
 #
 #
-# Copyright 2006 Sun Microsystems, Inc.  All rights reserved.
+# Copyright 2009 Sun Microsystems, Inc.  All rights reserved.
 # Use is subject to license terms.
 #
-# ident	"%Z%%M%	%I%	%E% SMI"
 #
 
 include $(SRC)/lib/Makefile.lib
@@ -30,8 +29,8 @@
 SUBDIRS = $(MACH)
 
 TEXT_DOMAIN=	SUNW_OST_OSCMD
-POFILE= audit_syslog.po
-MSGFILES=	`$(GREP) -l gettext *.[ch]`
+POFILE= 	audit_syslog.po
+MSGFILES=	sysplugin.c
 
 all	:=	TARGET= all
 clean	:=	TARGET= clean
@@ -43,13 +42,16 @@
 
 all clean clobber install lint: $(SUBDIRS)
 
-$(POFILE): pofile_MSGFILES
+$(POFILE): $(MSGFILES)
+	$(BUILDPO.msgfiles)
 
 _msg: $(MSGDOMAINPOFILE)
 
-include $(SRC)/Makefile.msg.targ
 
 $(SUBDIRS): FRC
 	@cd $@; pwd; $(MAKE) $(TARGET)
 
 FRC:
+
+include $(SRC)/lib/Makefile.targ
+include $(SRC)/Makefile.msg.targ
--- a/usr/src/lib/fm/topo/libtopo/common/topo_snap.c	Fri Jun 05 10:27:16 2009 -0400
+++ b/usr/src/lib/fm/topo/libtopo/common/topo_snap.c	Fri Jun 05 10:28:40 2009 -0400
@@ -233,6 +233,8 @@
 		topo_hdl_strfree(thp, thp->th_rootdir);
 	if (thp->th_ipmi != NULL)
 		ipmi_close(thp->th_ipmi);
+	if (thp->th_smbios != NULL)
+		smbios_close(thp->th_smbios);
 
 	/*
 	 * Clean-up snapshot
--- a/usr/src/lib/libgrubmgmt/common/libgrub_cmd.c	Fri Jun 05 10:27:16 2009 -0400
+++ b/usr/src/lib/libgrubmgmt/common/libgrub_cmd.c	Fri Jun 05 10:28:40 2009 -0400
@@ -48,6 +48,9 @@
 
 #define	RESET_MODULE(barg)	((barg)->gb_module[0] = 0)
 
+#define	BPROP_ZFSBOOTFS	"zfs-bootfs"
+#define	BPROP_BOOTPATH	"bootpath"
+
 #if defined(__i386)
 static const char cpuid_dev[] = "/dev/cpu/self/cpuid";
 
@@ -104,7 +107,8 @@
 
 	assert(barg);
 	if (strcmp(barg->gb_root.gr_fstyp, MNTTYPE_ZFS) == 0) {
-		n = snprintf(var, sz, "zfs-bootfs=%s,bootpath=\"%s\"",
+		n = snprintf(var, sz,
+		    BPROP_ZFSBOOTFS "=%s," BPROP_BOOTPATH "=\"%s\"",
 		    barg->gb_root.gr_fs[GRBM_ZFS_BOOTFS].gfs_dev,
 		    barg->gb_root.gr_physpath);
 	} else	{
@@ -153,6 +157,102 @@
 	return (ret);
 }
 
+/*
+ * Searches first occurence of boot-property 'bprop' in str.
+ * str supposed to be in format:
+ * " [-B prop=[value][,prop=[value]]...]
+ */
+static const char *
+find_bootprop(const char *str, const char *bprop)
+{
+	const char *s;
+	size_t bplen, len;
+
+	assert(str);
+	assert(bprop);
+
+	bplen = strlen(bprop);
+	s = str;
+
+	while ((str = strstr(s, " -B")) != NULL ||
+	    (str = strstr(s, "\t-B")) != NULL) {
+		s = str + 3;
+		len = strspn(s, " \t");
+
+		/* empty -B option, skip it */
+		if (len != 0 && s[len] == '-')
+			continue;
+
+		s += len;
+		do {
+			len = strcspn(s, "= \t");
+			if (s[len] !=  '=')
+				break;
+
+			/* boot property we are looking for? */
+			if (len == bplen && strncmp(s, bprop, bplen) == 0)
+				return (s);
+
+			s += len;
+
+			/* skip boot property value */
+			while ((s = strpbrk(s + 1, "\"\', \t")) != NULL) {
+
+				/* skip quoted */
+				if (s[0] == '\"' || s[0] == '\'') {
+					if ((s = strchr(s + 1, s[0])) == NULL) {
+						/* unbalanced quotes */
+						return (s);
+					}
+				}
+				else
+					break;
+			}
+
+			/* no more boot properties */
+			if (s == NULL)
+				return (s);
+
+			/* no more boot properties in that -B block */
+			if (s[0] != ',')
+				break;
+
+			s += strspn(s, ",");
+		} while (s[0] != ' ' && s[0] != '\t');
+	}
+	return (NULL);
+}
+
+/*
+ * Add bootpath property to str if
+ * 	1. zfs-bootfs property is set explicitly
+ * and
+ * 	2. bootpath property is not set
+ */
+static int
+update_bootpath(char *str, size_t strsz, const char *bootpath)
+{
+	size_t n;
+	char *buf;
+	const char *bfs;
+
+	/* zfs-bootfs is not specified, or bootpath is allready set */
+	if ((bfs = find_bootprop(str, BPROP_ZFSBOOTFS)) == NULL ||
+	    find_bootprop(str, BPROP_BOOTPATH) != NULL)
+		return (0);
+
+	n = bfs - str;
+	buf = alloca(strsz);
+
+	bcopy(str, buf, n);
+	if (snprintf(buf + n, strsz - n, BPROP_BOOTPATH "=\"%s\",%s",
+	    bootpath, bfs) >= strsz - n)
+		return (E2BIG);
+
+	bcopy(buf, str, strsz);
+	return (0);
+}
+
 static int
 match_bootfs(zfs_handle_t *zfh, void *data)
 {
@@ -242,8 +342,13 @@
 	    ZFS_BOOT_VAR, strlen(ZFS_BOOT_VAR), bootfs, bfslen)) != 0)
 		return (ret);
 
-	ret = expand_var(barg->gb_kernel, sizeof (barg->gb_kernel),
-	    ISADIR_VAR, strlen(ISADIR_VAR), isadir, isalen);
+	if ((ret = expand_var(barg->gb_kernel, sizeof (barg->gb_kernel),
+	    ISADIR_VAR, strlen(ISADIR_VAR), isadir, isalen)) != 0)
+		return (ret);
+
+	if (strcmp(barg->gb_root.gr_fstyp, MNTTYPE_ZFS) == 0)
+		ret = update_bootpath(barg->gb_kernel, sizeof (barg->gb_kernel),
+		    barg->gb_root.gr_physpath);
 
 	return (ret);
 }
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/usr/src/lib/libinstzones/Makefile	Fri Jun 05 10:28:40 2009 -0400
@@ -0,0 +1,43 @@
+#
+# 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
+# or http://www.opensolaris.org/os/licensing.
+# 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 2009 Sun Microsystems, Inc.  All rights reserved.
+# Use is subject to license terms.
+#
+
+SUBDIRS	=	$(MACH) 
+
+all :=		TARGET = all
+install :=	TARGET = install
+clean :=	TARGET = clean
+clobber :=	TARGET = clobber
+_msg :=		TARGET = _msg
+lint :=		TARGET = lint
+
+
+.KEEP_STATE:
+
+all clean clobber install _msg lint: $(SUBDIRS)
+
+$(SUBDIRS): FRC
+	@cd $@; pwd; $(MAKE) $(TARGET)
+
+FRC:
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/usr/src/lib/libinstzones/Makefile.com	Fri Jun 05 10:28:40 2009 -0400
@@ -0,0 +1,78 @@
+#
+# 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
+# or http://www.opensolaris.org/os/licensing.
+# 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 2009 Sun Microsystems, Inc.  All rights reserved.
+# Use is subject to license terms.
+#
+
+LIBRARY=	libinstzones.a
+VERS=		.1
+
+OBJECTS =	\
+	    	zones_args.o \
+		zones_exec.o \
+		zones_locks.o \
+		zones_paths.o \
+		zones_states.o \
+		zones_str.o \
+		zones_utils.o \
+		zones_lofs.o \
+		zones.o
+
+# include library definitions
+include $(SRC)/lib/Makefile.lib
+
+SRCDIR=		../common
+
+POFILE =	libinstzones.po
+MSGFILES =	$(OBJECTS:%.o=../common/%.i)
+CLEANFILES +=	$(MSGFILES)
+
+# openssl forces us to ignore dubious pointer casts, thanks to its clever
+# use of macros for stack management.
+LINTFLAGS=	-umx -errtags \
+		-erroff=E_BAD_PTR_CAST_ALIGN,E_BAD_PTR_CAST
+$(LINTLIB):=	SRCS = $(SRCDIR)/$(LINTSRC)
+
+LIBS = $(DYNLIB) $(LINTLIB)
+
+DYNFLAGS += $(ZLAZYLOAD)
+
+LDLIBS +=	-lc -lcontract -lzonecfg
+
+CFLAGS +=	$(CCVERBOSE)
+CPPFLAGS +=	-I$(SRCDIR)
+
+.KEEP_STATE:
+
+all:	$(LIBS)
+
+$(POFILE): $(MSGFILES)
+	$(BUILDPO.msgfiles)
+
+_msg: $(MSGDOMAINPOFILE)
+
+lint:	lintcheck
+
+# include library targets
+include $(SRC)/lib/Makefile.targ
+include $(SRC)/Makefile.msg.targ
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/usr/src/lib/libinstzones/common/instzones_api.h	Fri Jun 05 10:28:40 2009 -0400
@@ -0,0 +1,187 @@
+/*
+ * 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
+ * or http://www.opensolaris.org/os/licensing.
+ * 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.
+ */
+
+#ifndef _INSTZONES_API_H
+#define	_INSTZONES_API_H
+
+
+/*
+ * Module:	instzones_api.h
+ * Group:	libinstzones
+ * Description:	This module contains the libinstzones API data structures,
+ *		constants, and function prototypes.
+ */
+
+/*
+ * required includes
+ */
+
+/* System includes */
+
+#include <stdarg.h>
+#include <stdio.h>
+#include <string.h>
+#include <termios.h>
+#include <sys/mman.h>
+#include <sys/param.h>
+#include <sys/types.h>
+#include <sys/ioctl.h>
+#include <libzonecfg.h>
+
+/*
+ * C++ prefix
+ */
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+
+/* function prototypes */
+
+/* PRINTFLIKE1 */
+typedef void (*_z_printf_fcn_t)(char *a_format, ...);
+
+/* zone list structure */
+
+typedef struct _zoneListElement_t *zoneList_t;
+
+/* zone brand list structure */
+
+typedef struct _zoneBrandList zoneBrandList_t;
+
+/* flag for zone locking functions */
+
+typedef unsigned long ZLOCKS_T;
+
+/* flags for zone locking */
+
+#define	ZLOCKS_ZONE_ADMIN	((ZLOCKS_T)0x00000001)	/* zone admin */
+#define	ZLOCKS_PKG_ADMIN	((ZLOCKS_T)0x00000002)	/* package admin */
+#define	ZLOCKS_PATCH_ADMIN	((ZLOCKS_T)0x00000004)	/* patch admin */
+#define	ZLOCKS_ALL		((ZLOCKS_T)0xFFFFFFFF)	/* all locks */
+#define	ZLOCKS_NONE		((ZLOCKS_T)0x00000000)	/* no locks */
+
+/*
+ * external function definitions
+ */
+
+/* zones.c */
+
+extern boolean_t	z_zones_are_implemented(void);
+extern void		z_set_zone_root(const char *zroot);
+extern boolean_t	z_zlist_is_zone_runnable(zoneList_t a_zoneList,
+				int a_zoneIndex);
+extern boolean_t	z_zlist_restore_zone_state(zoneList_t a_zoneList,
+				int a_zoneIndex);
+extern boolean_t	z_zlist_change_zone_state(zoneList_t a_zoneList,
+				int a_zoneIndex, zone_state_t a_newState);
+extern char		*z_get_zonename(void);
+extern zone_state_t	z_zlist_get_current_state(zoneList_t a_zoneList,
+				int a_zoneIndex);
+extern char 		**z_zlist_get_inherited_pkg_dirs(zoneList_t a_zoneList,
+				int a_zoneIndex);
+extern zone_state_t	z_zlist_get_original_state(zoneList_t a_zoneList,
+				int a_zoneIndex);
+extern int		z_zoneExecCmdArray(int *r_status, char **r_results,
+				char *a_inputFile, char *a_path, char *a_argv[],
+				const char *a_zoneName, int *a_fds);
+extern int		z_zone_exec(const char *zonename, const char *path,
+				char *argv[], char *a_stdoutPath,
+				char *a_stderrPath, int *a_fds);
+extern boolean_t	z_create_zone_admin_file(char *a_zoneAdminFilename,
+				char *a_userAdminFilename);
+extern void		z_free_zone_list(zoneList_t a_zoneList);
+extern zoneList_t	z_get_nonglobal_zone_list(void);
+extern zoneList_t	z_get_nonglobal_zone_list_by_brand(zoneBrandList_t *);
+extern void		z_free_brand_list(zoneBrandList_t *a_brandList);
+extern zoneBrandList_t	*z_make_brand_list(const char *brandList,
+				const char *delim);
+extern boolean_t	z_lock_zones(zoneList_t a_zlst, ZLOCKS_T a_lflags);
+extern boolean_t	z_non_global_zones_exist(void);
+extern boolean_t	z_running_in_global_zone(void);
+extern void		z_set_output_functions(_z_printf_fcn_t a_echo_fcn,
+				_z_printf_fcn_t a_echo_debug_fcn,
+				_z_printf_fcn_t a_progerr_fcn);
+extern int		z_set_zone_spec(const char *zlist);
+extern int		z_verify_zone_spec(void);
+extern boolean_t	z_on_zone_spec(const char *zonename);
+extern boolean_t	z_global_only(void);
+extern boolean_t	z_unlock_zones(zoneList_t a_zlst, ZLOCKS_T a_lflags);
+extern boolean_t	z_lock_this_zone(ZLOCKS_T a_lflags);
+extern boolean_t	z_unlock_this_zone(ZLOCKS_T a_lflags);
+extern char		*z_zlist_get_zonename(zoneList_t a_zoneList,
+				int a_zoneId);
+extern char		*z_zlist_get_zonepath(zoneList_t a_zoneList,
+				int a_zoneId);
+extern char		*z_zlist_get_scratch(zoneList_t a_zoneList,
+				int a_zoneId);
+extern boolean_t	z_umount_lz_mount(char *a_lzMountPoint);
+extern boolean_t	z_mount_in_lz(char **r_lzMountPoint,
+				char **r_lzRootPath,
+				char *a_zoneName, char *a_gzPath,
+				char *a_mountPointPrefix);
+extern boolean_t	z_is_zone_branded(char *zoneName);
+extern boolean_t	z_is_zone_brand_in_list(char *zoneName,
+			    zoneBrandList_t *brands);
+extern boolean_t	z_zones_are_implemented(void);
+
+/* zones_exec.c */
+extern int		z_ExecCmdArray(int *r_status, char **r_results,
+				char *a_inputFile, char *a_cmd, char **a_args);
+/*VARARGS*/
+extern int		z_ExecCmdList(int *r_status, char **r_results,
+				char *a_inputFile, char *a_cmd, ...);
+
+/* zones_paths.c */
+extern boolean_t	z_add_inherited_file_system(
+				char *a_inheritedFileSystem);
+extern boolean_t	z_path_is_inherited(char *a_path, char a_ftype,
+				char *a_rootDir);
+extern char **		z_get_inherited_file_systems(void);
+extern char		*z_make_zone_root(char *);
+extern void		z_path_canonize(char *file);
+extern void		z_canoninplace(char *file);
+extern void		z_free_inherited_file_systems(void);
+
+/* zones_lofs.c */
+extern void z_destroyMountTable(void);
+extern int z_createMountTable(void);
+extern int z_isPathWritable(const char *);
+extern void z_resolve_lofs(char *path, size_t);
+
+/* zones_states.c */
+extern int UmountAllZones(char *mntpnt);
+
+/*
+ * C++ postfix
+ */
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* _INSTZONES_API_H */
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/usr/src/lib/libinstzones/common/instzones_lib.h	Fri Jun 05 10:28:40 2009 -0400
@@ -0,0 +1,387 @@
+/*
+ * 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
+ * or http://www.opensolaris.org/os/licensing.
+ * 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.
+ */
+
+
+
+#ifndef _INSTZONES_LIB_H
+#define	_INSTZONES_LIB_H
+
+
+/*
+ * Module:	instzones_lib.h
+ * Group:	libinstzones
+ * Description:	This module contains the libinstzones internal data structures,
+ *		constants, and function prototypes. This include should not be
+ *		needed by any external code (consumers of this library).
+ */
+
+/*
+ * required includes
+ */
+
+/* System includes */
+
+#include <zone.h>
+#include <libzonecfg.h>
+#include <libcontract.h>
+
+/* Local includes */
+
+#include "instzones_api.h"
+
+/*
+ * C++ prefix
+ */
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/* constants */
+
+
+/* macros */
+
+/*
+ * argument array processing type
+ */
+
+/*
+ * This is the "argument array" definition that is returned by _z_new_args
+ * and is used by _z_add_args, _z_free_args, etc.
+ */
+
+struct _argArray_t {
+	long	_aaNumArgs;	/* number of arguments set */
+	long	_aaMaxArgs;	/* number of arguments allocated */
+	char	**_aaArgs;	/* actual arguments */
+};
+
+typedef struct _argArray_t argArray_t;
+
+/*
+ * lock objects
+ */
+
+/*
+ * this allows a root path to be prepended to a lock object; e.g.
+ *   rootpath.%s/zone.%s/...
+ */
+#define	LOBJ_ROOTPATH	"rootpath.%s"
+
+/* this locks a single zone (zone.name) */
+#define	LOBJ_ONE_ZONE	"zone.%s"
+
+/* this locks all zones */
+#define	LOBJ_ZONEADMIN	"zone.*"
+
+/* this locks all packages, in all zones */
+#define	LOBJ_PKGADMIN	"zone.*/package.*"
+
+/* this locks all patches, in all zones */
+#define	LOBJ_PATCHADMIN	"zone.*/patch.*"
+
+#define	LOCK_OBJECT_MAXLEN	512
+#define	LOCK_KEY_MAXLEN		37
+
+/* paths to commands executed by this module */
+
+#define	PKGADM_CMD	"/usr/bin/pkgadm"
+#define	ZONEADM_CMD	"/usr/sbin/zoneadm"
+
+/* max message size for program output functions (echo, echo debug, progerr) */
+
+#define	MAX_MESSAGE_SIZE	4096
+
+/* maximum number of retries when waiting for lock */
+
+#define	MAX_RETRIES	300
+
+/* delay (in seconds) between retries when waiting for lock */
+
+#define	RETRY_DELAY_SECS	1
+
+/* Size of buffer increments when reading from pipe */
+
+#define	PIPE_BUFFER_INCREMENT	256
+
+/* Maximum number of arguments to pkg_ExecCmdList */
+
+#define	MAX_EXEC_CMD_ARGS	100
+
+/*
+ * These dynamic libraries are required in order to use the zones
+ * functionality - if these libraries are not available at runtime,
+ * then zones are assumed to NOT be available, and it is assumed that
+ * the program is running in the global zone with no non-global zones.
+ */
+
+#if	defined(LIBZONECFG_PATH)
+#define	ZONECFG1_LIBRARY	LIBZONECFG_PATH
+#else	/* defined(LIBZONECFG_PATH) */
+#define	ZONECFG1_LIBRARY	"libzonecfg.so.1"
+#endif	/* defined(LIBZONECFG_PATH) */
+
+#define	ZONECFG_LIBRARY		"libzonecfg.so"
+
+#define	CONTRACT1_LIBRARY	"libcontract.so.1"
+#define	CONTRACT_LIBRARY	"libcontract.so"
+
+/*
+ * Environment values used when running commands within a non-global zone
+ */
+
+/* SHELL= */
+
+#define	ZONE_FAILSAFESHELL	"/sbin/sh"
+
+/* PATH= */
+
+#define	ZONE_DEF_PATH		"/usr/sbin:/usr/bin"
+
+/* error codes */
+#define	ERR_MALLOC_FAIL		-50
+
+/*
+ * zone brand list structure
+ */
+
+struct _zoneBrandList {
+	char			*string_ptr;
+	struct _zoneBrandList	*next;
+};
+
+/*
+ * zone status structure - used to retrieve and hold status of zones
+ */
+
+typedef unsigned long _zone_status_t;
+
+struct _zoneListElement_t {
+	char		**_zlInheritedDirs;
+	char		*_zlName;
+	char		*_zlPath;
+	char		*_zlScratchName;
+	char		*_zlLockObjects;
+	/*
+	 * the install "state" refers to the zone states listed in
+	 * /usr/include/libzonecfg.h that is stored in the zone_state_t
+	 * structure and returned from getzoneent_private() - such as:
+	 * ZONE_STATE_CONFIGURED, ZONE_STATE_INCOMPLETE,
+	 * ZONE_STATE_INSTALLED, ZONE_STATE_READY, ZONE_STATE_MOUNTED,
+	 * ZONE_STATE_SHUTTING_DOWN, ZONE_STATE_DOWN.
+	 */
+	zone_state_t	_zlOrigInstallState;
+	zone_state_t	_zlCurrInstallState;
+	/*
+	 * the kernel "status" refers to the zone status listed in
+	 * /usr/include/sys/zone.h, returned by zone_get_state(),
+	 * and defined in the zone_status_t enum - such as:
+	 * ZONE_IS_UNINITIALIZED, ZONE_IS_READY, ZONE_IS_BOOTING,
+	 * ZONE_IS_RUNNING, ZONE_IS_SHUTTING_DOWN, ZONE_IS_EMPTY,
+	 * ZONE_IS_DOWN, ZONE_IS_DYING, ZONE_IS_DEAD.
+	 */
+	zone_status_t	_zlOrigKernelStatus;
+	zone_status_t	_zlCurrKernelStatus;
+	/*
+	 * this is an internal state recorded about the zone (ZSF_xxx).
+	 */
+	_zone_status_t	_zlStatus;
+};
+
+typedef struct _zoneListElement_t zoneListElement_t;
+
+/* bits used in the _zoneListElement _zlStatus variable */
+
+#define	ZST_NOT_BOOTABLE	((_zone_status_t)0x00000001)
+#define	ZST_LOCKED		((_zone_status_t)0x00000002)
+
+/*
+ * User-specified list of zones.
+ */
+
+typedef struct zone_spec_s {
+	struct zone_spec_s	*zl_next;
+	boolean_t		zl_used;
+	char			zl_name[ZONENAME_MAX];
+} zone_spec_t;
+
+/*
+ * The global data structure used to hold all of the global (extern) data
+ * used by this library.
+ *
+ * --> THESE DEFINITIONS ARE ORDER DEPENDENT BASED <--
+ * --> ON THE ORDER OF THE STRUCTURE INITIALIZERS! <--
+ */
+
+struct _z_global_data_t {
+	char		*_z_ObjectLocks;	/* object locks held */
+	char 		*_z_root_dir;		/* root for zone lib fctns */
+	int		_z_SigReceived;		/* received signal count */
+	pid_t		_z_ChildProcessId;	/* child to propagate sigs to */
+	zone_spec_t	*_zone_spec;		/* zones to operate on */
+	_z_printf_fcn_t	_z_echo;		/* operational message fcn */
+	_z_printf_fcn_t	_z_echo_debug;		/* debug message fcn */
+	_z_printf_fcn_t	_z_progerr;		/* program error fcn */
+};
+
+typedef struct _z_global_data_t z_global_data_t;
+
+/*
+ * When _INSTZONES_LIB_Z_DEFINE_GLOBAL_DATA is defined,
+ * instzones_lib.h will define the z_global_data structure.
+ * Otherwise an extern to the structure is inserted.
+ *
+ * --> THESE DEFINITIONS ARE ORDER DEPENDENT BASED ON <--
+ * --> THE ORDER OF THE _z_global_data_t STRUCTURE!!! <--
+ */
+
+#if	defined(_INSTZONES_LIB_Z_DEFINE_GLOBAL_DATA)
+
+/* define and initialize structure */
+
+z_global_data_t _z_global_data = {
+	NULL,	/* *_z_ObjectLocks */
+	"",	/* *_z_root_dir */
+	0,	/* _z_SigReceived */
+	-1,	/* _z_ChildProcessId */
+	NULL,	/* *_zone_spec */
+	NULL,	/* _z_echo */
+	NULL,	/* _z_echo_debug */
+	NULL	/* _z_progerr */
+};
+
+#else	/* !defined(_INSTZONES_LIB__Z_DEFINE_GLOBAL_DATA) */
+
+/* define structure extern */
+
+extern z_global_data_t _z_global_data;
+
+#endif	/* defined(_INSTZONES_LIB_Z_DEFINE_GLOBAL_DATA) */
+
+/* function prototypes */
+
+/*
+ *  The following functions can be used by other libs, but not
+ *  by applications.
+ */
+
+/* ---> zones_states.c */
+
+boolean_t	_z_make_zone_ready(zoneListElement_t *a_zlem);
+boolean_t	_z_make_zone_down(zoneListElement_t *a_zlem);
+boolean_t	_z_make_zone_running(zoneListElement_t *a_zlem);
+int		UmountAllZones(char *mntpnt);
+void		*_z_calloc(size_t size);
+void		*_z_malloc(size_t size);
+void		*_z_realloc(void *ptr, size_t size);
+void		*_z_strdup(char *str);
+
+/* ---> zones_utils.c */
+
+/*PRINTFLIKE1*/
+void		_z_program_error(char *fmt, ...);
+/*PRINTFLIKE1*/
+void		_z_echo(char *fmt, ...);
+/*PRINTFLIKE1*/
+void		_z_echoDebug(char *a_fmt, ...);
+int		_z_is_directory(char *path);
+char		**_z_get_inherited_dirs(char *a_zoneName);
+boolean_t	_z_running_in_global_zone(void);
+boolean_t	_z_zones_are_implemented(void);
+void		_z_sig_trap(int a_signo);
+int		_z_close_file_descriptors(void *a_fds, int a_fd);
+boolean_t	_z_brands_are_implemented(void);
+
+
+/* ---> zones_locks.c */
+
+boolean_t	_z_adjust_lock_object_for_rootpath(char **r_result,
+			char *a_lockObject);
+boolean_t	_z_acquire_lock(char **r_lockKey, char *a_zoneName,
+			char *a_lock, pid_t a_pid, boolean_t a_wait);
+boolean_t	_z_lock_zone(zoneListElement_t *a_zlst,
+			ZLOCKS_T a_lflags);
+boolean_t	_z_lock_zone_object(char **r_objectLocks,
+			char *a_zoneName, char *a_lockObject,
+			pid_t a_pid, char *a_waitingMsg,
+			char *a_busyMsg);
+boolean_t	_z_release_lock(char *a_zoneName, char *a_lock,
+			char *a_key, boolean_t a_wait);
+boolean_t	_z_unlock_zone(zoneListElement_t *a_zlst,
+			ZLOCKS_T a_lflags);
+boolean_t	_z_unlock_zone_object(char **r_objectLocks,
+			char *a_zoneName, char *a_lockObject,
+			char *a_errMsg);
+
+/* ---> zones_args.c */
+
+void		_z_free_args(argArray_t *a_args);
+argArray_t	*_z_new_args(int initialCount);
+/*PRINTFLIKE2*/
+boolean_t	_z_add_arg(argArray_t *a_args, char *a_format, ...);
+int		_z_get_argc(argArray_t *a_args);
+char		**_z_get_argv(argArray_t *a_args);
+
+/* ---> zones_str.c */
+
+boolean_t	_z_strContainsToken(char *a_string, char *a_token,
+			char *a_separators);
+char		*_z_strGetToken(char *r_sep, char *a_string,
+			int a_index, char *a_separators);
+void		_z_strRemoveLeadingWhitespace(char **a_str);
+void		_z_strGetToken_r(char *r_sep, char *a_string,
+			int a_index, char *a_separators, char *a_buf,
+			int a_bufLen);
+void		_z_strAddToken(char **a_old, char *a_new,
+			char a_separator);
+void		_z_strRemoveToken(char **r_string, char *a_token,
+			char *a_separators, int a_index);
+/*PRINTFLIKE3*/
+void		_z_strPrintf_r(char *a_buf, int a_bufLen,
+			char *a_format, ...);
+/*PRINTFLIKE1*/
+char		*_z_strPrintf(char *a_format, ...);
+
+/* ---> zones_exec.c */
+
+int		_z_zone_exec(int *r_status, char **r_results, char *a_inputFile,
+			char *a_path, char *a_argv[], const char *a_zoneName,
+			int *a_fds);
+int		_zexec(const char *a_zoneName,
+			const char *path, char *argv[]);
+char		*_zexec_add_env(char *name, char *value);
+int		_zexec_init_template(void);
+char		**_zexec_prep_env();
+
+/*
+ * C++ postfix
+ */
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif	/* _INSTZONES_LIB_H */
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/usr/src/lib/libinstzones/common/llib-linstzones	Fri Jun 05 10:28:40 2009 -0400
@@ -0,0 +1,31 @@
+/*
+ * 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
+ * or http://www.opensolaris.org/os/licensing.
+ * 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 2009 Sun Microsystems, Inc.  All rights reserved.
+ * Use is subject to license terms.
+ */
+
+
+/* LINTLIBRARY */
+/* PROTOLIB1 */
+
+#include "instzones_api.h"
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/usr/src/lib/libinstzones/common/mapfile-vers	Fri Jun 05 10:28:40 2009 -0400
@@ -0,0 +1,94 @@
+#
+# 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
+# or http://www.opensolaris.org/os/licensing.
+# 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 2009 Sun Microsystems, Inc.  All rights reserved.
+# Use is subject to license terms.
+#
+
+#
+# MAPFILE HEADER START
+#
+# WARNING:  STOP NOW.  DO NOT MODIFY THIS FILE.
+# Object versioning must comply with the rules detailed in
+#
+#	usr/src/lib/README.mapfiles
+#
+# You should not be making modifications here until you've read the most current
+# copy of that file. If you need help, contact a gatekeeper for guidance.
+#
+# MAPFILE HEADER END
+#
+
+SUNWprivate {
+    global:
+	UmountAllZones;
+	z_add_inherited_file_system;
+	z_brands_are_implemented;
+	z_canoninplace;
+	z_createMountTable;
+	z_create_zone_admin_file;
+	z_destroyMountTable;
+	z_ExecCmdArray;
+	z_ExecCmdList;
+	z_free_brand_list;
+	z_free_inherited_file_systems;
+	z_free_zone_list;
+	z_get_inherited_file_systems;
+	z_get_nonglobal_zone_list;
+	z_get_nonglobal_zone_list_by_brand;
+	z_get_zonename;
+	z_global_only;
+	z_isPathWritable;
+	z_is_zone_branded;
+	z_is_zone_brand_in_list;
+	z_lock_this_zone;
+	z_lock_zones;
+	z_make_brand_list;
+	z_make_zone_root;
+	z_mount_in_lz;
+	z_non_global_zones_exist;
+	z_on_zone_spec;
+	z_path_canonize;
+	z_path_is_inherited;
+	z_resolve_lofs;
+	z_running_in_global_zone;
+	z_set_output_functions;
+	z_set_zone_root;
+	z_set_zone_spec;
+	z_umount_lz_mount;
+	z_unlock_this_zone;
+	z_unlock_zones;
+	z_verify_zone_spec;
+	z_zlist_change_zone_state;
+	z_zlist_get_current_state;
+	z_zlist_get_inherited_pkg_dirs;
+	z_zlist_get_original_state;
+	z_zlist_get_scratch;
+	z_zlist_get_zonename;
+	z_zlist_get_zonepath;
+	z_zlist_is_zone_runnable;
+	z_zlist_restore_zone_state;
+	z_zone_exec;
+  local:
+	*;
+};
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/usr/src/lib/libinstzones/common/zones.c	Fri Jun 05 10:28:40 2009 -0400
@@ -0,0 +1,2425 @@
+/*
+ * 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
+ * or http://www.opensolaris.org/os/licensing.
+ * 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 2009 Sun Microsystems, Inc.  All rights reserved.
+ * Use is subject to license terms.
+ */
+
+
+/*
+ * Module:	zones.c
+ * Group:	libinstzones
+ * Description:	Provide "zones" interface for install consolidation code
+ *
+ * Public Methods:
+ *  z_create_zone_admin_file - Given a location to create the file, and
+ *	optionally an existing administration file, generate an
+ *	administration file that can be used to perform "non-interactive"
+ *	operations in a non-global zone.
+ *  z_free_zone_list - free contents of zoneList_t object
+ *  z_get_nonglobal_zone_list - return zoneList_t object describing all
+ *	non-global native zones
+ *  z_get_nonglobal_zone_list_by_brand - return zoneList_t object describing
+ *      all non-global zones matching the list of zone brands passed in.
+ *  z_free_brand_list - free contents of a zoneBrandList_t object
+ *  z_make_brand_list - return a zoneBrandList_t object describing the list
+ *	of all zone brands passed in.
+ *  z_get_zonename - return the name of the current zone
+ *  z_global_only - Determine if the global zone is only zone on the spec list
+ *  z_lock_this_zone - lock this zone
+ *  z_lock_zones - lock specified zones
+ *  z_mount_in_lz - Mount global zone directory in specified zone's root file
+ *	system
+ *  z_non_global_zones_exist - Determine if any non-global native zones exist
+ *  z_on_zone_spec - Determine if named zone is on the zone_spec list
+ *  z_running_in_global_zone - Determine if running in the "global" zone
+ *  z_set_output_functions - Link program specific output functions
+ *  z_set_zone_root - Set root for zones library operations
+ *  z_set_zone_spec - Set list of zones on which actions will be performed
+ *  z_umount_lz_mount - Unmount directory mounted with z_mount_in_lz
+ *  z_unlock_this_zone - unlock this zone
+ *  z_unlock_zones - unlock specified zones
+ *  z_verify_zone_spec - Verify list of zones on which actions will be performed
+ *  z_zlist_change_zone_state - Change the current state of the specified zone
+ *  z_zlist_get_current_state - Determine the current kernel state of the
+ *	specified zone
+ *  z_zlist_get_inherited_pkg_dirs - Determine directories inherited by
+ *	specified zone
+ *  z_zlist_get_original_state - Return the original kernal state of the
+ *	specified zone
+ *  z_zlist_get_scratch - Determine name of scratch zone
+ *  z_zlist_get_zonename - Determine name of specified zone
+ *  z_zlist_get_zonepath - Determine zonepath of specified zone
+ *  z_zlist_restore_zone_state - Return the zone to the state it was originally
+ *	in
+ *  z_zone_exec - Execute a Unix command in a specified zone and return results
+ *  z_zones_are_implemented - Determine if any zone operations can be performed
+ *  z_is_zone_branded - determine if zone has a non-native brand
+ *  z_is_zone_brand_in_list - determine if the zone's brand matches the
+ *      brand list passed in.
+ *  z_brands_are_implemented - determine if branded zones are implemented on
+ *			this system
+ */
+
+/*
+ * System includes
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <fcntl.h>
+#include <ctype.h>
+#include <sys/types.h>
+#include <sys/param.h>
+#include <sys/sysmacros.h>
+#include <string.h>
+#include <strings.h>
+#include <sys/stat.h>
+#include <stdarg.h>
+#include <limits.h>
+#include <errno.h>
+#include <time.h>
+#include <signal.h>
+#include <stropts.h>
+#include <wait.h>
+#include <zone.h>
+#include <sys/brand.h>
+#include <libintl.h>
+#include <locale.h>
+#include <libzonecfg.h>
+#include <libcontract.h>
+#include <sys/contract/process.h>
+#include <sys/ctfs.h>
+#include <assert.h>
+#include <dlfcn.h>
+#include <link.h>
+#include <time.h>
+
+/*
+ * local includes
+ */
+
+/*
+ * When _INSTZONES_LIB_Z_DEFINE_GLOBAL_DATA is defined,
+ * instzones_lib.h will define the z_global_data structure.
+ * Otherwise an extern to the structure is inserted.
+ */
+
+#define	_INSTZONES_LIB_Z_DEFINE_GLOBAL_DATA
+#include "instzones_lib.h"
+#include "zones_strings.h"
+
+/*
+ * Private structures
+ */
+
+#define	CLUSTER_BRAND_NAME	"cluster"
+
+/* maximum number of arguments to exec() call */
+
+#define	UUID_FORMAT	"%02d%02d%02d%03d-%02d%02d%02d%d-%016llx"
+
+/*
+ * Library Function Prototypes
+ */
+
+#define	streq(a, b) (strcmp((a), (b)) == 0)
+
+/*
+ * Local Function Prototypes
+ */
+
+/*
+ * global internal (private) declarations
+ */
+
+/*
+ * *****************************************************************************
+ * global external (public) functions
+ * *****************************************************************************
+ */
+
+/*
+ * Name:	z_create_zone_admin_file
+ * Description:	Given a location to create the file, and optionally an existing
+ *		administration file, generate an administration file that
+ *		can be used to perform "non-interactive" operations in a
+ *		non-global zone.
+ * Arguments:	a_zoneAdminFilename - pointer to string representing the
+ *			full path of zone admin file to create
+ *		a_userAdminFilename - pointer to string representing the path
+ *			to an existing "user" administration file - the
+ *			administration file created will contain the
+ *			settings contained in this file, modified as
+ *			appropriate to supress any interaction;
+ *			If this is == NULL then the administration file
+ *			created will not contain any extra settings
+ * Returns:	boolean_t
+ *			== B_TRUE - admin file created
+ *			== B_FALSE - failed to create admin file
+ */
+
+boolean_t
+z_create_zone_admin_file(char *a_zoneAdminFilename, char *a_userAdminFilename)
+{
+	FILE	*zFp;
+	FILE	*uFp = (FILE *)NULL;
+
+	/* entry assertions */
+
+	assert(a_zoneAdminFilename != NULL);
+	assert(*a_zoneAdminFilename != '\0');
+
+	/* create temporary zone admin file */
+
+	zFp = fopen(a_zoneAdminFilename, "w");
+	if (zFp == (FILE *)NULL) {
+		return (B_FALSE);
+	}
+
+	/* open user admin file if specified */
+
+	if (a_userAdminFilename != (char *)NULL) {
+		uFp = fopen(a_userAdminFilename, "r");
+	}
+
+	/* create default admin file for zone pkg ops if no user admin file */
+
+	if (uFp == (FILE *)NULL) {
+		/* create default admin file */
+		(void) fprintf(zFp, "action=nocheck\nauthentication=nocheck\n"
+		    "basedir=default\nconflict=nocheck\nidepend=nocheck\n"
+		    "instance=unique\npartial=nocheck\nrdepend=nocheck\n"
+		    "runlevel=nocheck\nsetuid=nocheck\nspace=nocheck\n"
+		    "mail=\n");
+	} else for (;;) {
+		/* copy user admin file substitute/change appropriate entries */
+		char	buf[LINE_MAX+1];
+		char	*p;
+
+		/* read next line of user admin file */
+
+		p = fgets(buf, sizeof (buf), uFp);
+		if (p == (char *)NULL) {
+			(void) fclose(uFp);
+			break;
+		}
+
+		/* modify / replace / accept as appropriate */
+
+		if (strncmp(buf, "instance=quit", 13) == 0) {
+			(void) fprintf(zFp, "%s", "instance=unique\n");
+			/*LINTED*/
+		} else if (strncmp(buf, "keystore=", 9) == 0) {
+		} else if (strncmp(buf, "action=", 7) == 0) {
+			(void) fprintf(zFp, "action=nocheck\n");
+		} else if (strncmp(buf, "authentication=", 15) == 0) {
+			(void) fprintf(zFp, "authentication=nocheck\n");
+		} else if (strncmp(buf, "conflict=", 9) == 0) {
+			(void) fprintf(zFp, "conflict=nocheck\n");
+		} else if (strncmp(buf, "idepend=", 8) == 0) {
+			(void) fprintf(zFp, "idepend=nocheck\n");
+		} else if (strncmp(buf, "mail=", 5) == 0) {
+			(void) fprintf(zFp, "mail=\n");
+		} else if (strncmp(buf, "partial=", 8) == 0) {
+			(void) fprintf(zFp, "partial=nocheck\n");
+		} else if (strncmp(buf, "rdepend=", 8) == 0) {
+			(void) fprintf(zFp, "rdepend=nocheck\n");
+		} else if (strncmp(buf, "runlevel=", 9) == 0) {
+			(void) fprintf(zFp, "runlevel=nocheck\n");
+		} else if (strncmp(buf, "setuid=", 7) == 0) {
+			(void) fprintf(zFp, "setuid=nocheck\n");
+		} else if (strncmp(buf, "space=", 6) == 0) {
+			(void) fprintf(zFp, "space=nocheck\n");
+		} else {
+			(void) fprintf(zFp, "%s", buf);
+		}
+	}
+
+	/* close admin file and return success */
+
+	(void) fclose(zFp);
+	return (B_TRUE);
+}
+
+/*
+ * Name:	z_brands_are_implemented
+ * Description:	Determine if any branded zones may be present
+ * Arguments:	void
+ * Returns:	boolean_t
+ *			== B_TRUE - branded zones are supported
+ *			== B_FALSE - branded zones are not supported
+ */
+
+boolean_t
+z_brands_are_implemented(void)
+{
+static	boolean_t	_brandsImplementedDetermined = B_FALSE;
+static	boolean_t	_brandsAreImplemented = B_FALSE;
+
+	/* if availability has not been determined, cache it now */
+
+	if (!_brandsImplementedDetermined) {
+		_brandsImplementedDetermined = B_TRUE;
+		_brandsAreImplemented = _z_brands_are_implemented();
+		if (_brandsAreImplemented) {
+			_z_echoDebug(DBG_BRANDS_ARE_IMPLEMENTED);
+		} else {
+			_z_echoDebug(DBG_BRANDS_NOT_IMPLEMENTED);
+		}
+	}
+
+	/* return cached answer */
+
+	return (_brandsAreImplemented);
+}
+
+/*
+ * Name:	z_free_zone_list
+ * Description:	free contents of zoneList_t object
+ * Arguments:	a_zlst - handle to zoneList_t object to free
+ * Returns:	void
+ */
+
+void
+z_free_zone_list(zoneList_t a_zlst)
+{
+	int	numzones;
+
+	/* ignore empty list */
+
+	if (a_zlst == (zoneList_t)NULL) {
+		return;
+	}
+
+	/* free each entry in the zone list */
+
+	for (numzones = 0; a_zlst[numzones]._zlName != (char *)NULL;
+	    numzones++) {
+		zoneListElement_t *zelm = &a_zlst[numzones];
+
+		/* free zone name string */
+
+		free(zelm->_zlName);
+
+		/* free zonepath string */
+
+		if (zelm->_zlPath != (char *)NULL) {
+			free(zelm->_zlPath);
+		}
+
+		/* free list of inherited package directories */
+
+		if (zelm->_zlInheritedDirs != (char **)NULL) {
+			int	n;
+
+			for (n = 0;
+			    (zelm->_zlInheritedDirs)[n] != (char *)NULL;
+			    n++) {
+				(void) free((zelm->_zlInheritedDirs)[n]);
+			}
+			(void) free(zelm->_zlInheritedDirs);
+		}
+	}
+
+	/* free handle to the list */
+
+	free(a_zlst);
+}
+
+/*
+ * Name:	z_get_nonglobal_zone_list
+ * Description: return zoneList_t object describing all non-global
+ *              native zones - branded zones are not included in list
+ * Arguments:	None.
+ * Returns:	zoneList_t
+ *			== NULL - error, list could not be generated
+ *			!= NULL - success, list returned
+ * NOTE:    	Any zoneList_t returned is placed in new storage for the
+ *		calling function. The caller must use 'z_free_zone_list' to
+ *		dispose of the storage once the list is no longer needed.
+ */
+
+zoneList_t
+z_get_nonglobal_zone_list(void)
+{
+	zoneList_t zones;
+	zoneBrandList_t *brands = NULL;
+
+	if ((brands = z_make_brand_list("native cluster", " ")) == NULL)
+		return (NULL);
+
+	zones = z_get_nonglobal_zone_list_by_brand(brands);
+
+	z_free_brand_list(brands);
+
+	return (zones);
+}
+
+/*
+ * Name:	z_free_brand_list
+ * Description: Free contents of zoneBrandList_t object
+ * Arguments:	brands - pointer to zoneBrandList_t object to free
+ * Returns: 	void
+ */
+void
+z_free_brand_list(zoneBrandList_t *brands)
+{
+	while (brands != NULL) {
+		zoneBrandList_t *temp = brands;
+		free(brands->string_ptr);
+		brands = brands->next;
+		free(temp);
+	}
+}
+
+/*
+ * Name:	z_make_brand_list
+ * Description:	Given a string with a list of brand name delimited by
+ *		the delimeter passed in, build a zoneBrandList_t structure
+ *		with the list of brand names and return it to the caller.
+ * Arguments:
+ *		brands - const char pointer to string list of brand names
+ *		delim - const char pointer to string representing the
+ *			delimeter for brands string.
+ * Returns:	zoneBrandList_t *
+ *			== NULL - error, list could not be generated
+ *			!= NULL - success, list returned
+ * NOTE:	Any zoneBrandList_t returned is placed in new storage for the
+ *		calling function.  The caller must use 'z_free_brand_list' to
+ *		dispose of the storage once the list is no longer needed.
+ */
+zoneBrandList_t *
+z_make_brand_list(const char *brands, const char *delim)
+{
+	zoneBrandList_t *brand = NULL, *head = NULL;
+	char		*blist = NULL;
+	char		*str = NULL;
+
+	if ((blist = strdup(brands)) == NULL)
+		return (NULL);
+
+	if ((str = strtok(blist, delim)) != NULL) {
+		if ((brand = (zoneBrandList_t *)
+		    malloc(sizeof (struct _zoneBrandList))) == NULL) {
+			return (NULL);
+		}
+
+		head = brand;
+		brand->string_ptr = strdup(str);
+		brand->next = NULL;
+
+		while ((str = strtok(NULL, delim)) != NULL) {
+			if ((brand->next = (zoneBrandList_t *)
+			    malloc(sizeof (struct _zoneBrandList))) == NULL) {
+				return (NULL);
+			}
+
+			brand = brand->next;
+			brand->string_ptr = strdup(str);
+			brand->next = NULL;
+		}
+	}
+
+	free(blist);
+	return (head);
+}
+
+/*
+ * Name:	z_get_nonglobal_zone_list_by_brand
+ * Description: return zoneList_t object describing all non-global
+ *              zones matching the list of brands passed in.
+ * Arguments:	brands - The list of zone brands to look for.
+ * Returns:	zoneList_t
+ *			== NULL - error, list could not be generated
+ *			!= NULL - success, list returned
+ * NOTE:    	Any zoneList_t returned is placed in new storage for the
+ *		calling function. The caller must use 'z_free_zone_list' to
+ *		dispose of the storage once the list is no longer needed.
+ */
+zoneList_t
+z_get_nonglobal_zone_list_by_brand(zoneBrandList_t *brands)
+{
+	FILE		*zoneIndexFP;
+	int		numzones = 0;
+	struct zoneent	*ze;
+	zoneList_t	zlst = NULL;
+	FILE		*mapFP;
+	char		zonename[ZONENAME_MAX];
+	zone_spec_t	*zent;
+
+	/* if zones are not implemented, return empty list */
+
+	if (!z_zones_are_implemented()) {
+		return ((zoneList_t)NULL);
+	}
+
+	/*
+	 * Open the zone index file.  Note that getzoneent_private() handles
+	 * NULL.
+	 */
+	zoneIndexFP = setzoneent();
+
+	mapFP = zonecfg_open_scratch("", B_FALSE);
+
+	/* index file open; scan all zones; see if any are at least installed */
+
+	while ((ze = getzoneent_private(zoneIndexFP)) != NULL) {
+		zone_state_t	st;
+
+		/* skip the global zone */
+
+		if (strcmp(ze->zone_name, GLOBAL_ZONENAME) == 0) {
+			free(ze);
+			continue;
+		}
+
+		/*
+		 * skip any zones with brands not on the brand list
+		 */
+		if (!z_is_zone_brand_in_list(ze->zone_name, brands)) {
+			free(ze);
+			continue;
+		}
+
+		/*
+		 * If the user specified an explicit zone list, then ignore any
+		 * zones that aren't on that list.
+		 */
+		if ((zent = _z_global_data._zone_spec) != NULL) {
+			while (zent != NULL) {
+				if (strcmp(zent->zl_name, ze->zone_name) == 0)
+					break;
+				zent = zent->zl_next;
+			}
+			if (zent == NULL) {
+				free(ze);
+				continue;
+			}
+		}
+
+		/* non-global zone: create entry for this zone */
+
+		if (numzones == 0) {
+			zlst = (zoneList_t)_z_calloc(
+			    sizeof (zoneListElement_t)*2);
+		} else {
+			zlst = (zoneList_t)_z_realloc(zlst,
+			    sizeof (zoneListElement_t)*(numzones+2));
+			(void) memset(&zlst[numzones], 0L,
+			    sizeof (zoneListElement_t)*2);
+		}
+
+		/*
+		 * remember the zone name, zonepath and the current
+		 * zone state of the zone.
+		 */
+		zlst[numzones]._zlName = _z_strdup(ze->zone_name);
+		zlst[numzones]._zlPath = _z_strdup(ze->zone_path);
+		zlst[numzones]._zlOrigInstallState = ze->zone_state;
+		zlst[numzones]._zlCurrInstallState = ze->zone_state;
+
+		/* get the zone kernel status */
+
+		if (zone_get_state(ze->zone_name, &st) != Z_OK) {
+			st = ZONE_STATE_INCOMPLETE;
+		}
+
+		_z_echoDebug(DBG_ZONES_NGZ_LIST_STATES,
+		    ze->zone_name, ze->zone_state, st);
+
+		/*
+		 * For a scratch zone, we need to know the kernel zone name.
+		 */
+		if (zonecfg_in_alt_root() && mapFP != NULL &&
+		    zonecfg_find_scratch(mapFP, ze->zone_name,
+		    zonecfg_get_root(), zonename, sizeof (zonename)) != -1) {
+			free(zlst[numzones]._zlScratchName);
+			zlst[numzones]._zlScratchName = _z_strdup(zonename);
+		}
+
+		/*
+		 * remember the current kernel status of the zone.
+		 */
+
+		zlst[numzones]._zlOrigKernelStatus = st;
+		zlst[numzones]._zlCurrKernelStatus = st;
+
+		zlst[numzones]._zlInheritedDirs =
+		    _z_get_inherited_dirs(ze->zone_name);
+
+		numzones++;
+		free(ze);
+	}
+
+	/* close the index file */
+	endzoneent(zoneIndexFP);
+
+	if (mapFP != NULL)
+		zonecfg_close_scratch(mapFP);
+
+	/* return generated list */
+
+	return (zlst);
+}
+
+/*
+ * Name:	z_get_zonename
+ * Description:	return the name of the current zone
+ * Arguments:	void
+ * Returns:	char *
+ *			- pointer to string representing the name of the current
+ *			zone
+ * NOTE:    	Any string returned is placed in new storage for the
+ *		calling function. The caller must use 'Free' to dispose
+ *		of the storage once the string is no longer needed.
+ */
+
+char *
+z_get_zonename(void)
+{
+	ssize_t		zonenameLen;
+	char		zonename[ZONENAME_MAX];
+	zoneid_t	zoneid = (zoneid_t)-1;
+
+	/* if zones are not implemented, return "" */
+
+	if (!z_zones_are_implemented()) {
+		return (_z_strdup(""));
+	}
+
+	/* get the zone i.d. of the current zone */
+
+	zoneid = getzoneid();
+
+	/* get the name of the current zone */
+
+	zonenameLen = getzonenamebyid(zoneid, zonename, sizeof (zonename));
+
+	/* return "" if could not get zonename */
+
+	if (zonenameLen < 1) {
+		return (_z_strdup(""));
+	}
+
+	return (_z_strdup(zonename));
+}
+
+/*
+ * Name:	z_global_only
+ * Description:	Determine if the global zone is only zone on the spec list.
+ * Arguments:	None
+ * Returns:	B_TRUE if global zone is the only zone on the list,
+ *		B_FALSE otherwise.
+ */
+
+boolean_t
+z_global_only(void)
+{
+	/* return true if zones are not implemented - treate as global zone */
+
+	if (!z_zones_are_implemented()) {
+		return (B_TRUE);
+	}
+
+	/* return true if this is the global zone */
+
+	if (_z_global_data._zone_spec != NULL &&
+	    _z_global_data._zone_spec->zl_next == NULL &&
+	    strcmp(_z_global_data._zone_spec->zl_name, GLOBAL_ZONENAME) == 0) {
+		return (B_TRUE);
+	}
+
+	/* return false - not the global zone */
+
+	return (B_FALSE);
+}
+
+/*
+ * Name:	z_lock_this_zone
+ * Description:	lock this zone
+ * Arguments:	a_lflags - [RO, *RO] - (ZLOCKS_T)
+ *			Flags indicating which locks to acquire
+ * Returns:	boolean_t
+ *			== B_TRUE - success specified locks acquired
+ *			== B_FALSE - failure specified locks not acquired
+ * NOTE: the lock objects for "this zone" are maintained internally.
+ */
+
+boolean_t
+z_lock_this_zone(ZLOCKS_T a_lflags)
+{
+	boolean_t	b;
+	char		*zoneName;
+	pid_t		pid = (pid_t)0;
+
+	/* entry assertions */
+
+	assert(a_lflags != ZLOCKS_NONE);
+
+	/* entry debugging info */
+
+	_z_echoDebug(DBG_ZONES_LCK_THIS, a_lflags);
+
+	zoneName = z_get_zonename();
+	pid = getpid();
+
+	/* lock zone administration */
+
+	if (a_lflags & ZLOCKS_ZONE_ADMIN) {
+		b = _z_lock_zone_object(&_z_global_data._z_ObjectLocks,
+		    zoneName, LOBJ_ZONEADMIN, pid,
+		    MSG_ZONES_LCK_THIS_ZONEADM,
+		    ERR_ZONES_LCK_THIS_ZONEADM);
+		if (!b) {
+			(void) free(zoneName);
+			return (B_FALSE);
+		}
+	}
+
+	/* lock package administration always */
+
+	if (a_lflags & ZLOCKS_PKG_ADMIN) {
+		b = _z_lock_zone_object(&_z_global_data._z_ObjectLocks,
+		    zoneName, LOBJ_PKGADMIN, pid,
+		    MSG_ZONES_LCK_THIS_PKGADM,
+		    ERR_ZONES_LCK_THIS_PKGADM);
+		if (!b) {
+			(void) z_unlock_this_zone(a_lflags);
+			(void) free(zoneName);
+			return (B_FALSE);
+		}
+	}
+
+	/* lock patch administration always */
+
+	if (a_lflags & ZLOCKS_PATCH_ADMIN) {
+		b = _z_lock_zone_object(&_z_global_data._z_ObjectLocks,
+		    zoneName, LOBJ_PATCHADMIN, pid,
+		    MSG_ZONES_LCK_THIS_PATCHADM,
+		    ERR_ZONES_LCK_THIS_PATCHADM);
+		if (!b) {
+			(void) z_unlock_this_zone(a_lflags);
+			(void) free(zoneName);
+			return (B_FALSE);
+		}
+	}
+
+	(void) free(zoneName);
+
+	return (B_TRUE);
+}
+
+/*
+ * Name:	z_lock_zones
+ * Description:	lock specified zones
+ * Arguments:	a_zlst - zoneList_t object describing zones to lock
+ *		a_lflags - [RO, *RO] - (ZLOCKS_T)
+ *			Flags indicating which locks to acquire
+ * Returns:	boolean_t
+ *			== B_TRUE - success, zones locked
+ *			== B_FALSE - failure, zones not locked
+ */
+
+boolean_t
+z_lock_zones(zoneList_t a_zlst, ZLOCKS_T a_lflags)
+{
+	boolean_t	b;
+	int		i;
+
+	/* entry assertions */
+
+	assert(a_lflags != ZLOCKS_NONE);
+
+	/* entry debugging info */
+
+	_z_echoDebug(DBG_ZONES_LCK_ZONES, a_lflags);
+
+	/* if zones are not implemented, return TRUE */
+
+	if (z_zones_are_implemented() == B_FALSE) {
+		_z_echoDebug(DBG_ZONES_LCK_ZONES_UNIMP);
+		return (B_TRUE);
+	}
+
+	/* lock this zone first before locking other zones */
+
+	b = z_lock_this_zone(a_lflags);
+	if (b == B_FALSE) {
+		return (b);
+	}
+
+	/* ignore empty list */
+
+	if (a_zlst == (zoneList_t)NULL) {
+		_z_echoDebug(DBG_ZONES_LCK_ZONES_NOZONES);
+		return (B_FALSE);
+	}
+
+	/* zones exist */
+
+	_z_echoDebug(DBG_ZONES_LCK_ZONES_EXIST);
+
+	/*
+	 * lock each listed zone that is currently running
+	 */
+
+	for (i = 0; (a_zlst[i]._zlName != (char *)NULL); i++) {
+		/* ignore zone if already locked */
+		if (a_zlst[i]._zlStatus & ZST_LOCKED) {
+			continue;
+		}
+
+		/* ignore zone if not running */
+		if (a_zlst[i]._zlCurrKernelStatus != ZONE_STATE_RUNNING &&
+		    a_zlst[i]._zlCurrKernelStatus != ZONE_STATE_MOUNTED) {
+			continue;
+		}
+
+		/*
+		 * mark zone locked - if interrupted out during lock, an attempt
+		 * will be made to release the lock
+		 */
+		a_zlst[i]._zlStatus |= ZST_LOCKED;
+
+		/* lock this zone */
+		b = _z_lock_zone(&a_zlst[i], a_lflags);
+
+		/* on failure unlock all zones and return error */
+		if (b != B_TRUE) {
+			_z_program_error(ERR_ZONES_LCK_ZONES_FAILED,
+			    a_zlst[i]._zlName);
+			(void) z_unlock_zones(a_zlst, a_lflags);
+			return (B_FALSE);
+		}
+	}
+
+	/* success */
+
+	return (B_TRUE);
+}
+
+/*
+ * Name:	z_mount_in_lz
+ * Description:	Mount global zone directory in specified zone's root file system
+ * Arguments:	r_lzMountPoint - pointer to handle to string - on success, the
+ *			full path to the mount point relative to the global zone
+ *			root file system is returned here - this is needed to
+ *			unmount the directory when it is no longer needed
+ *		r_lzRootPath - pointer to handle to string - on success, the
+ *			full path to the mount point relative to the specified
+ *			zone's root file system is returned here - this is
+ *			passed to any command executing in the specified zone to
+ *			access the directory mounted
+ *		a_zoneName - pointer to string representing the name of the zone
+ *			to mount the specified global zone directory in
+ *		a_gzPath - pointer to string representing the full absolute path
+ *			of the global zone directory to LOFS mount inside of the
+ *			specified non-global zone
+ *		a_mountPointPrefix - pointer to string representing the prefix
+ *			to be used when creating the mount point name in the
+ *			specified zone's root directory
+ * Returns:	boolean_t
+ *			== B_TRUE - global zone directory mounted successfully
+ *			== B_FALSE - failed to mount directory in specified zone
+ * NOTE:    	Any strings returned is placed in new storage for the
+ *		calling function. The caller must use 'Free' to dispose
+ *		of the storage once the strings are no longer needed.
+ */
+
+boolean_t
+z_mount_in_lz(char **r_lzMountPoint, char **r_lzRootPath, char *a_zoneName,
+	char *a_gzPath, char *a_mountPointPrefix)
+{
+	char		lzRootPath[MAXPATHLEN] = {'\0'};
+	char		uuid[MAXPATHLEN] = {'\0'};
+	char		gzMountPoint[MAXPATHLEN] = {'\0'};
+	char		lzMountPoint[MAXPATHLEN] = {'\0'};
+	hrtime_t	hretime;
+	int		err;
+	int		slen;
+	struct tm	tstruct;
+	time_t		thetime;
+	zoneid_t	zid;
+
+	/* entry assertions */
+
+	assert(a_zoneName != (char *)NULL);
+	assert(*a_zoneName != '\0');
+	assert(a_gzPath != (char *)NULL);
+	assert(*a_gzPath != '\0');
+	assert(r_lzMountPoint != (char **)NULL);
+	assert(r_lzRootPath != (char **)NULL);
+
+	/* entry debugging info */
+
+	_z_echoDebug(DBG_ZONES_MOUNT_IN_LZ_ENTRY, a_zoneName, a_gzPath);
+
+	/* reset returned non-global zone mount point path handle */
+
+	*r_lzMountPoint = (char *)NULL;
+	*r_lzRootPath = (char *)NULL;
+
+	/* if zones are not implemented, return FALSE */
+
+	if (z_zones_are_implemented() == B_FALSE) {
+		return (B_FALSE);
+	}
+
+	/* error if global zone path is not absolute */
+
+	if (*a_gzPath != '/') {
+		_z_program_error(ERR_GZPATH_NOT_ABSOLUTE, a_gzPath);
+		return (B_FALSE);
+	}
+
+	/* error if global zone path does not exist */
+
+	if (_z_is_directory(a_gzPath) != 0) {
+		_z_program_error(ERR_GZPATH_NOT_DIR, a_gzPath, strerror(errno));
+		return (B_FALSE);
+	}
+
+	/* verify that specified non-global zone exists */
+
+	err = zone_get_id(a_zoneName, &zid);
+	if (err != Z_OK) {
+		_z_program_error(ERR_GET_ZONEID, a_zoneName,
+		    zonecfg_strerror(err));
+		return (B_FALSE);
+	}
+
+	/* obtain global zone path to non-global zones root file system */
+
+	err = zone_get_rootpath(a_zoneName, lzRootPath, sizeof (lzRootPath));
+	if (err != Z_OK) {
+		_z_program_error(ERR_NO_ZONE_ROOTPATH, a_zoneName,
+		    zonecfg_strerror(err));
+		return (B_FALSE);
+	}
+
+	if (lzRootPath[0] == '\0') {
+		_z_program_error(ERR_ROOTPATH_EMPTY, a_zoneName);
+		return (B_FALSE);
+	}
+
+	/*
+	 * lofs resolve the non-global zone's root path first in case
+	 * its in a path that's been lofs mounted read-only.
+	 * (e.g. This happens when we're tyring to patch a zone in an ABE
+	 * that lives on a filesystem that the ABE shares with the currently
+	 * running BE.)
+	 */
+	z_resolve_lofs(lzRootPath, sizeof (lzRootPath));
+
+	/* verify that the root path exists */
+
+	if (_z_is_directory(lzRootPath) != 0) {
+		_z_program_error(ERR_LZROOT_NOTDIR, lzRootPath,
+		    strerror(errno));
+		return (B_FALSE);
+	}
+
+	/*
+	 * generate a unique key - the key is the same length as unique uid
+	 * but contains different information that is as unique as can be made;
+	 * include current hires time (nanosecond real timer). Such a unique
+	 * i.d. will look like:
+	 *		0203104092-1145345-0004e94d6af481a0
+	 */
+
+	hretime = gethrtime();
+
+	thetime = time((time_t *)NULL);
+	(void) localtime_r(&thetime, &tstruct);
+
+	slen = snprintf(uuid, sizeof (uuid),
+	    UUID_FORMAT,
+	    tstruct.tm_mday, tstruct.tm_mon, tstruct.tm_year,
+	    tstruct.tm_yday, tstruct.tm_hour, tstruct.tm_min,
+	    tstruct.tm_sec,	tstruct.tm_wday, hretime);
+	if (slen > sizeof (uuid)) {
+		_z_program_error(ERR_GZMOUNT_SNPRINTFUUID_FAILED,
+		    UUID_FORMAT, sizeof (uuid));
+		return (B_FALSE);
+	}
+
+	/* create the global zone mount point */
+
+	slen = snprintf(gzMountPoint, sizeof (gzMountPoint), "%s/.SUNW_%s_%s",
+	    lzRootPath,
+	    a_mountPointPrefix ? a_mountPointPrefix : "zones", uuid);
+	if (slen > sizeof (gzMountPoint)) {
+		_z_program_error(ERR_GZMOUNT_SNPRINTFGMP_FAILED,
+		    "%s/.SUNW_%s_%s", lzRootPath,
+		    a_mountPointPrefix ? a_mountPointPrefix : "zones",
+		    uuid, sizeof (gzMountPoint));
+		return (B_FALSE);
+	}
+
+	slen = snprintf(lzMountPoint, sizeof (lzMountPoint), "%s",
+	    gzMountPoint+strlen(lzRootPath));
+	if (slen > sizeof (lzMountPoint)) {
+		_z_program_error(ERR_GZMOUNT_SNPRINTFLMP_FAILED,
+		    "%s", gzMountPoint+strlen(lzRootPath),
+		    sizeof (lzMountPoint));
+		return (B_FALSE);
+	}
+
+	_z_echoDebug(DBG_MNTPT_NAMES, a_gzPath, a_zoneName, gzMountPoint,
+	    lzMountPoint);
+
+	/* error if the mount point already exists */
+
+	if (_z_is_directory(gzMountPoint) == 0) {
+		_z_program_error(ERR_ZONEROOT_NOTDIR, gzMountPoint,
+		    a_zoneName, strerror(errno));
+		return (B_FALSE);
+	}
+
+	/* create the temporary mount point */
+
+	if (mkdir(gzMountPoint, 0600) != 0) {
+		_z_program_error(ERR_MNTPT_MKDIR, gzMountPoint, a_zoneName,
+		    strerror(errno));
+		return (B_FALSE);
+	}
+
+	/* mount the global zone path on the non-global zone root file system */
+
+	err = mount(a_gzPath, gzMountPoint, MS_RDONLY|MS_DATA, "lofs",
+	    (char *)NULL, 0, (char *)NULL, 0);
+	if (err != 0) {
+		_z_program_error(ERR_GZMOUNT_FAILED, a_gzPath,
+		    gzMountPoint, a_zoneName, strerror(errno));
+		return (B_FALSE);
+	}
+
+	/* success - return both mountpoints to caller */
+
+	*r_lzMountPoint = _z_strdup(gzMountPoint);
+
+	*r_lzRootPath = _z_strdup(lzMountPoint);
+
+	/* return success */
+
+	return (B_TRUE);
+}
+
+/*
+ * Name:	z_non_global_zones_exist
+ * Description:	Determine if any non-global native zones exist
+ * Arguments:	None.
+ * Returns:	boolean_t
+ *	== B_TRUE - at least one non-global native zone exists
+ *	== B_FALSE - no non-global native zone exists
+ */
+
+boolean_t
+z_non_global_zones_exist(void)
+{
+	FILE		*zoneIndexFP;
+	boolean_t	anyExist = B_FALSE;
+	struct zoneent	*ze;
+	zone_spec_t	*zent;
+
+	/* if zones are not implemented, return FALSE */
+
+	if (z_zones_are_implemented() == B_FALSE) {
+		return (B_FALSE);
+	}
+
+	/* determine if any zones are configured */
+	zoneIndexFP = setzoneent();
+	if (zoneIndexFP == NULL) {
+		return (B_FALSE);
+	}
+
+	/* index file open; scan all zones; see if any are at least installed */
+
+	while ((ze = getzoneent_private(zoneIndexFP)) != NULL) {
+		/*
+		 * If the user specified an explicit zone list, then ignore any
+		 * zones that aren't on that list.
+		 */
+		if ((zent = _z_global_data._zone_spec) != NULL) {
+			while (zent != NULL) {
+				if (strcmp(zent->zl_name, ze->zone_name) == 0)
+					break;
+				zent = zent->zl_next;
+			}
+			if (zent == NULL) {
+				free(ze);
+				continue;
+			}
+		}
+
+		/* skip the global zone */
+		if (strcmp(ze->zone_name, GLOBAL_ZONENAME) == 0) {
+			free(ze);
+			continue;
+		}
+
+		/* skip any branded zones */
+		if (z_is_zone_branded(ze->zone_name)) {
+			free(ze);
+			continue;
+		}
+
+		/* is this zone installed? */
+		if (ze->zone_state >= ZONE_STATE_INSTALLED) {
+			free(ze);
+			anyExist = B_TRUE;
+			break;
+		}
+		free(ze);
+	}
+
+	/* close the index file */
+
+	endzoneent(zoneIndexFP);
+
+	/* return results */
+
+	return (anyExist);
+}
+
+/*
+ * Name:	z_on_zone_spec
+ * Description:	Determine if named zone is on the zone_spec list.
+ * Arguments:	Pointer to name to test.
+ * Returns:	B_TRUE if named zone is on the list or if the user specified
+ *		no list at all (all zones is the default), B_FALSE otherwise.
+ */
+
+boolean_t
+z_on_zone_spec(const char *zonename)
+{
+	zone_spec_t	*zent;
+
+	/* entry assertions */
+
+	assert(zonename != NULL);
+	assert(*zonename != '\0');
+
+	/* return true if zones not implemented or no zone spec list defined */
+
+	if (!z_zones_are_implemented() || _z_global_data._zone_spec == NULL) {
+		return (B_TRUE);
+	}
+
+	/* return true if named zone is on the zone spec list */
+
+	for (zent = _z_global_data._zone_spec;
+	    zent != NULL; zent = zent->zl_next) {
+		if (strcmp(zent->zl_name, zonename) == 0)
+			return (B_TRUE);
+	}
+
+	/* named zone is not on the zone spec list */
+
+	return (B_FALSE);
+}
+
+/*
+ * Name:	z_running_in_global_zone
+ * Description:	Determine if running in the "global" zone
+ * Arguments:	void
+ * Returns:	boolean_t
+ *			== B_TRUE - running in global zone
+ *			== B_FALSE - not running in global zone
+ */
+
+boolean_t
+z_running_in_global_zone(void)
+{
+	static	boolean_t	_zoneIdDetermined = B_FALSE;
+	static	boolean_t	_zoneIsGlobal = B_FALSE;
+
+	/* if ID has not been determined, cache it now */
+
+	if (!_zoneIdDetermined) {
+		_zoneIdDetermined = B_TRUE;
+		_zoneIsGlobal = _z_running_in_global_zone();
+	}
+
+	return (_zoneIsGlobal);
+}
+
+/*
+ * Name:	z_set_output_functions
+ * Description:	Link program specific output functions to this library.
+ * Arguments:	a_echo_fcn - (_z_printf_fcn_t)
+ *			Function to call to cause "normal operation" messages
+ *			to be output/displayed
+ *		a_echo_debug_fcn - (_z_printf_fcn_t)
+ *			Function to call to cause "debugging" messages
+ *			to be output/displayed
+ *		a_progerr_fcn - (_z_printf_fcn_t)
+ *			Function to call to cause "program error" messages
+ *			to be output/displayed
+ * Returns:	void
+ * NOTE:	If NULL is specified for any function, then the functionality
+ *		associated with that function is disabled.
+ * NOTE:	The function pointers provided must call a function that
+ *		takes two arguments:
+ *			function(char *format, char *message)
+ *		Any registered function will be called like:
+ *			function("%s", "message")
+ */
+
+void
+z_set_output_functions(_z_printf_fcn_t a_echo_fcn,
+    _z_printf_fcn_t a_echo_debug_fcn,
+    _z_printf_fcn_t a_progerr_fcn)
+{
+	_z_global_data._z_echo = a_echo_fcn;
+	_z_global_data._z_echo_debug = a_echo_debug_fcn;
+	_z_global_data._z_progerr = a_progerr_fcn;
+}
+
+/*
+ * Name:	z_set_zone_root
+ * Description:	Set root for zones library operations
+ * Arguments:	Path to root of boot environment containing zone; must be
+ *		absolute.
+ * Returns:	None.
+ * NOTE:	Must be called before performing any zone-related operations.
+ *		(Currently called directly by set_inst_root() during -R
+ *		argument handling.)
+ */
+
+void
+z_set_zone_root(const char *zroot)
+{
+	char *rootdir;
+
+	/* if zones are not implemented, just return */
+
+	if (!z_zones_are_implemented())
+		return;
+
+	/* entry assertions */
+
+	assert(zroot != NULL);
+
+	rootdir = _z_strdup((char *)zroot);
+	z_canoninplace(rootdir);
+
+	if (strcmp(rootdir, "/") == 0) {
+		rootdir[0] = '\0';
+	}
+
+	/* free any existing cached root path */
+
+	if (*_z_global_data._z_root_dir != '\0') {
+		free(_z_global_data._z_root_dir);
+	}
+
+	/* store duplicate of new zone root path */
+
+	if (*rootdir != '\0') {
+		_z_global_data._z_root_dir = _z_strdup(rootdir);
+	} else {
+		*_z_global_data._z_root_dir = '\0';
+	}
+
+	/* set zone root path */
+
+	zonecfg_set_root(rootdir);
+
+	free(rootdir);
+}
+
+/*
+ * Name:	z_set_zone_spec
+ * Description:	Set list of zones on which actions will be performed.
+ * Arguments:	Whitespace-separated list of zone names.
+ * Returns:	0 on success, -1 on error.
+ * NOTES:	Will call _z_program_error if argument can't be parsed or
+ *		memory not available.
+ */
+
+int
+z_set_zone_spec(const char *zlist)
+{
+	const char	*zend;
+	ptrdiff_t	zlen;
+	zone_spec_t	*zent;
+	zone_spec_t	*zhead;
+	zone_spec_t	**znextp = &zhead;
+
+	/* entry assertions */
+
+	assert(zlist != NULL);
+
+	/* parse list to zone_spec_t list, store in global data */
+
+	for (;;) {
+		while (isspace(*zlist)) {
+			zlist++;
+		}
+		if (*zlist == '\0') {
+			break;
+		}
+		for (zend = zlist; *zend != '\0'; zend++) {
+			if (isspace(*zend)) {
+				break;
+			}
+		}
+		zlen = ((ptrdiff_t)zend) - ((ptrdiff_t)zlist);
+		if (zlen >= ZONENAME_MAX) {
+			_z_program_error(ERR_ZONE_NAME_ILLEGAL, zlen, zlist);
+			return (-1);
+		}
+		zent = _z_malloc(sizeof (*zent));
+		(void) memcpy(zent->zl_name, zlist, zlen);
+		zent->zl_name[zlen] = '\0';
+		zent->zl_used = B_FALSE;
+		*znextp = zent;
+		znextp = &zent->zl_next;
+		zlist = zend;
+	}
+	*znextp = NULL;
+
+	if (zhead == NULL) {
+		_z_program_error(ERR_ZONE_LIST_EMPTY);
+		return (-1);
+	}
+
+	_z_global_data._zone_spec = zhead;
+	return (0);
+}
+
+/*
+ * Name:	z_umount_lz_mount
+ * Description:	Unmount directory mounted with z_mount_in_lz
+ * Arguments:	a_lzMountPointer - pointer to string returned by z_mount_in_lz
+ * Returns:	boolean_t
+ *			== B_TRUE - successfully unmounted directory
+ *			== B_FALSE - failed to unmount directory
+ */
+
+boolean_t
+z_umount_lz_mount(char *a_lzMountPoint)
+{
+	int	err;
+
+	/* entry assertions */
+
+	assert(a_lzMountPoint != (char *)NULL);
+	assert(*a_lzMountPoint != '\0');
+
+	/* entry debugging info */
+
+	_z_echoDebug(DBG_ZONES_UNMOUNT_FROM_LZ_ENTRY, a_lzMountPoint);
+
+	/* if zones are not implemented, return TRUE */
+
+	if (z_zones_are_implemented() == B_FALSE) {
+		return (B_FALSE);
+	}
+
+	/* error if global zone path is not absolute */
+
+	if (*a_lzMountPoint != '/') {
+		_z_program_error(ERR_LZMNTPT_NOT_ABSOLUTE, a_lzMountPoint);
+		return (B_FALSE);
+	}
+
+	/* verify mount point exists */
+
+	if (_z_is_directory(a_lzMountPoint) != 0) {
+		_z_program_error(ERR_LZMNTPT_NOTDIR, a_lzMountPoint,
+		    strerror(errno));
+		return (B_FALSE);
+	}
+
+	/* unmount */
+
+	err = umount2(a_lzMountPoint, 0);
+	if (err != 0) {
+		_z_program_error(ERR_GZUMOUNT_FAILED, a_lzMountPoint,
+		    strerror(errno));
+		return (B_FALSE);
+	}
+
+	/* remove the mount point */
+
+	(void) remove(a_lzMountPoint);
+
+	/* return success */
+
+	return (B_TRUE);
+}
+
+/*
+ * Name:	z_unlock_this_zone
+ * Description:	unlock this zone
+ * Arguments:	a_lflags - [RO, *RO] - (ZLOCKS_T)
+ *			Flags indicating which locks to release
+ * Returns:	boolean_t
+ *			== B_TRUE - success specified locks released
+ *			== B_FALSE - failure specified locks may not be released
+ * NOTE: the lock objects for "this zone" are maintained internally.
+ */
+
+boolean_t
+z_unlock_this_zone(ZLOCKS_T a_lflags)
+{
+	boolean_t	b;
+	boolean_t	errors = B_FALSE;
+	char		*zoneName;
+
+	/* entry assertions */
+
+	assert(a_lflags != ZLOCKS_NONE);
+
+	/* entry debugging info */
+
+	_z_echoDebug(DBG_ZONES_ULK_THIS, a_lflags);
+
+	/* return if no objects locked */
+
+	if ((_z_global_data._z_ObjectLocks == (char *)NULL) ||
+	    (*_z_global_data._z_ObjectLocks == '\0')) {
+		return (B_TRUE);
+	}
+
+	zoneName = z_get_zonename();
+
+	/* unlock patch administration */
+
+	if (a_lflags & ZLOCKS_PATCH_ADMIN) {
+		b = _z_unlock_zone_object(&_z_global_data._z_ObjectLocks,
+		    zoneName, LOBJ_PATCHADMIN, ERR_ZONES_ULK_THIS_PATCH);
+		if (!b) {
+			errors = B_TRUE;
+		}
+	}
+
+	/* unlock package administration */
+
+	if (a_lflags & ZLOCKS_PKG_ADMIN) {
+		b = _z_unlock_zone_object(&_z_global_data._z_ObjectLocks,
+		    zoneName, LOBJ_PKGADMIN, ERR_ZONES_ULK_THIS_PACKAGE);
+		if (!b) {
+			errors = B_TRUE;
+		}
+	}
+
+	/* unlock zone administration */
+
+	if (a_lflags & ZLOCKS_ZONE_ADMIN) {
+		b = _z_unlock_zone_object(&_z_global_data._z_ObjectLocks,
+		    zoneName, LOBJ_ZONEADMIN, ERR_ZONES_ULK_THIS_ZONES);
+		if (!b) {
+			errors = B_TRUE;
+		}
+	}
+
+	(void) free(zoneName);
+	return (!errors);
+}
+
+/*
+ * Name:	z_unlock_zones
+ * Description:	unlock specified zones
+ * Arguments:	a_zlst - zoneList_t object describing zones to unlock
+ *		a_lflags - [RO, *RO] - (ZLOCKS_T)
+ *			Flags indicating which locks to release
+ * Returns:	boolean_t
+ *			== B_TRUE - success, zones unlocked
+ *			== B_FALSE - failure, zones not unlocked
+ */
+
+boolean_t
+z_unlock_zones(zoneList_t a_zlst, ZLOCKS_T a_lflags)
+{
+	boolean_t	b;
+	boolean_t	errors = B_FALSE;
+	int		i;
+
+	/* entry assertions */
+
+	assert(a_lflags != ZLOCKS_NONE);
+
+	/* entry debugging info */
+
+	_z_echoDebug(DBG_ZONES_ULK_ZONES, a_lflags);
+
+	/* if zones are not implemented, return TRUE */
+
+	if (z_zones_are_implemented() == B_FALSE) {
+		_z_echoDebug(DBG_ZONES_ULK_ZONES_UNIMP);
+		return (B_TRUE);
+	}
+
+	/* ignore empty list */
+
+	if (a_zlst == (zoneList_t)NULL) {
+		_z_echoDebug(DBG_ZONES_ULK_ZONES_NOZONES);
+		/* unlock this zone before returning */
+		return (z_unlock_this_zone(a_lflags));
+	}
+
+	/* zones exist */
+
+	_z_echoDebug(DBG_ZONES_ULK_ZONES_EXIST);
+
+	/*
+	 * unlock each listed zone that is currently running
+	 */
+
+	for (i = 0; (a_zlst[i]._zlName != (char *)NULL); i++) {
+		/* ignore zone if not locked */
+		if (!(a_zlst[i]._zlStatus & ZST_LOCKED)) {
+			continue;
+		}
+
+		/* ignore zone if not running */
+		if (a_zlst[i]._zlCurrKernelStatus != ZONE_STATE_RUNNING &&
+		    a_zlst[i]._zlCurrKernelStatus != ZONE_STATE_MOUNTED) {
+			continue;
+		}
+
+		/* unlock this zone */
+		b = _z_unlock_zone(&a_zlst[i], a_lflags);
+
+		if (b != B_TRUE) {
+			errors = B_TRUE;
+		} else {
+			/* mark zone as unlocked */
+			a_zlst[i]._zlStatus &= ~ZST_LOCKED;
+		}
+	}
+
+	/* unlock this zone */
+
+	if (z_unlock_this_zone(a_lflags) != B_TRUE) {
+		errors = B_TRUE;
+	}
+
+	return (errors);
+}
+
+/*
+ * Name:	z_verify_zone_spec
+ * Description:	Verify list of zones on which actions will be performed.
+ * Arguments:	None.
+ * Returns:	0 on success, -1 on error.
+ * NOTES:	Will call _z_program_error if there are zones on the specified
+ *		list that don't exist on the system. Requires that
+ *		z_set_zone_root is called first (if it is called at all).
+ */
+
+int
+z_verify_zone_spec(void)
+{
+	FILE		*zoneIndexFP;
+	boolean_t	errors;
+	char		zoneIndexPath[MAXPATHLEN];
+	struct zoneent	*ze;
+	zone_spec_t	*zent;
+
+	if (!z_zones_are_implemented()) {
+		_z_program_error(ERR_ZONES_NOT_IMPLEMENTED);
+		return (-1);
+	}
+
+	zoneIndexFP = setzoneent();
+	if (zoneIndexFP == NULL) {
+		_z_program_error(ERR_ZONEINDEX_OPEN, zoneIndexPath,
+		    strerror(errno));
+		return (-1);
+	}
+
+	while ((ze = getzoneent_private(zoneIndexFP)) != NULL) {
+		for (zent = _z_global_data._zone_spec;
+		    zent != NULL; zent = zent->zl_next) {
+			if (strcmp(zent->zl_name, ze->zone_name) == 0) {
+				zent->zl_used = B_TRUE;
+				break;
+			}
+		}
+		free(ze);
+	}
+	endzoneent(zoneIndexFP);
+
+	errors = B_FALSE;
+	for (zent = _z_global_data._zone_spec;
+	    zent != NULL; zent = zent->zl_next) {
+		if (!zent->zl_used) {
+			_z_program_error(ERR_ZONE_NONEXISTENT, zent->zl_name);
+			errors = B_TRUE;
+		}
+	}
+	return (errors ? -1 : 0);
+}
+
+/*
+ * Name:	z_zlist_change_zone_state
+ * Description:	Change the current state of the specified zone
+ * Arguments:	a_zlst - handle to zoneList_t object describing all zones
+ *		a_zoneIndex - index into a_zlst of the zone to return the
+ *		a_newState - the state to put the specified zone in
+ * Returns:	boolean_t
+ *			== B_TRUE - the zone is in the new state
+ *			== B_FALSE - unable to transition the zone to the
+ *				specified state
+ * NOTE:	This changes the "current kernel" state of the specified
+ *		zone. For example, to boot the zone, change the state
+ *		to "ZONE_STATE_RUNNING". To halt the zone, change the
+ *		state to "ZONE_STATE_INSTALLED".
+ */
+
+boolean_t
+z_zlist_change_zone_state(zoneList_t a_zlst, int a_zoneIndex,
+	zone_state_t a_newState)
+{
+	int	i;
+
+	/* entry debugging info */
+
+	_z_echoDebug(DBG_ZONES_CHG_Z_STATE_ENTRY, a_zoneIndex, a_newState);
+
+	/* ignore empty list */
+
+	if (a_zlst == (zoneList_t)NULL) {
+		return (B_FALSE);
+	}
+
+	/* find the specified zone in the list */
+
+	for (i = 0; (i != a_zoneIndex) &&
+	    (a_zlst[i]._zlName != (char *)NULL); i++)
+		;
+
+	/* return error if the specified zone does not exist */
+
+	if (a_zlst[i]._zlName == (char *)NULL) {
+		return (B_FALSE);
+	}
+
+	/* return success if the zone is already in this state */
+
+	if (a_zlst[i]._zlCurrKernelStatus == a_newState) {
+		return (B_TRUE);
+	}
+
+	/* take action on new state to set zone to */
+
+	_z_echoDebug(DBG_ZONES_CHG_Z_STATE, a_zlst[i]._zlName,
+	    a_zlst[i]._zlCurrKernelStatus, a_newState);
+
+	switch (a_newState) {
+	case ZONE_STATE_RUNNING:
+	case ZONE_STATE_MOUNTED:
+		/* these states mean "boot the zone" */
+		return (_z_make_zone_running(&a_zlst[i]));
+
+	case ZONE_STATE_DOWN:
+	case ZONE_STATE_INSTALLED:
+		/* these states mean "halt the zone" */
+		return (_z_make_zone_down(&a_zlst[i]));
+
+	case ZONE_STATE_READY:
+		return (_z_make_zone_ready(&a_zlst[i]));
+
+	case ZONE_STATE_CONFIGURED:
+	case ZONE_STATE_INCOMPLETE:
+	case ZONE_STATE_SHUTTING_DOWN:
+	default:
+		/* do not know how to change zone to this state */
+		return (B_FALSE);
+	}
+}
+
+/*
+ * Name:	z_is_zone_branded
+ * Description:	Determine whether zone has a non-native brand
+ * Arguments:	a_zoneName - name of the zone to check for branding
+ * Returns:	boolean_t
+ *			== B_TRUE - zone has a non-native brand
+ *			== B_FALSE - zone is native
+ */
+boolean_t
+z_is_zone_branded(char *zoneName)
+{
+	char			brandname[MAXNAMELEN];
+	int			err;
+
+	/* if zones are not implemented, return FALSE */
+	if (!z_zones_are_implemented()) {
+		return (B_FALSE);
+	}
+
+	/* if brands are not implemented, return FALSE */
+	if (!z_brands_are_implemented()) {
+		return (B_FALSE);
+	}
+
+	err = zone_get_brand(zoneName, brandname, sizeof (brandname));
+	if (err != Z_OK) {
+		_z_program_error(ERR_BRAND_GETBRAND, zonecfg_strerror(err));
+		return (B_FALSE);
+	}
+
+	/*
+	 * Both "native" and "cluster" are native brands
+	 * that use the standard facilities in the areas
+	 * of packaging/installation/patching/update.
+	 */
+	if (streq(brandname, NATIVE_BRAND_NAME) ||
+	    streq(brandname, CLUSTER_BRAND_NAME)) {
+		return (B_FALSE);
+	} else {
+		return (B_TRUE);
+	}
+}
+
+/*
+ * Name:	z_is_zone_brand_in_list
+ * Description:	Determine whether zone's brand has a match in the list
+ *              brands passed in.
+ * Arguments:	zoneName - name of the zone to check for branding
+ *              list - list of brands to check the zone against
+ * Returns:	boolean_t
+ *			== B_TRUE - zone has a matching brand
+ *			== B_FALSE - zone brand is not in list
+ */
+boolean_t
+z_is_zone_brand_in_list(char *zoneName, zoneBrandList_t *list)
+{
+	char			brandname[MAXNAMELEN];
+	int			err;
+	zoneBrandList_t		*sp;
+
+	if (zoneName == NULL || list == NULL)
+		return (B_FALSE);
+
+	/* if zones are not implemented, return FALSE */
+	if (!z_zones_are_implemented()) {
+		return (B_FALSE);
+	}
+
+	/* if brands are not implemented, return FALSE */
+	if (!z_brands_are_implemented()) {
+		return (B_FALSE);
+	}
+
+	err = zone_get_brand(zoneName, brandname, sizeof (brandname));
+	if (err != Z_OK) {
+		_z_program_error(ERR_BRAND_GETBRAND, zonecfg_strerror(err));
+		return (B_FALSE);
+	}
+
+	for (sp = list; sp != NULL; sp = sp->next) {
+		if (sp->string_ptr != NULL &&
+		    strcmp(sp->string_ptr, brandname) == 0) {
+			return (B_TRUE);
+		}
+	}
+
+	return (B_FALSE);
+}
+
+/*
+ * Name:	z_zlist_get_current_state
+ * Description:	Determine the current kernel state of the specified zone
+ * Arguments:	a_zlst - handle to zoneList_t object describing all zones
+ *		a_zoneIndex - index into a_zlst of the zone to return
+ * Returns:	zone_state_t
+ *			The current state of the specified zone is returned
+ */
+
+zone_state_t
+z_zlist_get_current_state(zoneList_t a_zlst, int a_zoneIndex)
+{
+	int	i;
+
+	/* ignore empty list */
+
+	if (a_zlst == (zoneList_t)NULL) {
+		return (ZONE_STATE_INCOMPLETE);
+	}
+
+	/* find the specified zone in the list */
+
+	for (i = 0; (i != a_zoneIndex) &&
+	    (a_zlst[i]._zlName != (char *)NULL); i++)
+		;
+
+	/* return error if the specified zone does not exist */
+
+	if (a_zlst[i]._zlName == (char *)NULL) {
+		return (ZONE_STATE_INCOMPLETE);
+	}
+
+	/* return selected zone's current kernel state */
+
+	_z_echoDebug(DBG_ZONES_GET_ZONE_STATE,
+	    a_zlst[i]._zlName ? a_zlst[i]._zlName : "",
+	    a_zlst[i]._zlCurrKernelStatus);
+
+	return (a_zlst[i]._zlCurrKernelStatus);
+}
+
+/*
+ * Name:	z_zlist_get_inherited_pkg_dirs
+ * Description:	Determine directories inherited by specified zone
+ * Arguments:	a_zlst - handle to zoneList_t object describing all zones
+ *		a_zoneIndex - index into a_zlst of the zone to return the
+ *			inherited directories list
+ * Returns:	char **
+ *			== NULL - zone does not inherit any directories
+ *				- zone index is invalid
+ *			!= NULL - array of inherited directories
+ * NOTE:    	Any directory list returned is located in static storage that
+ *		must NEVER be free()ed by the caller.
+ */
+
+extern char **
+z_zlist_get_inherited_pkg_dirs(zoneList_t a_zlst, int a_zoneIndex)
+{
+	int	i;
+
+	/* if zones are not implemented, return empty list */
+
+	if (z_zones_are_implemented() == B_FALSE) {
+		return (NULL);
+	}
+
+	/* ignore empty list */
+
+	if (a_zlst == (zoneList_t)NULL) {
+		return (NULL);
+	}
+
+	/* find the specified zone in the list */
+
+	for (i = 0; (i != a_zoneIndex) &&
+	    (a_zlst[i]._zlName != (char *)NULL); i++)
+		;
+
+	/* return error if the specified zone does not exist */
+
+	if (a_zlst[i]._zlName == (char *)NULL) {
+		return (NULL);
+	}
+
+	/* return selected zone's inherited directories */
+
+	return (a_zlst[i]._zlInheritedDirs);
+}
+
+/*
+ * Name:	z_zlist_get_original_state
+ * Description:	Return the original kernal state of the specified zone
+ * Arguments:	a_zlst - handle to zoneList_t object describing all zones
+ *		a_zoneIndex - index into a_zlst of the zone to return the
+ * Returns:	zone_state_t
+ *			The original state of the specified zone is returned.
+ *			This is the state of the zone when the zoneList_t
+ *			object was first generated.
+ */
+
+zone_state_t
+z_zlist_get_original_state(zoneList_t a_zlst, int a_zoneIndex)
+{
+	int	i;
+
+	/* ignore empty list */
+
+	if (a_zlst == (zoneList_t)NULL) {
+		return (ZONE_STATE_INCOMPLETE);
+	}
+
+	/* find the specified zone in the list */
+
+	for (i = 0; (i != a_zoneIndex) &&
+	    (a_zlst[i]._zlName != (char *)NULL); i++)
+		;
+
+	/* return error if the specified zone does not exist */
+
+	if (a_zlst[i]._zlName == (char *)NULL) {
+		return (ZONE_STATE_INCOMPLETE);
+	}
+
+	/* return selected zone's original kernel state */
+
+	return (a_zlst[i]._zlOrigKernelStatus);
+}
+
+/*
+ * Name:	z_zlist_get_scratch
+ * Description:	Determine name of scratch zone
+ * Arguments:	a_zlst - handle to zoneList_t object describing all zones
+ *		a_zoneIndex - index into a_zlst of the zone to use
+ * Return:	char *
+ *			== NULL - zone name could not be determined
+ *			!= NULL - pointer to string representing scratch zone
+ * NOTE:    	Any name returned is placed in static storage that must
+ *		NEVER be free()ed by the caller.
+ */
+
+char *
+z_zlist_get_scratch(zoneList_t a_zlst, int a_zoneIndex)
+{
+	int	i;
+
+	/* ignore empty list */
+
+	if (a_zlst == NULL)
+		return (NULL);
+
+	/* find the specified zone in the list */
+
+	for (i = 0; i != a_zoneIndex; i++) {
+		if (a_zlst[i]._zlName == NULL)
+			return (NULL);
+	}
+
+	/* return selected zone's scratch name */
+
+	return (a_zlst[i]._zlScratchName == NULL ? a_zlst[i]._zlName :
+	    a_zlst[i]._zlScratchName);
+}
+
+/*
+ * Name:	z_zlist_get_zonename
+ * Description:	Determine name of specified zone
+ * Arguments:	a_zlst - handle to zoneList_t object describing all zones
+ *		a_zoneIndex - index into a_zlst of the zone to return the
+ * Return:	char *
+ *			== NULL - zone name could not be determined
+ *			!= NULL - pointer to string representing zone name
+ * NOTE:    	Any zoneList_t returned is placed in static storage that must
+ *		NEVER be free()ed by the caller.
+ */
+
+char *
+z_zlist_get_zonename(zoneList_t a_zlst, int a_zoneIndex)
+{
+	int	i;
+
+	/* ignore empty list */
+
+	if (a_zlst == (zoneList_t)NULL) {
+		return ((char *)NULL);
+	}
+
+	/* find the specified zone in the list */
+
+	for (i = 0; (i != a_zoneIndex) &&
+	    (a_zlst[i]._zlName != (char *)NULL); i++)
+		;
+
+	/* return error if the specified zone does not exist */
+
+	if (a_zlst[i]._zlName == (char *)NULL) {
+		return (NULL);
+	}
+
+	/* return selected zone's name */
+
+	return (a_zlst[i]._zlName);
+}
+
+/*
+ * Name:	z_zlist_get_zonepath
+ * Description:	Determine zonepath of specified zone
+ * Arguments:	a_zlst - handle to zoneList_t object describing all zones
+ *		a_zoneIndex - index into a_zlst of the zone to return
+ * Return:	char *
+ *			== NULL - zonepath could not be determined
+ *			!= NULL - pointer to string representing zonepath
+ * NOTE:    	Any zoneList_t returned is placed in static storage that must
+ *		NEVER be free()ed by the caller.
+ */
+
+char *
+z_zlist_get_zonepath(zoneList_t a_zlst, int a_zoneIndex)
+{
+	int	i;
+
+	/* ignore empty list */
+
+	if (a_zlst == (zoneList_t)NULL) {
+		return ((char *)NULL);
+	}
+
+	/* find the specified zone in the list */
+
+	for (i = 0; (i != a_zoneIndex) &&
+	    (a_zlst[i]._zlName != (char *)NULL); i++)
+		;
+
+	/* return error if the specified zone does not exist */
+
+	if (a_zlst[i]._zlName == (char *)NULL) {
+		return (NULL);
+	}
+
+	/* return selected zone's zonepath */
+
+	return (a_zlst[i]._zlPath);
+}
+
+boolean_t
+z_zlist_is_zone_runnable(zoneList_t a_zlst, int a_zoneIndex)
+{
+	int	i;
+
+	/* if zones are not implemented, return error */
+
+	if (z_zones_are_implemented() == B_FALSE) {
+		return (B_FALSE);
+	}
+
+	/* ignore empty list */
+
+	if (a_zlst == (zoneList_t)NULL) {
+		return (B_FALSE);
+	}
+
+	/* find the specified zone in the list */
+
+	for (i = 0; (i != a_zoneIndex) &&
+	    (a_zlst[i]._zlName != (char *)NULL); i++)
+		;
+
+	/* return error if the specified zone does not exist */
+
+	if (a_zlst[i]._zlName == (char *)NULL) {
+		return (B_FALSE);
+	}
+
+	/* choose based on current state */
+
+	switch (a_zlst[i]._zlCurrKernelStatus) {
+	case ZONE_STATE_RUNNING:
+	case ZONE_STATE_MOUNTED:
+		/* already running */
+		return (B_TRUE);
+
+	case ZONE_STATE_INSTALLED:
+	case ZONE_STATE_DOWN:
+	case ZONE_STATE_READY:
+	case ZONE_STATE_SHUTTING_DOWN:
+		/* return false if the zone cannot be booted */
+
+		if (a_zlst[i]._zlStatus & ZST_NOT_BOOTABLE) {
+			return (B_FALSE);
+		}
+
+		return (B_TRUE);
+
+	case ZONE_STATE_CONFIGURED:
+	case ZONE_STATE_INCOMPLETE:
+	default:
+		/* cannot transition (boot) these states */
+		return (B_FALSE);
+	}
+}
+
+/*
+ * Name:	z_zlist_restore_zone_state
+ * Description:	Return the zone to the state it was originally in
+ * Arguments:	a_zlst - handle to zoneList_t object describing all zones
+ *		a_zoneIndex - index into a_zlst of the zone to return the
+ * Returns:	boolean_t
+ *			== B_TRUE - the zone's state has been restored
+ *			== B_FALSE - unable to transition the zone to its
+ *				original state
+ */
+
+boolean_t
+z_zlist_restore_zone_state(zoneList_t a_zlst, int a_zoneIndex)
+{
+	int		i;
+
+	/* ignore empty list */
+
+	if (a_zlst == (zoneList_t)NULL) {
+		return (B_FALSE);
+	}
+
+	/* find the specified zone in the list */
+
+	for (i = 0; (i != a_zoneIndex) &&
+	    (a_zlst[i]._zlName != (char *)NULL); i++)
+		;
+
+	/* return error if the specified zone does not exist */
+
+	if (a_zlst[i]._zlName == (char *)NULL) {
+		return (B_FALSE);
+	}
+
+	/* transition the zone back to its original state */
+
+	return (z_zlist_change_zone_state(a_zlst,
+	    a_zoneIndex, a_zlst[i]._zlOrigKernelStatus));
+}
+
+/*
+ * Name:	z_zone_exec
+ * Description:	Execute a Unix command in a specified zone and return results
+ * Arguments:	a_zoneName - pointer to string representing the name of the zone
+ *			to execute the specified command in
+ *		a_path - pointer to string representing the full path *in the
+ *			non-global zone named by a_zoneName* of the Unix command
+ *			to be executed
+ *		a_argv[] - Pointer to array of character strings representing
+ *			the arguments to be passed to the Unix command. The list
+ *			must be termianted with an element that is (char *)NULL
+ *		NOTE: a_argv[0] is the "command name" passed to the command
+ *		a_stdoutPath - Pointer to string representing the path to a file
+ *			into which all output to "stdout" from the Unix command
+ *			is placed.
+ *			== (char *)NULL - leave stdout open and pass through
+ *			== "/dev/null" - discard stdout output
+ *		a_strerrPath - Pointer to string representing the path to a file
+ *			into which all output to "stderr" from the Unix command
+ *			is placed.
+ *			== (char *)NULL - leave stderr open and pass through
+ *			== "/dev/null" - discard stderr output
+ *		a_fds - Pointer to array of integers representing file
+ *			descriptors to remain open during the call - all
+ *			file descriptors above STDERR_FILENO not in this
+ *			list will be closed.
+ * Returns:	int
+ *			The return (exit) code from the specified Unix command
+ *			Special return codes:
+ *			-1 : failure to exec process
+ *			-2 : could not create contract for greenline
+ *			-3 : fork() failed
+ *			-4 : could not open stdout capture file
+ *			-5 : error from 'waitpid' other than EINTR
+ *			-6 : zones are not supported
+ * NOTE:	All file descriptores other than 0, 1 and 2 are closed except
+ *		for those file descriptors listed in the a_fds array.
+ */
+
+int
+z_zone_exec(const char *a_zoneName, const char *a_path, char *a_argv[],
+	char *a_stdoutPath, char *a_stderrPath, int *a_fds)
+{
+	int			final_status;
+	int			lerrno;
+	int			status;
+	int			tmpl_fd;
+	pid_t			child_pid;
+	pid_t			result_pid;
+	struct sigaction	nact;
+	struct sigaction	oact;
+	void			(*funcSighup)();
+	void			(*funcSigint)();
+
+	/* if zones are not implemented, return TRUE */
+
+	if (z_zones_are_implemented() == B_FALSE) {
+		return (-6);	/* -6 : zones are not supported */
+	}
+
+	if ((tmpl_fd = _zexec_init_template()) == -1) {
+		_z_program_error(ERR_CANNOT_CREATE_CONTRACT, strerror(errno));
+		return (-2);	/* -2 : could not create greenline contract */
+	}
+
+	/*
+	 * hold SIGINT/SIGHUP signals and reset signal received counter;
+	 * after the fork1() the parent and child need to setup their respective
+	 * interrupt handling and release the hold on the signals
+	 */
+
+	(void) sighold(SIGINT);
+	(void) sighold(SIGHUP);
+
+	_z_global_data._z_SigReceived = 0;	/* no signals received */
+
+	/*
+	 * fork off a new process to execute command in;
+	 * fork1() is used instead of vfork() so the child process can
+	 * perform operations that would modify the parent process if
+	 * vfork() were used
+	 */
+
+	child_pid = fork1();
+
+	if (child_pid < 0) {
+		/*
+		 * *************************************************************
+		 * fork failed!
+		 * *************************************************************
+		 */
+
+		(void) ct_tmpl_clear(tmpl_fd);
+		(void) close(tmpl_fd);
+		_z_program_error(ERR_FORK, strerror(errno));
+
+		/* release hold on signals */
+
+		(void) sigrelse(SIGHUP);
+		(void) sigrelse(SIGINT);
+
+		return (-3);	/* -3 : fork() failed */
+	}
+
+	if (child_pid == 0) {
+		int	i;
+
+		/*
+		 * *************************************************************
+		 * This is the forked (child) process
+		 * *************************************************************
+		 */
+
+		(void) ct_tmpl_clear(tmpl_fd);
+		(void) close(tmpl_fd);
+
+		/* reset any signals to default */
+
+		for (i = 0; i < NSIG; i++) {
+			(void) sigset(i, SIG_DFL);
+		}
+
+		/*
+		 * close all file descriptors not in the a_fds list
+		 */
+
+		(void) fdwalk(&_z_close_file_descriptors, (void *)a_fds);
+
+		/*
+		 * if a file for stdout is present, open the file and use the
+		 * file to capture stdout from the _zexec process
+		 */
+
+		if (a_stdoutPath != (char *)NULL) {
+			int	stdoutfd;
+
+			stdoutfd = open(a_stdoutPath,
+			    O_WRONLY|O_CREAT|O_TRUNC, 0600);
+			if (stdoutfd < 0) {
+				_z_program_error(ERR_CAPTURE_FILE, a_stdoutPath,
+				    strerror(errno));
+				return (-4);
+			}
+
+			(void) dup2(stdoutfd, STDOUT_FILENO);
+			(void) close(stdoutfd);
+		}
+
+		/*
+		 * if a file for stderr is present, open the file and use the
+		 * file to capture stderr from the _zexec process
+		 */
+
+		if (a_stderrPath != (char *)NULL) {
+			int	stderrfd;
+
+			stderrfd = open(a_stderrPath,
+			    O_WRONLY|O_CREAT|O_TRUNC, 0600);
+			if (stderrfd < 0) {
+				_z_program_error(ERR_CAPTURE_FILE, a_stderrPath,
+				    strerror(errno));
+				return (-4);
+			}
+
+			(void) dup2(stderrfd, STDERR_FILENO);
+			(void) close(stderrfd);
+		}
+
+		/* release all held signals */
+
+		(void) sigrelse(SIGHUP);
+		(void) sigrelse(SIGINT);
+
+		/* execute command in the specified non-global zone */
+
+		_exit(_zexec(a_zoneName, a_path, a_argv));
+	}
+
+	/*
+	 * *********************************************************************
+	 * This is the forking (parent) process
+	 * *********************************************************************
+	 */
+
+	/* register child process i.d. so signal handlers can pass signal on */
+
+	_z_global_data._z_ChildProcessId = child_pid;
+
+	/*
+	 * setup signal handlers for SIGINT and SIGHUP and release hold
+	 */
+
+	/* hook SIGINT to _z_sig_trap() */
+
+	nact.sa_handler = _z_sig_trap;
+	nact.sa_flags = SA_RESTART;
+	(void) sigemptyset(&nact.sa_mask);
+
+	if (sigaction(SIGINT, &nact, &oact) < 0) {
+		funcSigint = SIG_DFL;
+	} else {
+		funcSigint = oact.sa_handler;
+	}
+
+	/* hook SIGHUP to _z_sig_trap() */
+
+	nact.sa_handler = _z_sig_trap;
+	nact.sa_flags = SA_RESTART;
+	(void) sigemptyset(&nact.sa_mask);
+
+	if (sigaction(SIGHUP, &nact, &oact) < 0) {
+		funcSighup = SIG_DFL;
+	} else {
+		funcSighup = oact.sa_handler;
+	}
+
+	/* release hold on signals */
+
+	(void) sigrelse(SIGHUP);
+	(void) sigrelse(SIGINT);
+
+	(void) ct_tmpl_clear(tmpl_fd);
+	(void) close(tmpl_fd);
+
+	/*
+	 * wait for the process to exit, reap child exit status
+	 */
+
+	for (;;) {
+		result_pid = waitpid(child_pid, &status, 0L);
+		lerrno = (result_pid == -1 ? errno : 0);
+
+		/* break loop if child process status reaped */
+
+		if (result_pid != -1) {
+			break;
+		}
+
+		/* break loop if not interrupted out of waitpid */
+
+		if (errno != EINTR) {
+			break;
+		}
+	}
+
+	/* reset child process i.d. so signal handlers do not pass signals on */
+
+	_z_global_data._z_ChildProcessId = -1;
+
+	/*
+	 * If the child process terminated due to a call to exit(), then
+	 * set results equal to the 8-bit exit status of the child process;
+	 * otherwise, set the exit status to "-1" indicating that the child
+	 * exited via a signal.
+	 */
+
+	if (WIFEXITED(status)) {
+		final_status = WEXITSTATUS(status);
+		if ((_z_global_data._z_SigReceived != 0) &&
+		    (final_status == 0)) {
+			final_status = 1;
+		}
+	} else {
+		final_status = -1;	/* -1 : failure to exec process */
+	}
+
+	/* determine proper exit code */
+
+	if (result_pid == -1) {
+		final_status = -5;	/* -5 : error from waitpid not EINTR */
+	} else if (_z_global_data._z_SigReceived != 0) {
+		final_status = -7;	/* -7 : interrupt received */
+	}
+
+	/*
+	 * reset signal handlers
+	 */
+
+	/* reset SIGINT */
+
+	nact.sa_handler = funcSigint;
+	nact.sa_flags = SA_RESTART;
+	(void) sigemptyset(&nact.sa_mask);
+
+	(void) sigaction(SIGINT, &nact, (struct sigaction *)NULL);
+
+	/* reset SIGHUP */
+
+	nact.sa_handler = funcSighup;
+	nact.sa_flags = SA_RESTART;
+	(void) sigemptyset(&nact.sa_mask);
+
+	(void) sigaction(SIGHUP, &nact, (struct sigaction *)NULL);
+
+	/*
+	 * if signal received during command execution, interrupt
+	 * this process now.
+	 */
+
+	if (_z_global_data._z_SigReceived != 0) {
+		(void) kill(getpid(), SIGINT);
+	}
+
+	/* set errno and return */
+
+	errno = lerrno;
+
+	return (final_status);
+}
+
+/*
+ * Name:	z_zones_are_implemented
+ * Description:	Determine if any zone operations can be performed
+ * Arguments:	void
+ * Returns:	boolean_t
+ *			== B_TRUE - zone operations are available
+ *			== B_FALSE - no zone operations can be done
+ */
+
+boolean_t
+z_zones_are_implemented(void)
+{
+	static	boolean_t	_zonesImplementedDetermined = B_FALSE;
+	static	boolean_t	_zonesAreImplemented = B_FALSE;
+
+	/* if availability has not been determined, cache it now */
+
+	if (!_zonesImplementedDetermined) {
+		_zonesImplementedDetermined = B_TRUE;
+		_zonesAreImplemented = _z_zones_are_implemented();
+		if (!_zonesAreImplemented) {
+			_z_echoDebug(DBG_ZONES_NOT_IMPLEMENTED);
+		} else {
+			_z_echoDebug(DBG_ZONES_ARE_IMPLEMENTED);
+		}
+	}
+
+	return (_zonesAreImplemented);
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/usr/src/lib/libinstzones/common/zones_args.c	Fri Jun 05 10:28:40 2009 -0400
@@ -0,0 +1,307 @@
+/*
+ * 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
+ * or http://www.opensolaris.org/os/licensing.
+ * 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 2009 Sun Microsystems, Inc.  All rights reserved.
+ * Use is subject to license terms.
+ */
+
+
+
+/*
+ * Module:	zones_args.c
+ * Group:	libinstzones
+ * Description:	Private functions used by zones library functions to manipulate
+ *		argument lists
+ *
+ * Public Methods:
+ *
+ * _z_add_arg - add new argument to argument array for use in exec() calls
+ * _z_free_args - free all storage contained in an argument array previously
+ * _z_get_argc - return (int) argc count from argument array
+ * _z_get_argv - return (char **)argv pointer from argument array
+ * _z_new_args - create a new argument array for use in exec() calls
+ */
+
+/*
+ * System includes
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <fcntl.h>
+#include <ctype.h>
+#include <sys/types.h>
+#include <sys/param.h>
+#include <string.h>
+#include <strings.h>
+#include <stdarg.h>
+#include <limits.h>
+#include <errno.h>
+#include <stropts.h>
+#include <libintl.h>
+#include <locale.h>
+#include <assert.h>
+
+/*
+ * local includes
+ */
+
+#include "instzones_lib.h"
+#include "zones_strings.h"
+
+/*
+ * Private structures
+ */
+
+/*
+ * Library Function Prototypes
+ */
+
+/*
+ * Local Function Prototypes
+ */
+
+/*
+ * Global internal (private) declarations
+ */
+
+/*
+ * *****************************************************************************
+ * global external (public) functions
+ * *****************************************************************************
+ */
+
+/*
+ * Name:	_z_add_arg
+ * Description:	add new argument to argument array for use in exec() calls
+ * Arguments:	a_args - [RO, *RW] - (argArray_t *)
+ *			Pointer to argument array (previously allocated via
+ *			a call to _z_new_args) to add the argument to
+ *		a_format - [RO, *RO] - (char *)
+ *			Pointer to "printf(3C)" style format argument
+ *		... - [RO, *RO] - (varies)
+ *			Arguments as appropriate for format argument specified
+ * Returns:	boolean_t
+ *			B_TRUE - success
+ *			B_FALSE - failure
+ * Examples:
+ * - to add an argument that specifies a file descriptor:
+ *	int fd;
+ *	_z_add_arg(aa, "/proc/self/fd/%d", fd);
+ * - to add a flag or other known text:
+ *	_z_add_arg(aa, "-s")
+ * - to add random text:
+ *	char *random_text;
+ *	_z_add_arg(aa, "%s", random_text);
+ */
+
+/*PRINTFLIKE2*/
+boolean_t
+_z_add_arg(argArray_t *a_args, char *a_format, ...)
+{
+	char		*rstr = NULL;
+	char		bfr[MAX_CANON];
+	size_t		vres = 0;
+	va_list		ap;
+
+	/* entry assertions */
+
+	assert(a_args != NULL);
+	assert(a_format != NULL);
+	assert(*a_format != '\0');
+
+	/*
+	 * double argument array if array is full
+	 */
+
+	if (a_args->_aaNumArgs >= a_args->_aaMaxArgs) {
+		int	newMax;
+		char	**newArgs;
+
+		newMax = a_args->_aaMaxArgs * 2;
+		newArgs = (char **)_z_realloc(a_args->_aaArgs,
+		    (newMax+1) * sizeof (char *));
+		a_args->_aaArgs = newArgs;
+		a_args->_aaMaxArgs = newMax;
+	}
+
+	/*
+	 * determine size of argument to add to list
+	 */
+
+	va_start(ap, a_format);
+	vres = vsnprintf(bfr, sizeof (bfr), a_format, ap);
+	va_end(ap);
+
+	/*
+	 * use the expanded argument if it will fit in the built in buffer,
+	 * otherwise, allocate space to hold the argument
+	 */
+
+	if (vres < sizeof (bfr)) {
+		/* duplicate text already generated in buffer */
+		rstr = _z_strdup(bfr);
+	} else {
+		/* allocate new space for argument to add */
+
+		rstr = (char *)_z_malloc(vres+2);
+
+		/* generate argument to add */
+
+		va_start(ap, a_format);
+		vres = vsnprintf(rstr, vres+1, a_format, ap);
+		va_end(ap);
+	}
+
+	/* add argument to the end of the argument array */
+
+	a_args->_aaArgs[a_args->_aaNumArgs++] = rstr;
+	a_args->_aaArgs[a_args->_aaNumArgs] = NULL;
+
+	/* successful - return */
+
+	return (B_TRUE);
+}
+
+/*
+ * Name:	_z_free_args
+ * Description:	free all storage contained in an argument array previously
+ *		allocated by a call to _z_new_args
+ * Arguments:	a_args - [RO, *RW] - (argArray_t *)
+ *			Pointer to argument array (previously allocated via
+ *			a call to _z_new_args) to free
+ * Returns:	void
+ * NOTE:	preserves errno (usually called right after e_execCmd*())
+ */
+
+void
+_z_free_args(argArray_t *a_args)
+{
+	int	i;
+	int	lerrno = errno;
+
+	/* entry assertions */
+
+	assert(a_args != NULL);
+	assert(a_args->_aaArgs != NULL);
+
+	/* free all arguments in the argument array */
+
+	for (i = (a_args->_aaNumArgs-1); i >= 0; i--) {
+		assert(a_args->_aaArgs[i] != NULL);
+		(void) free(a_args->_aaArgs[i]);
+	}
+
+	/* free argument array */
+
+	(void) free(a_args->_aaArgs);
+
+	/* free argument array structure */
+
+	(void) free(a_args);
+
+	/* restore errno */
+
+	errno = lerrno;
+}
+
+/*
+ * Name:	_z_get_argc
+ * Description:	return (int) argc count from argument array
+ * Arguments:	a_args - [RO, *RW] - (argArray_t *)
+ *			Pointer to argument array (previously allocated via
+ *			a call to _z_new_args) to return argc count for
+ * Returns:	int
+ *			Count of the number of arguments in the argument array
+ *			suitable for use in an exec*() call
+ */
+
+int
+_z_get_argc(argArray_t *a_args)
+{
+	return (a_args->_aaNumArgs);
+}
+
+/*
+ * Name:	_z_get_argv
+ * Description:	return (char **)argv pointer from argument array
+ * Arguments:	a_args - [RO, *RW] - (argArray_t *)
+ *			Pointer to argument array (previously allocated via
+ *			a call to _z_new_args) to return argv pointer for
+ * Returns:	char **
+ *			Pointer to (char **)argv pointer suitable for use
+ *			in an exec*() call
+ * NOTE: the actual character array is always terminated with a NULL
+ */
+
+char **
+_z_get_argv(argArray_t *a_args)
+{
+	return (a_args->_aaArgs);
+}
+
+/*
+ * Name:	_z_new_args
+ * Description:	create a new argument array for use in exec() calls
+ * Arguments:	initialCount - [RO, *RO] - (int)
+ *			Initial number of elements to populate the
+ *			argument array with - use best guess
+ * Returns:	argArray_t *
+ *			Pointer to argument array that can be used in other
+ *			functions that accept it as an argument
+ *			== (argArray_t *)NULL - error
+ * NOTE: you must call _z_free_args() when the returned argument array is
+ * no longer needed so that all storage used can be freed up.
+ */
+
+argArray_t *
+_z_new_args(int initialCount)
+{
+	argArray_t	*aa;
+
+	/* entry assertions */
+
+	assert(initialCount >= 0);
+
+	/* if no guess on size, then assume 1 */
+
+	if (initialCount == 0) {
+		initialCount = 1;
+	}
+
+	/* allocate new argument array structure */
+
+	aa = (argArray_t *)_z_calloc(sizeof (argArray_t));
+
+	/* allocate initial argument array */
+
+	aa->_aaArgs = (char **)_z_calloc((initialCount+1) * sizeof (char *));
+
+	/* initialize argument indexes */
+
+	aa->_aaNumArgs = 0;
+	aa->_aaMaxArgs = initialCount;
+
+	/* successful - return pointer to argument array created */
+
+	return (aa);
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/usr/src/lib/libinstzones/common/zones_exec.c	Fri Jun 05 10:28:40 2009 -0400
@@ -0,0 +1,1120 @@
+/*
+ * 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
+ * or http://www.opensolaris.org/os/licensing.
+ * 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 2009 Sun Microsystems, Inc.  All rights reserved.
+ * Use is subject to license terms.
+ */
+
+
+
+/*
+ * Module:	zones_exec.c
+ * Group:	libinstzones
+ * Description:	Provide "zones" execution interface for install
+ *		consolidation code
+ *
+ * Public Methods:
+ *
+ * z_ExecCmdArray - Execute a Unix command and return results and status
+ * _zexec - run a command with arguments on a specified zone
+ * _zexec_init_template - used by _zexec to establish contracts
+ * _z_zone_exec - Execute a Unix command in a specified zone and return results
+ * z_ExecCmdList - Execute a Unix command and return results and status
+ */
+
+/*
+ * System includes
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <fcntl.h>
+#include <ctype.h>
+#include <sys/types.h>
+#include <sys/param.h>
+#include <string.h>
+#include <strings.h>
+#include <stdarg.h>
+#include <limits.h>
+#include <errno.h>
+#include <signal.h>
+#include <wait.h>
+#include <stropts.h>
+#include <libintl.h>
+#include <locale.h>
+#include <libcontract.h>
+#include <sys/contract/process.h>
+#include <sys/ctfs.h>
+#include <assert.h>
+
+/*
+ * local includes
+ */
+
+#include "instzones_lib.h"
+#include "zones_strings.h"
+
+/*
+ * Private structures
+ */
+
+/*
+ * Library Function Prototypes
+ */
+
+/*
+ * Local Function Prototypes
+ */
+
+/*
+ * global internal (private) declarations
+ */
+
+/*
+ * *****************************************************************************
+ * global external (public) functions
+ * *****************************************************************************
+ */
+
+/*
+ * Name:	z_ExecCmdArray
+ * Synopsis:	Execute Unix command and return results
+ * Description:	Execute a Unix command and return results and status
+ * Arguments:
+ *		r_status - [RO, *RW] - (int *)
+ *			Return (exit) status from Unix command:
+ *			== -1 : child terminated with a signal
+ *			!= -1 : lower 8-bit value child passed to exit()
+ *		r_results - [RO, *RW] - (char **)
+ *			Any output generated by the Unix command to stdout
+ *			and to stderr
+ *			== (char *)NULL if no output generated
+ *		a_inputFile - [RO, *RO] - (char *)
+ *			Pointer to character string representing file to be
+ *			used as "standard input" for the command.
+ *			== (char *)NULL to use "/dev/null" as standard input
+ *		a_cmd - [RO, *RO] - (char *)
+ *			Pointer to character string representing the full path
+ *			of the Unix command to execute
+ *		char **a_args - [RO, *RO] - (char **)
+ *			List of character strings representing the arguments
+ *			to be passed to the Unix command. The list must be
+ *			terminated with an element that is (char *)NULL
+ * Returns:	int
+ *			== 0 - Command executed
+ *				Look at r_status for results of Unix command
+ *			!= 0 - problems executing command
+ *				r_status and r_results have no meaning;
+ *				r_status will be -1
+ *				r_results will be NULL
+ * NOTE:    	Any results returned is placed in new storage for the
+ *		calling method. The caller must use 'free' to dispose
+ *		of the storage once the results are no longer needed.
+ * NOTE:	If 0 is returned, 'r_status' must be queried to
+ *		determine the results of the Unix command.
+ * NOTE:	The system "errno" value from immediately after waitpid() call
+ *		is preserved for the calling method to use to determine
+ *		the system reason why the operation failed.
+ */
+
+int
+z_ExecCmdArray(int *r_status, char **r_results,
+    char *a_inputFile, char *a_cmd, char **a_args)
+{
+	char		*buffer;
+	int		bufferIndex;
+	int		bufferSize;
+	int		ipipe[2] = {0, 0};
+	int		lerrno;
+	int		status;
+	int		stdinfile = -1;
+	pid_t		pid;
+	pid_t		resultPid;
+
+	/* entry assertions */
+
+	assert(r_status != NULL);
+	assert(a_cmd != NULL);
+	assert(*a_cmd != '\0');
+	assert(a_args != NULL);
+
+	/* reset return results buffer pointer */
+
+	if (r_results != (char **)NULL) {
+		*r_results = (char *)NULL;
+	}
+
+	*r_status = -1;
+
+	/*
+	 * See if command exists
+	 */
+
+	if (access(a_cmd, F_OK|X_OK) != 0) {
+		return (-1);
+	}
+
+	/*
+	 * See if input file exists
+	 */
+
+	if (a_inputFile != (char *)NULL) {
+		stdinfile = open(a_inputFile, O_RDONLY);
+	} else {
+		stdinfile = open("/dev/null", O_RDONLY); /* stdin = /dev/null */
+	}
+
+	if (stdinfile < 0) {
+		return (-1);
+	}
+
+	/*
+	 * Create a pipe to be used to capture the command output
+	 */
+
+	if (pipe(ipipe) != 0) {
+		(void) close(stdinfile);
+		return (-1);
+	}
+
+
+	bufferSize = PIPE_BUFFER_INCREMENT;
+	bufferIndex = 0;
+	buffer = calloc(1, bufferSize);
+	if (buffer == (char *)NULL) {
+		(void) close(stdinfile);
+		return (-1);
+	}
+
+	/* flush standard i/o before creating new process */
+
+	(void) fflush(stderr);
+	(void) fflush(stdout);
+
+	/*
+	 * create new process to execute command in;
+	 * vfork() is being used to avoid duplicating the parents
+	 * memory space - this means that the child process may
+	 * not modify any of the parents memory including the
+	 * standard i/o descriptors - all the child can do is
+	 * adjust interrupts and open files as a prelude to a
+	 * call to exec().
+	 */
+
+	pid = vfork();
+
+	if (pid == 0) {
+		/*
+		 * This is the forked (child) process ======================
+		 */
+
+		int	i;
+
+		/* reset any signals to default */
+
+		for (i = 0; i < NSIG; i++) {
+			(void) sigset(i, SIG_DFL);
+		}
+
+		/* assign stdin, stdout, stderr as appropriate */
+
+		(void) dup2(stdinfile, STDIN_FILENO);
+		(void) close(ipipe[0]);		/* close out pipe reader side */
+		(void) dup2(ipipe[1], STDOUT_FILENO);
+		(void) dup2(ipipe[1], STDERR_FILENO);
+
+		/* Close all open files except standard i/o */
+
+		closefrom(3);
+
+		/* execute target executable */
+
+		(void) execvp(a_cmd, a_args);
+		perror(a_cmd);	/* Emit error msg - ends up in callers buffer */
+		_exit(0x00FE);
+	} else if (pid == -1) {
+		_z_program_error(ERR_FORK, strerror(errno));
+		*r_status = -1;
+		return (-1);
+	}
+
+	/*
+	 * This is the forking (parent) process ====================
+	 */
+
+	(void) close(stdinfile);
+	(void) close(ipipe[1]);		/* Close write side of pipe */
+
+	/*
+	 * Spin reading data from the child into the buffer - when the read eofs
+	 * the child has exited
+	 */
+
+	for (;;) {
+		ssize_t	bytesRead;
+
+		/* read as much child data as there is available buffer space */
+
+		bytesRead = read(ipipe[0], buffer + bufferIndex,
+		    bufferSize - bufferIndex);
+
+		/* break out of read loop if end-of-file encountered */
+
+		if (bytesRead == 0) {
+			break;
+		}
+
+		/* if error, continue if recoverable, else break out of loop */
+
+		if (bytesRead == -1) {
+			/* try again: EAGAIN - insufficient resources */
+
+			if (errno == EAGAIN) {
+				continue;
+			}
+
+			/* try again: EINTR - interrupted system call */
+
+			if (errno == EINTR) {
+				continue;
+			}
+
+			/* break out of loop - error not recoverable */
+			break;
+		}
+
+		/* at least 1 byte read: expand buffer if at end */
+
+		bufferIndex += bytesRead;
+		if (bufferIndex >= bufferSize) {
+			buffer = realloc(buffer,
+			    bufferSize += PIPE_BUFFER_INCREMENT);
+			(void) memset(buffer + bufferIndex, 0,
+			    bufferSize - bufferIndex);
+		}
+	}
+
+	(void) close(ipipe[0]);		/* Close read side of pipe */
+
+	/* Get subprocess exit status */
+
+	for (;;) {
+		resultPid = waitpid(pid, &status, 0L);
+		lerrno = (resultPid == -1 ? errno : 0);
+
+		/* break loop if child process status reaped */
+
+		if (resultPid != -1) {
+			break;
+		}
+
+		/* break loop if not interrupted out of waitpid */
+
+		if (errno != EINTR) {
+			break;
+		}
+	}
+
+	/*
+	 * If the child process terminated due to a call to exit(), then
+	 * set results equal to the 8-bit exit status of the child process;
+	 * otherwise, set the exit status to "-1" indicating that the child
+	 * exited via a signal.
+	 */
+
+	*r_status = WIFEXITED(status) ? WEXITSTATUS(status) : -1;
+
+	/* return appropriate output */
+
+	if (!*buffer) {
+		/* No contents in output buffer - discard */
+		free(buffer);
+	} else if (r_results == (char **)NULL) {
+		/* Not requested to return results - discard */
+		free(buffer);
+	} else {
+		/* have output and request to return: pass to calling method */
+		*r_results = buffer;
+	}
+
+	errno = lerrno;
+	return (resultPid == -1 ? -1 : 0);
+}
+
+/*
+ * Name:	_zexec
+ * Description:	run a command with arguments on a specified zone
+ * Arguments:	a_zoneName - pointer to string representing the name of the zone
+ *			to execute the specified command in
+ *		a_path - pointer to string representing the full path *in the
+ *			non-global zone named by a_zoneName* of the Unix command
+ *			to be executed
+ *		a_argv[] - Pointer to array of character strings representing
+ *			the arguments to be passed to the Unix command. The list
+ *			must be termianted with an element that is (char *)NULL
+ *		NOTE: a_argv[0] is the "command name" passed to the command
+ * Returns:	int
+ *			This function must be treated like a call to exec()
+ *			If the exec() is successful, the thread of control is
+ *			NOT returned, and the process will exit when completed.
+ *			If this function returns, it means the exec() could not
+ *			be done, or another fatal error occurred.
+ */
+
+int
+_zexec(const char *a_zoneName, const char *a_path, char *a_argv[])
+{
+	zoneid_t zoneid;
+	zone_state_t st;
+	char **new_env = { NULL };
+	priv_set_t *privset;
+
+	/* entry assertions */
+
+	assert(a_zoneName != NULL);
+	assert(*a_zoneName != '\0');
+	assert(a_path != NULL);
+	assert(*a_path != '\0');
+
+	/* establish locale settings */
+
+	(void) setlocale(LC_ALL, "");
+	(void) textdomain(TEXT_DOMAIN);
+
+	/* can only be invoked from within the global zone */
+
+	if (getzoneid() != GLOBAL_ZONEID) {
+		_z_program_error(ERR_ZEXEC_NOT_IN_GZ, a_zoneName);
+		return (-1);
+	}
+
+	if (strcmp(a_zoneName, GLOBAL_ZONENAME) == 0) {
+		_z_program_error(ERR_ZEXEC_GZUSED, a_zoneName);
+		return (-1);
+	}
+
+	/* get the state of the specified zone */
+
+	if (zone_get_state((char *)a_zoneName, &st) != Z_OK) {
+		_z_program_error(ERR_ZEXEC_BADZONE, a_zoneName);
+		return (-1);
+	}
+
+	if (st < ZONE_STATE_INSTALLED) {
+		_z_program_error(ERR_ZEXEC_BADSTATE, a_zoneName,
+		    zone_state_str(st));
+		return (-1);
+	}
+
+	if (st != ZONE_STATE_RUNNING && st != ZONE_STATE_MOUNTED) {
+		_z_program_error(ERR_ZEXEC_NOTRUNNING, a_zoneName,
+		    zone_state_str(st));
+		return (-1);
+	}
+
+	/*
+	 * In both console and non-console cases, we require all privs.
+	 * In the console case, because we may need to startup zoneadmd.
+	 * In the non-console case in order to do zone_enter(2), zonept()
+	 * and other tasks.
+	 *
+	 * Future work: this solution is temporary.  Ultimately, we need to
+	 * move to a flexible system which allows the global admin to
+	 * designate that a particular user can zlogin (and probably zlogin
+	 * -C) to a particular zone.  This all-root business we have now is
+	 * quite sketchy.
+	 */
+
+	if ((privset = priv_allocset()) == NULL) {
+		_z_program_error(ERR_ZEXEC_PRIV_ALLOCSET, a_zoneName,
+		    strerror(errno));
+		return (-1);
+	}
+
+	if (getppriv(PRIV_EFFECTIVE, privset) != 0) {
+		_z_program_error(ERR_ZEXEC_GETPPRIV, a_zoneName,
+		    strerror(errno));
+		priv_freeset(privset);
+		return (-1);
+	}
+
+	if (priv_isfullset(privset) == B_FALSE) {
+		_z_program_error(ERR_ZEXEC_PRIVS, a_zoneName);
+		priv_freeset(privset);
+		return (-1);
+	}
+	priv_freeset(privset);
+
+	if ((zoneid = getzoneidbyname(a_zoneName)) == -1) {
+		_z_program_error(ERR_ZEXEC_NOZONEID, a_zoneName,
+		    strerror(errno));
+		return (-1);
+	}
+
+	if ((new_env = _zexec_prep_env()) == NULL) {
+		_z_program_error(ERR_ZEXEC_ASSEMBLE, a_zoneName);
+		return (-1);
+	}
+
+	/*
+	 * In case any of stdin, stdout or stderr are streams,
+	 * anchor them to prevent malicious I_POPs.
+	 *
+	 * Future work: use pipes to entirely eliminate FD leakage
+	 * into the zone.
+	 */
+
+	(void) ioctl(STDIN_FILENO, I_ANCHOR);
+	(void) ioctl(STDOUT_FILENO, I_ANCHOR);
+	(void) ioctl(STDERR_FILENO, I_ANCHOR);
+
+	if (zone_enter(zoneid) == -1) {
+		int	lerrno = errno;
+
+		_z_program_error(ERR_ZEXEC_ZONEENTER, a_zoneName,
+		    strerror(errno));
+
+		if (lerrno == EFAULT) {
+			_z_program_error(ERR_ZEXEC_EFAULT, a_zoneName);
+		}
+
+		free(new_env);
+
+		return (-1);
+	}
+
+	(void) execve(a_path, &a_argv[0], new_env);
+
+	_z_program_error(ERR_ZEXEC_EXECFAILURE, a_zoneName, strerror(errno));
+
+	return (-1);
+}
+
+/*
+ * Name:	_zexec_init_template
+ * Description:	used by _zexec to establish contracts
+ */
+
+int
+_zexec_init_template(void)
+{
+	int fd;
+	int err = 0;
+
+	fd = open64(CTFS_ROOT "/process/template", O_RDWR);
+	if (fd == -1) {
+		return (-1);
+	}
+
+	/*
+	 * zlogin doesn't do anything with the contract.
+	 * Deliver no events, don't inherit, and allow it to be orphaned.
+	 */
+	err |= ct_tmpl_set_critical(fd, 0);
+	err |= ct_tmpl_set_informative(fd, 0);
+	err |= ct_pr_tmpl_set_fatal(fd, CT_PR_EV_HWERR);
+	err |= ct_pr_tmpl_set_param(fd, CT_PR_PGRPONLY | CT_PR_REGENT);
+	if (err || ct_tmpl_activate(fd)) {
+		(void) close(fd);
+		return (-1);
+	}
+
+	return (fd);
+}
+
+/*
+ * Helper routine for _zexec_prep_env below.
+ */
+char *
+_zexec_add_env(char *name, char *value)
+{
+	size_t sz = strlen(name) + strlen(value) + 1;
+	char *str;
+
+	if ((str = malloc(sz)) == NULL)
+		return (NULL);
+
+	(void) snprintf(str, sz, "%s%s", name, value);
+	return (str);
+}
+
+/*
+ * Prepare envp array for exec'd process.
+ */
+char **
+_zexec_prep_env()
+{
+	int e = 0, size = 1;
+	char **new_env, *estr;
+	char *term = getenv("TERM");
+
+	size++;	/* for $PATH */
+	if (term != NULL)
+		size++;
+
+	/*
+	 * In failsafe mode we set $HOME
+	 */
+	size++;
+
+	/*
+	 * In failsafe mode we set $SHELL, since login won't be around to do it.
+	 */
+	size++;
+
+	if ((new_env = malloc(sizeof (char *) * size)) == NULL)
+		return (NULL);
+
+	if ((estr = _zexec_add_env("PATH=", ZONE_DEF_PATH)) == NULL) {
+		free(new_env);
+		return (NULL);
+	}
+	new_env[e++] = estr;
+
+	if (term != NULL) {
+		if ((estr = _zexec_add_env("TERM=", term)) == NULL) {
+			free(new_env);
+			return (NULL);
+		}
+		new_env[e++] = estr;
+	}
+
+	if ((estr = _zexec_add_env("HOME=", "/")) == NULL) {
+		free(new_env);
+		return (NULL);
+	}
+	new_env[e++] = estr;
+
+	if ((estr = _zexec_add_env("SHELL=", ZONE_FAILSAFESHELL)) == NULL) {
+		free(new_env);
+		return (NULL);
+	}
+	new_env[e++] = estr;
+
+	new_env[e++] = NULL;
+
+	return (new_env);
+}
+
+/*
+ * Name:	_z_zone_exec
+ * Description:	Execute a Unix command in a specified zone and return results
+ * Arguments:
+ *		r_status - [RO, *RW] - (int *)
+ *			Return (exit) status from Unix command:
+ *			== -1 : child terminated with a signal
+ *			!= -1 : lower 8-bit value child passed to exit()
+ *		r_results - [RO, *RW] - (char **)
+ *			Any output generated by the Unix command to stdout
+ *			and to stderr
+ *			== (char *)NULL if no output generated
+ *		a_inputFile - [RO, *RO] - (char *)
+ *			Pointer to character string representing file to be
+ *			used as "standard input" for the command.
+ *			== (char *)NULL to use "/dev/null" as standard input
+ *		a_path - [RO, *RO] - (char *)
+ *			Pointer to character string representing the full path
+ *			*in the non-global zone named by a_zoneName*of the Unix
+ *			command to be executed
+ *		char **a_args - [RO, *RO] - (char **)
+ *			List of character strings representing the arguments
+ *			to be passed to the Unix command.
+ *			NOTE: The list must be terminated with an element that
+ *			----- is (char *)NULL
+ *			NOTE: a_argv[0] is the "command name" passed to the
+ *			----- command executed in the specified non-global zone
+ *		a_zoneName - pointer to string representing the name of the zone
+ *			to execute the specified command in
+ *		a_fds - Pointer to array of integers representing file
+ *			descriptors to remain open during the call - all
+ *			file descriptors above STDERR_FILENO not in this
+ *			list will be closed.
+ * Returns:	int
+ *			== 0 - Command executed
+ *				Look at r_status for results of Unix command
+ *			!= 0 - problems executing command
+ *				r_status and r_results have no meaning;
+ *				r_status will be -1
+ *				r_results will be NULL
+ *			The return (exit) code from the specified Unix command
+ *			Special return codes:
+ *			-1 : failure to exec process
+ *			-2 : could not create contract for greenline
+ *			-3 : fork() failed
+ *			-4 : could not open stdin source file
+ *			-5 : error from 'waitpid' other than EINTR
+ *			-6 : zones are not supported
+ *			-7 : interrupt received
+ * NOTE:	All file descriptores other than 0, 1 and 2 are closed except
+ *		for those file descriptors listed in the a_fds array.
+ */
+
+int
+_z_zone_exec(int *r_status, char **r_results, char *a_inputFile,
+	char *a_path, char *a_argv[], const char *a_zoneName, int *a_fds)
+{
+	struct sigaction	nact;
+	struct sigaction	oact;
+	char			*buffer;
+	char			*thisZoneName;
+	int			bufferIndex;
+	int			bufferSize;
+	int			exit_no;
+	int			ipipe[2] = {0, 0};
+	int			lerrno;
+	int			n;
+	int			status;
+	int			stdinfile = -1;
+	int			tmpl_fd;
+	pid_t			child_pid;
+	pid_t			result_pid;
+	void			(*funcSighup)();
+	void			(*funcSigint)();
+
+	/* entry assertions */
+
+	assert(a_path != (char *)NULL);
+	assert(*a_path != '\0');
+	assert(a_argv != (char **)NULL);
+	assert(a_argv[0] != (char *)NULL);
+	assert(*a_argv[0] != '\0');
+	assert(a_zoneName != (char *)NULL);
+
+	/*
+	 * if requested to execute in current zone name, directly execute
+	 */
+
+	thisZoneName = z_get_zonename();
+	status = (strcmp(a_zoneName, thisZoneName) == 0);
+
+	/* entry debugging info */
+
+	_z_echoDebug(DBG_ZONE_EXEC_CMD_ENTER, a_path, a_zoneName, thisZoneName);
+	(void) free(thisZoneName);
+	for (n = 0; a_argv[n]; n++) {
+		_z_echoDebug(DBG_ARG, n, a_argv[n]);
+	}
+
+	/* if this zone, just exec the command directly */
+
+	if (status != 0) {
+		return (z_ExecCmdArray(r_status, r_results, a_inputFile,
+		    a_path, a_argv));
+	}
+
+	/* reset return results buffer pointer */
+
+	if (r_results != (char **)NULL) {
+		*r_results = (char *)NULL;
+	}
+
+	*r_status = -1;	/* -1 : failure to exec process */
+
+	/* if zones are not implemented, return TRUE */
+
+	if (!z_zones_are_implemented()) {
+		return (-6);	/* -6 : zones are not supported */
+	}
+
+	if ((tmpl_fd = _zexec_init_template()) == -1) {
+		_z_program_error(ERR_CANNOT_CREATE_CONTRACT, strerror(errno));
+		return (-2);	/* -2 : cannot create greenline contract */
+	}
+
+	/*
+	 * See if input file exists
+	 */
+
+	if (a_inputFile != (char *)NULL) {
+		stdinfile = open(a_inputFile, O_RDONLY);
+	} else {
+		stdinfile = open("/dev/null", O_RDONLY); /* stdin = /dev/null */
+	}
+
+	if (stdinfile < 0) {
+		return (-4);	/* -4 : could not open stdin source file */
+	}
+
+	/*
+	 * Create a pipe to be used to capture the command output
+	 */
+
+	if (pipe(ipipe) != 0) {
+		(void) close(stdinfile);
+		return (-1);
+	}
+
+	bufferSize = PIPE_BUFFER_INCREMENT;
+	bufferIndex = 0;
+	buffer = calloc(1, bufferSize);
+	if (buffer == (char *)NULL) {
+		(void) close(stdinfile);
+		return (-1);
+	}
+
+	/* flush standard i/o before creating new process */
+
+	(void) fflush(stderr);
+	(void) fflush(stdout);
+
+	/*
+	 * hold SIGINT/SIGHUP signals and reset signal received counter;
+	 * after the fork1() the parent and child need to setup their respective
+	 * interrupt handling and release the hold on the signals
+	 */
+
+	(void) sighold(SIGINT);
+	(void) sighold(SIGHUP);
+
+	_z_global_data._z_SigReceived = 0;	/* no signals received */
+
+	/*
+	 * fork off a new process to execute command in;
+	 * fork1() is used instead of vfork() so the child process can
+	 * perform operations that would modify the parent process if
+	 * vfork() were used
+	 */
+
+	child_pid = fork1();
+
+	if (child_pid < 0) {
+		/*
+		 * *************************************************************
+		 * fork failed!
+		 * *************************************************************
+		 */
+
+		(void) ct_tmpl_clear(tmpl_fd);
+		(void) close(tmpl_fd);
+		(void) free(buffer);
+		_z_program_error(ERR_FORK, strerror(errno));
+
+		/* release hold on signals */
+		(void) sigrelse(SIGHUP);
+		(void) sigrelse(SIGINT);
+
+		return (-3);	/* -3 : fork() failed */
+	}
+
+	if (child_pid == 0) {
+		int	i;
+
+		/*
+		 * *************************************************************
+		 * This is the forked (child) process
+		 * *************************************************************
+		 */
+
+		(void) ct_tmpl_clear(tmpl_fd);
+		(void) close(tmpl_fd);
+
+		/* reset any signals to default */
+
+		for (i = 0; i < NSIG; i++) {
+			(void) sigset(i, SIG_DFL);
+		}
+
+		/* assign stdin, stdout, stderr as appropriate */
+
+		(void) dup2(stdinfile, STDIN_FILENO);
+		(void) close(ipipe[0]);		/* close out pipe reader side */
+		(void) dup2(ipipe[1], STDOUT_FILENO);
+		(void) dup2(ipipe[1], STDERR_FILENO);
+
+		/*
+		 * close all file descriptors not in the a_fds list
+		 */
+
+		(void) fdwalk(&_z_close_file_descriptors, (void *)a_fds);
+
+		/* release all held signals */
+
+		(void) sigrelse(SIGHUP);
+		(void) sigrelse(SIGINT);
+
+		/* execute command in the specified non-global zone */
+
+		_exit(_zexec(a_zoneName, a_path, a_argv));
+	}
+
+	/*
+	 * *********************************************************************
+	 * This is the forking (parent) process
+	 * *********************************************************************
+	 */
+
+	/* register child process i.d. so signal handlers can pass signal on */
+
+	_z_global_data._z_ChildProcessId = child_pid;
+
+	/*
+	 * setup signal handlers for SIGINT and SIGHUP and release hold
+	 */
+
+	/* hook SIGINT to _z_sig_trap() */
+
+	nact.sa_handler = _z_sig_trap;
+	nact.sa_flags = SA_RESTART;
+	(void) sigemptyset(&nact.sa_mask);
+
+	if (sigaction(SIGINT, &nact, &oact) < 0) {
+		funcSigint = SIG_DFL;
+	} else {
+		funcSigint = oact.sa_handler;
+	}
+
+	/* hook SIGHUP to _z_sig_trap() */
+
+	nact.sa_handler = _z_sig_trap;
+	nact.sa_flags = SA_RESTART;
+	(void) sigemptyset(&nact.sa_mask);
+
+	if (sigaction(SIGHUP, &nact, &oact) < 0) {
+		funcSighup = SIG_DFL;
+	} else {
+		funcSighup = oact.sa_handler;
+	}
+
+	/* release hold on signals */
+
+	(void) sigrelse(SIGHUP);
+	(void) sigrelse(SIGINT);
+
+	(void) ct_tmpl_clear(tmpl_fd);
+	(void) close(tmpl_fd);
+
+	(void) close(stdinfile);
+	(void) close(ipipe[1]);		/* Close write side of pipe */
+
+	/*
+	 * Spin reading data from the child into the buffer - when the read eofs
+	 * the child has exited
+	 */
+
+	for (;;) {
+		ssize_t	bytesRead;
+
+		/* read as much child data as there is available buffer space */
+
+		bytesRead = read(ipipe[0], buffer + bufferIndex,
+		    bufferSize - bufferIndex);
+
+		/* break out of read loop if end-of-file encountered */
+
+		if (bytesRead == 0) {
+			break;
+		}
+
+		/* if error, continue if recoverable, else break out of loop */
+
+		if (bytesRead == -1) {
+			/* try again: EAGAIN - insufficient resources */
+
+			if (errno == EAGAIN) {
+				continue;
+			}
+
+			/* try again: EINTR - interrupted system call */
+
+			if (errno == EINTR) {
+				continue;
+			}
+
+			/* break out of loop - error not recoverable */
+			break;
+		}
+
+		/* at least 1 byte read: expand buffer if at end */
+
+		bufferIndex += bytesRead;
+		if (bufferIndex >= bufferSize) {
+			buffer = realloc(buffer,
+			    bufferSize += PIPE_BUFFER_INCREMENT);
+			(void) memset(buffer + bufferIndex, 0,
+			    bufferSize - bufferIndex);
+		}
+	}
+
+	(void) close(ipipe[0]);		/* Close read side of pipe */
+
+	/*
+	 * wait for the process to exit, reap child exit status
+	 */
+
+	for (;;) {
+		result_pid = waitpid(child_pid, &status, 0L);
+		lerrno = (result_pid == -1 ? errno : 0);
+
+		/* break loop if child process status reaped */
+
+		if (result_pid != -1) {
+			break;
+		}
+
+		/* break loop if not interrupted out of waitpid */
+
+		if (errno != EINTR) {
+			break;
+		}
+	}
+
+	/* reset child process i.d. so signal handlers do not pass signals on */
+
+	_z_global_data._z_ChildProcessId = -1;
+
+	/*
+	 * If the child process terminated due to a call to exit(), then
+	 * set results equal to the 8-bit exit status of the child process;
+	 * otherwise, set the exit status to "-1" indicating that the child
+	 * exited via a signal.
+	 */
+
+	if (WIFEXITED(status)) {
+		*r_status = WEXITSTATUS(status);
+		if ((_z_global_data._z_SigReceived != 0) && (*r_status == 0)) {
+			*r_status = 1;
+		}
+	} else {
+		*r_status = -1;	/* -1 : failure to exec process */
+	}
+
+	/* determine proper exit code */
+
+	if (result_pid == -1) {
+		exit_no = -5;	/* -5 : error from 'waitpid' other than EINTR */
+	} else if (_z_global_data._z_SigReceived != 0) {
+		exit_no = -7;	/* -7 : interrupt received */
+	} else {
+		exit_no = 0;
+	}
+
+	/* return appropriate output */
+
+	if (!*buffer) {
+		/* No contents in output buffer - discard */
+		free(buffer);
+	} else if (r_results == (char **)NULL) {
+		/* Not requested to return results - discard */
+		free(buffer);
+	} else {
+		/* have output and request to return: pass to calling method */
+		*r_results = buffer;
+	}
+
+	/*
+	 * reset signal handlers
+	 */
+
+	/* reset SIGINT */
+
+	nact.sa_handler = funcSigint;
+	nact.sa_flags = SA_RESTART;
+	(void) sigemptyset(&nact.sa_mask);
+
+	(void) sigaction(SIGINT, &nact, (struct sigaction *)NULL);
+
+	/* reset SIGHUP */
+
+	nact.sa_handler = funcSighup;
+	nact.sa_flags = SA_RESTART;
+	(void) sigemptyset(&nact.sa_mask);
+
+	(void) sigaction(SIGHUP, &nact, (struct sigaction *)NULL);
+
+	/*
+	 * if signal received during command execution, interrupt
+	 * this process now.
+	 */
+
+	if (_z_global_data._z_SigReceived != 0) {
+		(void) kill(getpid(), SIGINT);
+	}
+
+	/* set errno and return */
+
+	errno = lerrno;
+
+	return (exit_no);
+}
+
+/*
+ * Name:	z_ExecCmdList
+ * Synopsis:	Execute Unix command and return results
+ * Description:	Execute a Unix command and return results and status
+ * Arguments:
+ *		r_status - [RO, *RW] - (int *)
+ *			Return (exit) status from Unix command
+ *		r_results - [RO, *RW] - (char **)
+ *			Any output generated by the Unix command to stdout
+ *			and to stderr
+ *			== (char *)NULL if no output generated
+ *		a_inputFile - [RO, *RO] - (char *)
+ *			Pointer to character string representing file to be
+ *			used as "standard input" for the command.
+ *			== (char *)NULL to use "/dev/null" as standard input
+ *		a_cmd - [RO, *RO] - (char *)
+ *			Pointer to character string representing the full path
+ *			of the Unix command to execute
+ *		... - [RO] (?)
+ *			Zero or more arguments to the Unix command
+ *			The argument list must be ended with (void *)NULL
+ * Returns:	int
+ *			== 0 - Command executed
+ *				Look at r_status for results of Unix command
+ *			!= 0 - problems executing command
+ *				r_status and r_results have no meaning
+ * NOTE:    	Any results returned is placed in new storage for the
+ *		calling method. The caller must use 'free' to dispose
+ *		of the storage once the results are no longer needed.
+ * NOTE:	If LU_SUCCESS is returned, 'r_status' must be queried to
+ *		determine the results of the Unix command.
+ */
+
+/*VARARGS*/
+int
+z_ExecCmdList(int *r_status, char **r_results,
+	char *a_inputFile, char *a_cmd, ...)
+{
+	va_list		ap;		/* references variable argument list */
+	char		*array[MAX_EXEC_CMD_ARGS+1];
+	int		argno = 0;
+
+	/*
+	 * Create argument array for exec system call
+	 */
+
+	bzero(array, sizeof (array));
+
+	va_start(ap, a_cmd);	/* Begin variable argument processing */
+
+	for (argno = 0; argno < MAX_EXEC_CMD_ARGS; argno++) {
+		array[argno] = va_arg(ap, char *);
+		if (array[argno] == (char *)NULL) {
+			break;
+		}
+	}
+
+	va_end(ap);
+	return (z_ExecCmdArray(r_status, r_results, a_inputFile,
+	    a_cmd, array));
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/usr/src/lib/libinstzones/common/zones_locks.c	Fri Jun 05 10:28:40 2009 -0400
@@ -0,0 +1,995 @@
+/*
+ * 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
+ * or http://www.opensolaris.org/os/licensing.
+ * 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 2009 Sun Microsystems, Inc.  All rights reserved.
+ * Use is subject to license terms.
+ */
+
+
+
+/*
+ * Module:	zones_locks.c
+ * Group:	libinstzones
+ * Description:	Provide "zones" locking interfaces for install consolidation
+ *		code
+ *
+ * Public Methods:
+ *
+ * _z_acquire_lock - acquire a lock on an object on a zone
+ * _z_adjust_lock_object_for_rootpath - Given a lock object and a root path,
+ *	if the root path is not
+ * _z_lock_zone - Acquire specified locks on specified zone
+ * _z_lock_zone_object - lock a single lock object in a specified zone
+ * _z_release_lock - release a lock held on a zone
+ * _z_unlock_zone - Released specified locks on specified zone
+ * _z_unlock_zone_object - unlock a single lock object in a specified zone
+ */
+
+/*
+ * System includes
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <fcntl.h>
+#include <ctype.h>
+#include <sys/types.h>
+#include <sys/param.h>
+#include <string.h>
+#include <strings.h>
+#include <stdarg.h>
+#include <limits.h>
+#include <errno.h>
+#include <time.h>
+#include <stropts.h>
+#include <libintl.h>
+#include <locale.h>
+#include <assert.h>
+
+/*
+ * local includes
+ */
+
+#include "instzones_lib.h"
+#include "zones_strings.h"
+
+/*
+ * Private structures
+ */
+
+/*
+ * Library Function Prototypes
+ */
+
+/*
+ * Local Function Prototypes
+ */
+
+boolean_t	_z_adjust_lock_object_for_rootpath(char **r_result,
+		    char *a_lockObject);
+boolean_t	_z_acquire_lock(char **r_lockKey, char *a_zoneName,
+		    char *a_lock, pid_t a_pid, boolean_t a_wait);
+boolean_t	_z_lock_zone(zoneListElement_t *a_zlst,
+		    ZLOCKS_T a_lflags);
+boolean_t	_z_lock_zone_object(char **r_objectLocks,
+		    char *a_zoneName, char *a_lockObject,
+		    pid_t a_pid, char *a_waitingMsg,
+		    char *a_busyMsg);
+boolean_t	_z_release_lock(char *a_zoneName, char *a_lock,
+		    char *a_key, boolean_t a_wait);
+		    boolean_t	_z_unlock_zone(zoneListElement_t *a_zlst,
+		    ZLOCKS_T a_lflags);
+boolean_t	_z_unlock_zone_object(char **r_objectLocks,
+		    char *a_zoneName, char *a_lockObject,
+		    char *a_errMsg);
+
+/*
+ * global internal (private) declarations
+ */
+
+/*
+ * *****************************************************************************
+ * global external (public) functions
+ * *****************************************************************************
+ */
+
+/*
+ * Name:	_z_acquire_lock
+ * Description:	acquire a lock on an object on a zone
+ * Arguments:	r_lockKey - [RW, *RW] - (char *)
+ *			Pointer to handle to string representing the lock key
+ *			associated with the lock object to be acquired - this
+ *			key is returned when the lock is acquired and must be
+ *			provided when releasing the lock
+ *			== (char *)NULL - lock not acquired
+ *		a_zoneName - [RO, *RO] - (char *)
+ *			Pointer to string representing the name of the zone to
+ *			acquire the specified lock on
+ *		a_lockObject - [RO, *RO] - (char *)
+ *			Pointer to string representing the lock object to
+ *			acquire on the specified zone
+ *		a_pid - [RO, *RO] - (pid_t)
+ *			Process i.d. to associate with this lock
+ *			== 0 - no process i.d. associated with the lock
+ *		a_wait - [RO, *RO] - (int)
+ *			Determines what to do if the lock cannot be acquired:
+ *			== B_TRUE - wait for the lock to be acquired
+ *			== B_FALSE - do not wait for the lock to be acquired
+ * Returns:	boolean_t
+ *			B_TRUE - lock acquired
+ *			B_FALSE - lock not acquired
+ */
+
+boolean_t
+_z_acquire_lock(char **r_lockKey, char *a_zoneName, char *a_lockObject,
+	pid_t a_pid, boolean_t a_wait)
+{
+	argArray_t	*args;
+	boolean_t	b;
+	char		*adjustedLockObject = (char *)NULL;
+	char		*p;
+	char		*results = (char *)NULL;
+	int		r;
+	int		status;
+
+	/* entry assertions */
+
+	assert(a_zoneName != (char *)NULL);
+	assert(a_lockObject != (char *)NULL);
+	assert(*a_lockObject != '\0');
+	assert(r_lockKey != (char **)NULL);
+
+	/* entry debugging info */
+
+	_z_echoDebug(DBG_ZONES_APLK, a_zoneName, a_lockObject, a_pid);
+
+	/* reset returned lock key handle */
+
+	*r_lockKey = (char *)NULL;
+
+	/*
+	 * Only one lock file must ever be used - the one located on the root
+	 * file system of the currently running Solaris instance. To allow for
+	 * alternative roots to be properly locked, adjust the lock object to
+	 * take root path into account; if necessary, the root path will be
+	 * prepended to the lock object.
+	 */
+
+	b = _z_adjust_lock_object_for_rootpath(&adjustedLockObject,
+	    a_lockObject);
+	if (!b) {
+		return (B_FALSE);
+	}
+
+	/*
+	 * construct command arguments:
+	 * pkgadm lock -a -q -o adjustedLockObject [ -w -W timeout ]
+	 *		[ -p a_pid -z zoneid ]
+	 */
+
+	args = _z_new_args(20);			/* generate new arg list */
+	(void) _z_add_arg(args, PKGADM_CMD);	/* pkgadm command */
+	(void) _z_add_arg(args, "lock");		/* lock sub-command */
+	(void) _z_add_arg(args, "-a");		/* acquire lock */
+	(void) _z_add_arg(args, "-q");		/* quiet (no extra messages) */
+	(void) _z_add_arg(args, "-o");		/* object to acquire */
+	(void) _z_add_arg(args, "%s", adjustedLockObject);
+
+	/* add [ -w -W timeout ] if waiting for lock */
+
+	if (a_wait == B_TRUE) {
+		(void) _z_add_arg(args, "-w");		/* wait */
+		(void) _z_add_arg(args, "-W");		/* wait timeout */
+		(void) _z_add_arg(args, "%ld",
+		    (long)MAX_RETRIES*RETRY_DELAY_SECS);
+	}
+
+	/* add process/zone i.d.s if process i.d. provided */
+
+	if (a_pid > 0) {
+		(void) _z_add_arg(args, "-p");	/* lock valid process i.d. */
+		(void) _z_add_arg(args, "%ld", getpid());
+		(void) _z_add_arg(args, "-z");	/* lock valid zone i.d. */
+		(void) _z_add_arg(args, "%ld", getzoneid());
+	}
+
+	/* execute command */
+
+	r = _z_zone_exec(&status, &results, (char *)NULL, PKGADM_CMD,
+	    _z_get_argv(args), a_zoneName, (int *)NULL);
+
+	/* free generated argument list */
+
+	_z_free_args(args);
+
+	/* return error if failed to acquire */
+
+	if ((r != 0) || (status != 0)) {
+		_z_echoDebug(DBG_ZONES_APLK_EXIT, a_zoneName,
+		    adjustedLockObject, a_pid, r, status,
+		    results ? results : "");
+
+		/* free up results if returned */
+		if (results) {
+			free(results);
+		}
+
+		/* free adjusted lock object */
+		free(adjustedLockObject);
+
+		/* return failure */
+		return (B_FALSE);
+	}
+
+	/* return success if no results returned */
+
+	if (results == (char *)NULL) {
+		return (B_TRUE);
+	}
+
+	/* return the lock key */
+
+	p = _z_strGetToken((char *)NULL, results, 0, "\n");
+	_z_strRemoveLeadingWhitespace(&p);
+	*r_lockKey = p;
+
+	/* exit debugging info */
+
+	_z_echoDebug(DBG_ZONES_APLK_RESULTS, a_zoneName, adjustedLockObject, p,
+	    results);
+
+	/* free up results */
+
+	free(results);
+
+	/* free adjusted lock object */
+
+	free(adjustedLockObject);
+
+	/* return success */
+
+	return (B_TRUE);
+}
+
+/*
+ * Name:	_z_adjust_lock_object_for_rootpath
+ * Description:	Given a lock object and a root path, if the root path is not
+ *		the current running system root, then alter the lock object
+ *		to contain a reference to the root path. Only one lock file must
+ *		ever be used to create and maintain locks - the lock file that
+ *		is located in /tmp on the root file system of the currently
+ *		running Solaris instance. To allow for alternative roots to be
+ *		properly locked, if necessary adjust the lock object to take
+ *		root path into account. If the root path does not indicate the
+ *		current running Solaris instance, then the root path will be
+ *		prepended to the lock object.
+ * Arguments:	r_result - [RW, *RW] - (char **)
+ *			Pointer to handle to character string that will contain
+ *			the lock object to use.
+ *		a_lockObject - [RO, *RO] - (char *)
+ *			Pointer to string representing the lock object to adjust
+ * Returns:	boolean_t
+ *			B_TRUE - lock object adjusted and returned
+ *			B_FALSE - unable to adjust lock object
+ * NOTE:    	Any string returned is placed in new storage for the
+ *		calling function. The caller must use 'free' to dispose
+ *		of the storage once the string is no longer needed.
+ *
+ * A lock object has this form:
+ *
+ * name.value [ /name.value [ /name.value ... ] ]
+ *
+ * The "value is either a specific object or a "*", for example:
+ *
+ *     package.test
+ *
+ * This locks the package "test"
+ *
+ *     zone.* /package.*
+ *
+ * This locks all packages on all zones.
+ *
+ *     zone.* /package.SUNWluu
+ *
+ * This locks the package SUNWluu on all zones.
+ *
+ * If a -R rootpath is specified, since there is only one lock file in
+ * the current /tmp, the lock object is modified to include the root
+ * path:
+ *
+ *     rootpath.rootpath/zone.* /package.*
+ *
+ * locks all packages on all zones in the root path "?"
+ *
+ * The characters "/" and "*" and "." cannot be part of the "value"; that
+ * is if "-R /tmp/gmg*dir.test-path" is specified, the final object
+ * cannot be:
+ *
+ *     rootpath./tmp/gmg*dir.test-path/zone.* /package.*
+ *
+ * This would be parsed as:
+ *
+ *      "rootpath." "/tmp" "gmg*dir.test-path" "zone.*" "package.*"
+ *
+ * which is not correct.
+ *
+ * So the path is modified by the loop, in this case it would result in
+ * this lock object:
+ *
+ *     rootpath.-1tmp-1gmg-3dir-2test---path/zone.* /package.*
+ *
+ * This is parsed as:
+ *
+ *     "rootpath.-1tmp-1gmg-3dir-2test---path" "zone.*" "package.*"
+ *
+ * which is then interpreted as:
+ *
+ *     "rootpath./tmp/gmg*dir.test-path" "zone.*" "package.*"
+ */
+
+boolean_t
+_z_adjust_lock_object_for_rootpath(char **r_result, char *a_lockObject)
+{
+	char		realRootPath[PATH_MAX] = {'\0'};
+	const char	*a_rootPath;
+
+	/* entry assertions */
+
+	assert(r_result != (char **)NULL);
+	assert(a_lockObject != (char *)NULL);
+	assert(*a_lockObject != '\0');
+
+	/* reset returned lock object handle */
+
+	*r_result = (char *)NULL;
+
+	/*
+	 * if root path points to "/" return a duplicate of the passed in
+	 * lock objects; otherwise, resolve root path and adjust lock object by
+	 * prepending the rootpath to the lock object (using LOBJ_ROOTPATH).
+	 */
+
+	a_rootPath = _z_global_data._z_root_dir;
+	if ((a_rootPath == (char *)NULL) ||
+	    (*a_rootPath == '\0') ||
+	    (strcmp(a_rootPath, "/") == 0)) {
+
+		/* root path not specified or is only "/" - no -R specified */
+
+		*r_result = _z_strdup(a_lockObject);
+	} else {
+		/*
+		 * root path is not "" or "/" - -R to an alternative root has
+		 * been specified; resolve all symbolic links and relative nodes
+		 * of path name and determine absolute path to the root path.
+		 */
+
+		if (realpath(a_rootPath, realRootPath) == (char *)NULL) {
+			/* cannot determine absolute path; use path specified */
+			(void) strlcpy(realRootPath, a_rootPath,
+			    sizeof (realRootPath));
+		}
+
+		/*
+		 * if root path points to "/" duplicate existing lock object;
+		 * otherwise, resolve root path and adjust lock object by
+		 * prepending the rootpath to the lock object
+		 */
+
+		if (strcmp(realRootPath, "/") == 0) {
+			*r_result = _z_strdup(a_lockObject);
+		} else {
+			char *p1, *p2, *p3;
+
+			/* prefix out /.* which cannot be part of lock object */
+
+			p1 = _z_calloc((strlen(realRootPath)*2)+1);
+			for (p3 = p1, p2 = realRootPath; *p2 != '\0'; p2++) {
+				switch (*p2) {
+				case '/':	/* / becomes -1 */
+					*p3++ = '-';
+					*p3++ = '1';
+					break;
+				case '.':	/* . becomes -2 */
+					*p3++ = '-';
+					*p3++ = '2';
+					break;
+				case '*':	/* * becomes -3 */
+					*p3++ = '-';
+					*p3++ = '3';
+					break;
+				case '-':	/* - becomes -- */
+					*p3++ = '-';
+					*p3++ = '-';
+					break;
+				default:	/* do not prefix out char */
+					*p3++ = *p2;
+					break;
+				}
+			}
+
+			/* create "realpath.%s" object */
+
+			p2 = _z_strPrintf(LOBJ_ROOTPATH, p1);
+			free(p1);
+			if (p2 == (char *)NULL) {
+				_z_program_error(ERR_MALLOC, "<path>", errno,
+				    strerror(errno));
+				return (B_FALSE);
+			}
+
+			/* create "realpath.%s/..." final lock object */
+
+			*r_result = _z_strPrintf("%s/%s", p2, a_lockObject);
+			free(p2);
+			if (*r_result == (char *)NULL) {
+				_z_program_error(ERR_MALLOC, "<path>", errno,
+				    strerror(errno));
+				return (B_FALSE);
+			}
+		}
+	}
+
+	/* exit debugging info */
+
+	_z_echoDebug(DBG_ZONES_ADJLCKOBJ_EXIT, a_lockObject, *r_result,
+	    a_rootPath ? a_rootPath : "",
+	    realRootPath ? realRootPath : "");
+
+	/* return success */
+
+	return (B_TRUE);
+}
+
+/*
+ * Name:	_z_lock_zone
+ * Description:	Acquire specified locks on specified zone
+ * Arguments:	a_zlst - [RO, *RW] - (zoneListElement_t *)
+ *			Pointer to zone list structure element describing
+ *			the zone the lock - the structure is updated with
+ *			the lock objects and keys if the locks are acquired
+ *		a_lflags - [RO, *RO] - (ZLOCKS_T)
+ *			Flags indicating which locks to acquire on the zone
+ * Returns:	boolean_t
+ *			== B_TRUE - locks successfully acquired
+ *			== B_FALSE - failed to acquire the locks
+ */
+
+boolean_t
+_z_lock_zone(zoneListElement_t *a_zlst, ZLOCKS_T a_lflags)
+{
+	char *scratchName;
+	boolean_t	b;
+
+	/* entry assertions */
+
+	assert(a_zlst != (zoneListElement_t *)NULL);
+
+	/* entry debugging info */
+
+	_z_echoDebug(DBG_ZONES_LCK_ZONE, a_zlst->_zlName, a_lflags);
+
+	scratchName = a_zlst->_zlScratchName == NULL ? a_zlst->_zlName :
+	    a_zlst->_zlScratchName;
+
+	/*
+	 * acquire zone lock
+	 */
+
+	if (a_lflags & ZLOCKS_ZONE_ADMIN) {
+		/*
+		 * lock zone administration if not already locked
+		 * if the lock cannot be released, stop and return an error
+		 */
+
+		_z_echoDebug(DBG_ZONES_LCK_ZONE_ZONEADM, a_zlst->_zlName,
+		    LOBJ_ZONEADMIN);
+
+		b = _z_lock_zone_object(&a_zlst->_zlLockObjects,
+		    scratchName, LOBJ_ZONEADMIN, (pid_t)0,
+		    MSG_ZONES_LCK_ZONE_ZONEADM,
+		    ERR_ZONES_LCK_ZONE_ZONEADM);
+		if (b == B_FALSE) {
+			return (b);
+		}
+	}
+
+	/*
+	 * acquire package lock
+	 */
+
+	if (a_lflags & ZLOCKS_PKG_ADMIN) {
+
+		/*
+		 * zone administration is locked; lock package administration if
+		 * not already locked; if the lock cannot be released, stop,
+		 * release the zone administration lock and return an error
+		 */
+
+		_z_echoDebug(DBG_ZONES_LCK_ZONE_PKGADM, a_zlst->_zlName,
+		    LOBJ_PKGADMIN);
+
+		b = _z_lock_zone_object(&a_zlst->_zlLockObjects,
+		    scratchName, LOBJ_PKGADMIN, (pid_t)0,
+		    MSG_ZONES_LCK_ZONE_PKGADM,
+		    ERR_ZONES_LCK_ZONE_PKGADM);
+		if (b == B_FALSE) {
+			(void) _z_unlock_zone(a_zlst, a_lflags);
+			return (b);
+		}
+	}
+
+	/*
+	 * acquire patch lock
+	 */
+
+	if (a_lflags & ZLOCKS_PATCH_ADMIN) {
+
+		/*
+		 * zone and package administration is locked; lock patch
+		 * administration; if the lock cannot be released, stop,
+		 * release the other locks and return an error
+		 */
+
+		_z_echoDebug(DBG_ZONES_LCK_ZONE_PATCHADM, a_zlst->_zlName,
+		    LOBJ_PATCHADMIN);
+
+		b = _z_lock_zone_object(&a_zlst->_zlLockObjects,
+		    scratchName, LOBJ_PATCHADMIN, (pid_t)0,
+		    MSG_ZONES_LCK_ZONE_PATCHADM,
+		    ERR_ZONES_LCK_ZONE_PATCHADM);
+		if (b == B_FALSE) {
+			(void) _z_unlock_zone(a_zlst, a_lflags);
+			return (b);
+		}
+	}
+
+	/*
+	 * all locks have been obtained - return success!
+	 */
+
+	return (B_TRUE);
+}
+
+/*
+ * Name:	_z_lock_zone_object
+ * Description:	lock a single lock object in a specified zone
+ * Arguments:	r_objectLocks - [RW, *RW] - (char **)
+ *			Pointer to handle to character string containing a list
+ *			of all objects locked for this zone - this string will
+ *			have the key to release the specified object added to it
+ *			if the lock is acquired.
+ *		a_zoneName - [RO, *RO] - (char *)
+ *			Pointer to string representing the name of the zone to
+ *			acquire the specified lock on
+ *		a_lockObject - [RO, *RO] - (char *)
+ *			Pointer to string representing the lock object to
+ *			acquire on the specified zone
+ *		a_pid - [RO, *RO] - (pid_t)
+ *			Process i.d. to associate with this lock
+ *			== 0 - no process i.d. associated with the lock
+ *		a_waitingMsg - [RO, *RO] - (char *)
+ *			Localized message to be output if waiting for the lock
+ *			because the lock cannot be immediately be acquired
+ *		a_busyMsg - [RO, *RO] - (char *)
+ *			Localized message to be output if the lock cannot be
+ *			released
+ * Returns:	boolean_t
+ *			B_TRUE - lock released
+ *			B_FALSE - lock not released
+ */
+
+boolean_t
+_z_lock_zone_object(char **r_objectLocks, char *a_zoneName, char *a_lockObject,
+	pid_t a_pid, char *a_waitingMsg, char *a_busyMsg)
+{
+	boolean_t	b;
+	char		*p = (char *)NULL;
+	char		lockItem[LOCK_OBJECT_MAXLEN+LOCK_KEY_MAXLEN+4];
+	char		lockKey[LOCK_KEY_MAXLEN+2];
+	char		lockObject[LOCK_OBJECT_MAXLEN+2];
+	int		i;
+
+	/* entry assertions */
+
+	assert(r_objectLocks != (char **)NULL);
+	assert(a_zoneName != (char *)NULL);
+	assert(a_waitingMsg != (char *)NULL);
+	assert(a_busyMsg != (char *)NULL);
+	assert(a_lockObject != (char *)NULL);
+	assert(*a_lockObject != '\0');
+
+	/* entry debugging info */
+
+	_z_echoDebug(DBG_ZONES_LCK_OBJ, a_lockObject, a_zoneName, a_pid,
+	    *r_objectLocks ? *r_objectLocks : "");
+
+	/* if lock objects held search for object to lock */
+
+	if (*r_objectLocks != (char *)NULL) {
+		for (i = 0; ; i++) {
+			/* get next object locked on this zone */
+			_z_strGetToken_r((char *)NULL, *r_objectLocks, i, "\n",
+			    lockItem, sizeof (lockItem));
+
+			/* break out of loop if no more locks in list */
+
+			if (lockItem[0] == '\0') {
+				_z_echoDebug(DBG_ZONES_LCK_OBJ_NOTHELD,
+				    a_lockObject, a_zoneName);
+				break;
+			}
+
+			/* get object and key for this lock */
+			_z_strGetToken_r((char *)NULL, lockItem, 0, "\t",
+			    lockObject, sizeof (lockObject));
+			_z_strGetToken_r((char *)NULL, lockItem, 1, "\t",
+			    lockKey, sizeof (lockKey));
+
+			/* return success if the lock is held */
+
+			if (strcmp(lockObject, a_lockObject) == 0) {
+				_z_echoDebug(DBG_ZONES_LCK_OBJ_FOUND,
+				    lockObject, lockKey);
+				return (B_TRUE);
+			}
+
+			/* not the object to lock - scan next object */
+			_z_echoDebug(DBG_ZONES_LCK_OBJ_NOTFOUND, lockObject,
+			    lockKey);
+		}
+	}
+
+	/*
+	 * the object to lock is not held - acquire the lock
+	 */
+
+	/* acquire object with no wait */
+	b = _z_acquire_lock(&p, a_zoneName, a_lockObject, a_pid, B_FALSE);
+	if (b == B_FALSE) {
+		/* failure - output message and acquire with wait */
+		_z_echo(a_waitingMsg, (long)MAX_RETRIES*RETRY_DELAY_SECS,
+		    a_zoneName, _z_global_data._z_root_dir);
+		b = _z_acquire_lock(&p, a_zoneName, a_lockObject, a_pid,
+		    B_TRUE);
+	}
+
+	/* output error message and return failure if both acquires failed */
+	if (b == B_FALSE) {
+		_z_program_error(a_busyMsg, a_zoneName);
+		return (b);
+	}
+
+	/* add object/key to held locks */
+
+	_z_strPrintf_r(lockItem, sizeof (lockItem), "%s\t%s", a_lockObject, p);
+	_z_strAddToken(r_objectLocks, lockItem, '\n');
+
+	free(p);
+
+	/* return success */
+	return (B_TRUE);
+}
+
+/*
+ * Name:	_z_release_lock
+ * Description:	release a lock held on a zone
+ * Arguments:	a_zoneName - [RO, *RO] - (char *)
+ *			Pointer to string representing the name of the zone to
+ *			release the specified lock on
+ *		a_lockObject - [RO, *RO] - (char *)
+ *			Pointer to string representing the lock object to
+ *			release on the specified zone
+ *		a_lockKey - [RO, *RO] - (char *)
+ *			Pointer to string representing the lock key associated
+ *			with the lock object to be released - this key is
+ *			returned when the lock is acquired and must be provided
+ *			when releasing the lock
+ *		a_wait - [RO, *RO] - (int)
+ *			Determines what to do if the lock cannot be released:
+ *			== B_TRUE - wait for the lock to be released
+ *			== B_FALSE - do not wait for the lock to be released
+ * Returns:	boolean_t
+ *			B_TRUE - lock released
+ *			B_FALSE - lock not released
+ */
+
+boolean_t
+_z_release_lock(char *a_zoneName, char *a_lockObject, char *a_lockKey,
+	boolean_t a_wait)
+{
+	argArray_t	*args;
+	boolean_t	b;
+	char		*adjustedLockObject = (char *)NULL;
+	char		*results = (char *)NULL;
+	int		r;
+	int		status;
+
+	/* entry assertions */
+
+	assert(a_zoneName != (char *)NULL);
+	assert(a_lockObject != (char *)NULL);
+	assert(*a_lockObject != '\0');
+	assert(a_lockKey != (char *)NULL);
+	assert(*a_lockKey != '\0');
+
+	/* entry debugging info */
+
+	_z_echoDebug(DBG_ZONES_RELK, a_zoneName, a_lockObject,
+	    a_lockKey ? a_lockKey : "");
+
+	/*
+	 * Only one lock file must ever be used - the one located on the root
+	 * file system of the currently running Solaris instance. To allow for
+	 * alternative roots to be properly locked, adjust the lock object to
+	 * take root path into account; if necessary, the root path will be
+	 * prepended to the lock object.
+	 */
+
+	b = _z_adjust_lock_object_for_rootpath(&adjustedLockObject,
+	    a_lockObject);
+	if (!b) {
+		return (B_FALSE);
+	}
+
+	/*
+	 * construct command arguments:
+	 * pkgadm lock -r -o adjustedLockObject -k a_lockKey [-w -W timeout]
+	 */
+
+	args = _z_new_args(20);			/* generate new arg list */
+	(void) _z_add_arg(args, PKGADM_CMD);		/* pkgadm command */
+	(void) _z_add_arg(args, "lock");		/* lock sub-command */
+	(void) _z_add_arg(args, "-r");			/* release lock */
+	(void) _z_add_arg(args, "-o");			/* object to release */
+	(void) _z_add_arg(args, "%s", adjustedLockObject);
+	(void) _z_add_arg(args, "-k");			/* object's key */
+	(void) _z_add_arg(args, "%s", a_lockKey);
+
+	/* add [ -w -W timeout ] if waiting for lock */
+
+	if (a_wait == B_TRUE) {
+		(void) _z_add_arg(args, "-w");		/* wait */
+		(void) _z_add_arg(args, "-W");		/* wait timeout */
+		(void) _z_add_arg(args, "%ld",
+		    (long)MAX_RETRIES*RETRY_DELAY_SECS);
+	}
+
+	/* execute command */
+
+	r = _z_zone_exec(&status, &results, (char *)NULL, PKGADM_CMD,
+	    _z_get_argv(args), a_zoneName, (int *)NULL);
+
+	/* free generated argument list */
+
+	_z_free_args(args);
+
+	/* exit debugging info */
+
+	_z_echoDebug(DBG_ZONES_RELK_EXIT, adjustedLockObject, a_lockKey,
+	    a_zoneName, r, status, results ? results : "");
+
+	/* free adjusted lock object */
+
+	free(adjustedLockObject);
+	free(results);
+
+	return (((r == 0) && (status == 0)) ? B_TRUE : B_FALSE);
+}
+
+
+
+/*
+ * Name:	_z_unlock_zone
+ * Description:	Released specified locks on specified zone
+ * Arguments:	a_zlst - [RO, *RW] - (zoneListElement_t *)
+ *			Pointer to zone list structure element describing
+ *			the zone the unlock - the structure is updated by
+ *			removing the lock object and key if the locks are
+ *			successfully released
+ *		a_lflags - [RO, *RO] - (ZLOCKS_T)
+ *			Flags indicating which locks to release on the zone
+ * Returns:	boolean_t
+ *			== B_TRUE - locks successfully released
+ *			== B_FALSE - failed to release the locks
+ */
+
+boolean_t
+_z_unlock_zone(zoneListElement_t *a_zlst, ZLOCKS_T a_lflags)
+{
+	char		*scratchName;
+	boolean_t	b;
+	boolean_t	errors = B_FALSE;
+
+	/* entry assertions */
+
+	assert(a_zlst != (zoneListElement_t *)NULL);
+
+	/* entry debugging info */
+
+	_z_echoDebug(DBG_ZONES_ULK_ZONE, a_zlst->_zlName, a_lflags);
+
+	scratchName = a_zlst->_zlScratchName == NULL ? a_zlst->_zlName :
+	    a_zlst->_zlScratchName;
+
+	if (a_lflags & ZLOCKS_PATCH_ADMIN) {
+		/*
+		 * if locked, unlock patch administration lock
+		 * if the lock cannot be released, continue anyway
+		 */
+
+		_z_echoDebug(DBG_ZONES_ULK_ZONE_PATCHADM, a_zlst->_zlName,
+		    LOBJ_PATCHADMIN);
+
+		b = _z_unlock_zone_object(&a_zlst->_zlLockObjects,
+		    scratchName, LOBJ_PATCHADMIN,
+		    WRN_ZONES_ULK_ZONE_PATCHADM);
+		if (b == B_FALSE) {
+			errors = B_TRUE;
+		}
+	}
+
+	if (a_lflags & ZLOCKS_PKG_ADMIN) {
+		/*
+		 * if locked, unlock package administration lock
+		 * if the lock cannot be released, continue anyway
+		 */
+
+		_z_echoDebug(DBG_ZONES_ULK_ZONE_PKGADM, a_zlst->_zlName,
+		    LOBJ_PKGADMIN);
+
+		b = _z_unlock_zone_object(&a_zlst->_zlLockObjects,
+		    scratchName, LOBJ_PKGADMIN,
+		    WRN_ZONES_ULK_ZONE_PKGADM);
+		if (b == B_FALSE) {
+			errors = B_TRUE;
+		}
+	}
+
+	if (a_lflags & ZLOCKS_ZONE_ADMIN) {
+
+		/*
+		 * if locked, unlock zone administration lock
+		 * if the lock cannot be released, continue anyway
+		 */
+
+		_z_echoDebug(DBG_ZONES_ULK_ZONE_ZONEADM, a_zlst->_zlName,
+		    LOBJ_ZONEADMIN);
+
+		b = _z_unlock_zone_object(&a_zlst->_zlLockObjects,
+		    scratchName, LOBJ_ZONEADMIN,
+		    WRN_ZONES_ULK_ZONE_ZONEADM);
+		if (b == B_FALSE) {
+			errors = B_TRUE;
+		}
+	}
+
+	return (!errors);
+}
+
+/*
+ * Name:	_z_unlock_zone_object
+ * Description:	unlock a single lock object in a specified zone
+ * Arguments:	r_objectLocks - [RW, *RW] - (char **)
+ *			Pointer to handle to character string containing a list
+ *			of all objects locked for this zone - this string must
+ *			contain the key to release the specified object - if not
+ *			then the lock is not released - if so then the lock is
+ *			released and the key is removed from this list.
+ *		a_zoneName - [RO, *RO] - (char *)
+ *			Pointer to string representing the name of the zone to
+ *			release the specified lock on
+ *		a_lockObject - [RO, *RO] - (char *)
+ *			Pointer to string representing the lock object to
+ *			release on the specified zone
+ *		a_errMsg - [RO, *RO] - (char *)
+ *			Localized message to be output if the lock cannot be
+ *			released
+ * Returns:	boolean_t
+ *			B_TRUE - lock released
+ *			B_FALSE - lock not released
+ */
+
+boolean_t
+_z_unlock_zone_object(char **r_objectLocks, char *a_zoneName,
+	char *a_lockObject, char *a_errMsg)
+{
+	boolean_t	b;
+	char		lockItem[LOCK_OBJECT_MAXLEN+LOCK_KEY_MAXLEN+4];
+	char		lockKey[LOCK_KEY_MAXLEN+2];
+	char		lockObject[LOCK_OBJECT_MAXLEN+2];
+	int		i;
+
+	/* entry assertions */
+
+	assert(r_objectLocks != (char **)NULL);
+	assert(a_zoneName != (char *)NULL);
+	assert(a_errMsg != (char *)NULL);
+	assert(a_lockObject != (char *)NULL);
+	assert(*a_lockObject != '\0');
+
+	/* entry debugging info */
+
+	_z_echoDebug(DBG_ZONES_ULK_OBJ, a_lockObject, a_zoneName,
+	    *r_objectLocks ? *r_objectLocks : "");
+
+	/* return success if no objects are locked */
+
+	if (*r_objectLocks == (char *)NULL) {
+		_z_echoDebug(DBG_ZONES_ULK_OBJ_NONE, a_zoneName);
+		return (B_TRUE);
+	}
+
+	/* see if the specified lock is held on this zone */
+
+	for (i = 0; ; i++) {
+		/* get next object locked on this zone */
+		_z_strGetToken_r((char *)NULL, *r_objectLocks, i, "\n",
+		    lockItem, sizeof (lockItem));
+
+		/* return success if no more objects locked */
+		if (lockItem[0] == '\0') {
+			_z_echoDebug(DBG_ZONES_ULK_OBJ_NOTHELD, a_lockObject,
+			    a_zoneName);
+			return (B_TRUE);
+		}
+
+		/* get object and key for this lock */
+		_z_strGetToken_r((char *)NULL, lockItem, 0, "\t",
+		    lockObject, sizeof (lockObject));
+		_z_strGetToken_r((char *)NULL, lockItem, 1, "\t",
+		    lockKey, sizeof (lockKey));
+
+		/* break out of loop if object is the one to unlock */
+
+		if (strcmp(lockObject, a_lockObject) == 0) {
+			_z_echoDebug(DBG_ZONES_ULK_OBJ_FOUND, lockObject,
+			    lockKey);
+			break;
+		}
+
+		/* not the object to unlock - scan next object */
+		_z_echoDebug(DBG_ZONES_ULK_OBJ_NOTFOUND, lockObject, lockKey);
+	}
+
+	/*
+	 * the object to unlock is held - release the lock
+	 */
+
+	/* release object with wait */
+
+	b = _z_release_lock(a_zoneName, a_lockObject, lockKey, B_TRUE);
+	if (b == B_FALSE) {
+		/* failure - issue error message and return failure */
+		_z_program_error(a_errMsg, a_zoneName);
+		return (b);
+	}
+
+	/* remove object/key from held locks */
+
+	_z_strRemoveToken(r_objectLocks, lockItem, "\n", 0);
+
+	/* return success */
+
+	return (B_TRUE);
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/usr/src/lib/libinstzones/common/zones_lofs.c	Fri Jun 05 10:28:40 2009 -0400
@@ -0,0 +1,288 @@
+/*
+ * 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
+ * or http://www.opensolaris.org/os/licensing.
+ * 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 2009 Sun Microsystems, Inc.  All rights reserved.
+ * Use is subject to license terms.
+ */
+
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+#include <libintl.h>
+#include <errno.h>
+#include <assert.h>
+#include <sys/types.h>
+#include <sys/mntent.h>
+#include <sys/mnttab.h>
+#include <sys/param.h>
+#include <libzonecfg.h>
+#include "zones_strings.h"
+#include "instzones_lib.h"
+
+#define	MNTTAB	"/etc/mnttab"
+
+#define	MNTTAB_HUNK	32
+
+static struct mnttab *mountTable;
+static size_t mountTableSize = 0;
+static boolean_t createdFlag = B_FALSE;
+
+/*
+ * Name		: z_createMountTable
+ * Description	: Populate the mountTable Array with mnttab entries
+ * Arguments	: void
+ * Returns	: int
+ *		  0: The Mount Table was succesfully initialized
+ *		 -1: There was an error during initialisation
+ */
+int
+z_createMountTable(void)
+{
+	FILE *fp;
+	struct mnttab ent;
+	struct mnttab *entp;
+
+	if (createdFlag) {
+		return (0);
+	}
+
+	fp = fopen(MNTTAB, "r");
+	if (fp == NULL) {
+		_z_program_error(ERR_OPEN_READ, MNTTAB, errno,
+		    strerror(errno));
+		return (-1);
+	}
+
+	/* Put the entries into the table */
+	mountTable = NULL;
+	mountTableSize = 0;
+	createdFlag = B_TRUE;
+	while (getmntent(fp, &ent) == 0) {
+		if (mountTableSize % MNTTAB_HUNK == 0) {
+			mountTable = _z_realloc(mountTable,
+			    (mountTableSize + MNTTAB_HUNK) * sizeof (ent));
+		}
+		entp = &mountTable[mountTableSize++];
+
+		/*
+		 * Zero out any fields we're not using.
+		 */
+		(void) memset(entp, 0, sizeof (*entp));
+
+		if (ent.mnt_special != NULL)
+			entp->mnt_special = _z_strdup(ent.mnt_special);
+		if (ent.mnt_mntopts != NULL)
+			entp->mnt_mntopts = _z_strdup(ent.mnt_mntopts);
+		entp->mnt_mountp = _z_strdup(ent.mnt_mountp);
+		entp->mnt_fstype = _z_strdup(ent.mnt_fstype);
+	}
+
+	(void) fclose(fp);
+	return (0);
+}
+
+/*
+ * Name		: findPathRWStatus
+ * Description	: Check whether the given path is an mnttab entry
+ * Arguments	: char * - The Path to be verified
+ * Returns	: int
+ *		  -1: The Path is NOT present in the table (mnttab)
+ *		   0: The Path is present in the table and is mounted read-only
+ *		   1: The Path is present in the table and is mounted read-write
+ */
+static int
+findPathRWStatus(const char *a_path)
+{
+	int i;
+
+	for (i = 0; i < mountTableSize; i++) {
+		if (strcmp(a_path, mountTable[i].mnt_mountp) == 0) {
+			if (hasmntopt(&mountTable[i], MNTOPT_RO) != NULL) {
+				return (0);
+			} else {
+				return (1);
+			}
+		}
+	}
+
+	return (-1);
+}
+
+
+/*
+ * Name		: z_isPathWritable
+ * Description	: Check if the given path is in a writable area
+ * Arguments	: char * - The Path to be verified
+ * Returns	: int
+ *		   0: The Path is under a read-only mount
+ *		   1: The Path is under a read-write mount
+ * NOTE		: This funcion automatically initialises
+ *		  the mountPoint table if needed.
+ */
+int
+z_isPathWritable(const char *a_str)
+{
+	int i, result, slen;
+	char a_path[MAXPATHLEN];
+
+	if (!createdFlag) {
+		if (z_createMountTable() != 0) {
+			return (1);
+		}
+	}
+
+	(void) strlcpy(a_path, a_str, sizeof (a_path));
+	slen = strlen(a_path);
+
+	/*
+	 * This for loop traverses Path backwards, incrementally removing the
+	 * basename of Path and looking for the resultant directory in the
+	 * mnttab.  Once found, it returns the rw status of that file system.
+	 */
+	for (i = slen; i > 0; i--) {
+		if ((a_path[i] == '/') || (a_path[i] == '\0')) {
+			a_path[i] = '\0';
+			result = findPathRWStatus(a_path);
+			if (result != -1) {
+				return (result);
+			}
+		}
+	}
+
+	return (1);
+}
+
+/*
+ * Name		: z_destroyMountTable
+ * Description	: Clear the entries in the mount table
+ * Arguments	: void
+ * Returns	: void
+ */
+void
+z_destroyMountTable(void)
+{
+	int i;
+
+	if (!createdFlag) {
+		return;
+	}
+
+	if (mountTable == NULL) {
+		return;
+	}
+
+	for (i = 0; i < mountTableSize; i++) {
+		free(mountTable[i].mnt_mountp);
+		free(mountTable[i].mnt_fstype);
+		free(mountTable[i].mnt_special);
+		free(mountTable[i].mnt_mntopts);
+		assert(mountTable[i].mnt_time == NULL);
+	}
+
+	free(mountTable);
+	mountTable = NULL;
+	mountTableSize = 0;
+	createdFlag = B_FALSE;
+}
+
+/*
+ * Name		: z_resolve_lofs
+ * Description	: Loop over potential loopback mounts and symlinks in a
+ *		  given path and resolve them all down to an absolute path.
+ * Arguments	: char * - path to resolve.  path is in writable storage.
+ *		  size_t - length of path storage.
+ * Returns	: void
+ */
+void
+z_resolve_lofs(char *path, size_t pathlen)
+{
+	int len, arlen, i;
+	const char *altroot;
+	char tmppath[MAXPATHLEN];
+	boolean_t outside_altroot;
+
+	if ((len = resolvepath(path, tmppath, sizeof (tmppath))) == -1)
+		return;
+
+	tmppath[len] = '\0';
+	(void) strlcpy(path, tmppath, pathlen);
+
+	if (z_createMountTable() == -1)
+		return;
+
+	altroot = zonecfg_get_root();
+	arlen = strlen(altroot);
+	outside_altroot = B_FALSE;
+	for (;;) {
+		struct mnttab *mnp;
+
+		/* Search in reverse order to find longest match */
+		for (i = mountTableSize; i > 0; i--) {
+			mnp = &mountTable[i - 1];
+			if (mnp->mnt_fstype == NULL ||
+			    mnp->mnt_mountp == NULL ||
+			    mnp->mnt_special == NULL)
+				continue;
+			len = strlen(mnp->mnt_mountp);
+			if (strncmp(mnp->mnt_mountp, path, len) == 0 &&
+			    (path[len] == '/' || path[len] == '\0'))
+				break;
+		}
+		if (i <= 0)
+			break;
+
+		/* If it's not a lofs then we're done */
+		if (strcmp(mnp->mnt_fstype, MNTTYPE_LOFS) != 0)
+			break;
+
+		if (outside_altroot) {
+			char *cp;
+			int olen = sizeof (MNTOPT_RO) - 1;
+
+			/*
+			 * If we run into a read-only mount outside of the
+			 * alternate root environment, then the user doesn't
+			 * want this path to be made read-write.
+			 */
+			if (mnp->mnt_mntopts != NULL &&
+			    (cp = strstr(mnp->mnt_mntopts, MNTOPT_RO)) !=
+			    NULL &&
+			    (cp == mnp->mnt_mntopts || cp[-1] == ',') &&
+			    (cp[olen] == '\0' || cp[olen] == ',')) {
+				break;
+			}
+		} else if (arlen > 0 &&
+		    (strncmp(mnp->mnt_special, altroot, arlen) != 0 ||
+		    (mnp->mnt_special[arlen] != '\0' &&
+		    mnp->mnt_special[arlen] != '/'))) {
+			outside_altroot = B_TRUE;
+		}
+		/* use temporary buffer because new path might be longer */
+		(void) snprintf(tmppath, sizeof (tmppath), "%s%s",
+		    mnp->mnt_special, path + len);
+		if ((len = resolvepath(tmppath, path, pathlen)) == -1)
+			break;
+		path[len] = '\0';
+	}
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/usr/src/lib/libinstzones/common/zones_paths.c	Fri Jun 05 10:28:40 2009 -0400
@@ -0,0 +1,464 @@
+/*
+ * 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
+ * or http://www.opensolaris.org/os/licensing.
+ * 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 2009 Sun Microsystems, Inc.  All rights reserved.
+ * Use is subject to license terms.
+ */
+
+
+
+/*
+ * System includes
+ */
+
+#include <stdio.h>
+#include <limits.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <string.h>
+#include <fcntl.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <signal.h>
+#include <assert.h>
+#include <locale.h>
+#include <libintl.h>
+
+/*
+ * local includes
+ */
+
+#include "instzones_lib.h"
+#include "zones_strings.h"
+
+#define	isdot(x)	((x[0] == '.') && (!x[1] || (x[1] == '/')))
+#define	isdotdot(x)	((x[0] == '.') && (x[1] == '.') && \
+		    (!x[2] || (x[2] == '/')))
+
+/*
+ * forward declarations
+ */
+
+static char		**inheritedFileSystems = (char **)NULL;
+static size_t		*inheritedFileSystemsLen = (size_t *)NULL;
+static int		numInheritedFileSystems = 0;
+
+/*
+ * *****************************************************************************
+ * global external (public) functions
+ * *****************************************************************************
+ */
+
+/*
+ * Name:	z_get_inherited_file_systems
+ * Description:	Return list of file systems inherited from the global zone;
+ *		These file systems are entered into the list when the function
+ *		pkgAddInheritedFileSystem() is called.
+ * Arguments:	void
+ * Returns:	char **
+ *			- pointer to array of character pointers, each pointer
+ *			being a pointer to a string representing a file
+ *			system that is inherited from the global zone
+ *			the last entry will be (char *)NULL
+ *			- (char **)NULL - no file systems inherited
+ *
+ */
+
+char **
+z_get_inherited_file_systems(void)
+{
+	return (inheritedFileSystems);
+}
+
+/*
+ * Name:	z_add_inherited_file_system
+ * Description:	Add specified package to internal list of inherited file systems
+ * Arguments:	a_inheritedFileSystem - absolute path to file systen "inherited"
+ *
+ *		This function is called to register a directory (or
+ *		file system) as being inherited from the global zone
+ *		into the non-global zone being operated on.  The
+ *		inherited directory must be specified relative to the
+ *		root file system ("/").  For example, if "/usr" is
+ *		inherited, then the path specified would be "/usr".
+ *
+ *		Any path subsequently checked for being present in a
+ *		directory inherited read-only from the global zone:
+ *
+ *		-- will NOT have $PKG_INSTALL_ROOT prepended to it
+ *		-- if $PKG_INSTALL_ROOT is set and $BASEDIR is not set.
+ *		-- WILL have $BASEDIR prepended to it (if set).
+ *		-- $BASEDIR always has $PKG_INSTALL_ROOT included in it.
+ *		-- For example, if $PKG_INSTALL_ROOT is set to /a, and
+ *		-- the base install directory is set to "/opt", then the
+ *		-- $BASEDIR variable will be set to "/a/opt".
+ *
+ *		Any path that is checked for being present in an inherited
+ *		directory will be specified relative to the root file system
+ *		of the non-global zone in which the path is located.
+ *
+ *		When a path to update is checked for being present in
+ *		an inherited directory, $PKG_INSTALL_ROOT is stripped
+ *		off the path before it is checked.
+ *
+ *		If the non-global zone is not running, the scratch zone
+ *		is used to access the non-global zone.  In this case,
+ *		$PKG_INSTALL_ROOT will be set to "/a" and both the
+ *		non-global zone's root file system and all inherited
+ *		directories will be mounted on "/a". When a path is checked
+ *		for being inherited, it will have $PKG_INSTALL_ROOT stripped
+ *		from the beginning, so any inherited directories must be
+ *		specified relative to "/" and not $PKG_INSTALL_ROOT.
+ *
+ *		If the non-global zone is running, the non-global zone
+ *		is used directly. In this case, $PKG_INSTALL_ROOT will
+ *		be set to "/" and both the non-global zone's root file
+ *		system and all inherited directories will be mounted on
+ *		"/". $PKG_INSTALL_ROOT is set to "/" so the path is unchanged
+ *		before being checked against the list of inherited directories.
+ *
+ * Returns:	boolean_t
+ *			B_TRUE - file system successfully added to list
+ *			B_FALSE - failed to add file system to list
+ */
+
+boolean_t
+z_add_inherited_file_system(char *a_inheritedFileSystem)
+{
+#define	IPSLOP	2		/* for trailing '/' and '\0' */
+#define	IPMAX	((sizeof (rp))-IPSLOP)
+
+	char	rp[PATH_MAX+1+IPSLOP] = {'\0'};
+	int	n;
+
+	/* file system cannot be empty */
+
+	if (a_inheritedFileSystem == NULL || *a_inheritedFileSystem == '\0') {
+		_z_program_error(ERR_INHERITED_PATH_NULL);
+		return (B_FALSE);
+	}
+
+	/* file system must be absolute */
+
+	if (*a_inheritedFileSystem != '/') {
+		_z_program_error(ERR_INHERITED_PATH_NOT_ABSOLUTE,
+		    a_inheritedFileSystem);
+		return (B_FALSE);
+	}
+
+	/* make a local copy of the path and canonize it */
+
+	n = strlcpy(rp, a_inheritedFileSystem, IPMAX);
+	if (n > IPMAX) {
+		_z_program_error(ERR_INHERITED_PATH_TOO_LONG,
+		    strlen(a_inheritedFileSystem), IPMAX,
+		    a_inheritedFileSystem);
+		return (B_FALSE);
+	}
+
+	assert(n > 0);	/* path must have at least 1 byte in it */
+
+	z_path_canonize(rp);	/* remove duplicate "/"s, ./, etc */
+
+	/* add trailing "/" if it's not already there */
+	n = strlen(rp);
+	if (rp[n-1] != '/') {
+		rp[n++] = '/';
+	}
+
+	/* null terminate the string */
+
+	rp[n] = '\0';
+
+	/* add file system to internal list */
+
+	if (inheritedFileSystems == (char **)NULL) {
+		inheritedFileSystems = (char **)_z_calloc(
+		    2 * (sizeof (char **)));
+		inheritedFileSystemsLen =
+		    (size_t *)_z_calloc(2 * (sizeof (size_t *)));
+	} else {
+		inheritedFileSystems = (char **)_z_realloc(inheritedFileSystems,
+		    sizeof (char **)*(numInheritedFileSystems+2));
+		inheritedFileSystemsLen = (size_t *)_z_realloc(
+		    inheritedFileSystemsLen,
+		    sizeof (size_t *)*(numInheritedFileSystems+2));
+	}
+
+	/* add this entry to the end of the list */
+
+	inheritedFileSystemsLen[numInheritedFileSystems] = strlen(rp);
+	inheritedFileSystems[numInheritedFileSystems] = _z_strdup(rp);
+
+	numInheritedFileSystems++;
+
+	/* make sure end of the list is properly terminated */
+
+	inheritedFileSystemsLen[numInheritedFileSystems] = 0;
+	inheritedFileSystems[numInheritedFileSystems] = (char *)NULL;
+
+	/* exit debugging info */
+
+	_z_echoDebug(DBG_PATHS_ADD_FS, numInheritedFileSystems,
+	    inheritedFileSystems[numInheritedFileSystems-1]);
+
+	return (B_TRUE);
+}
+
+/*
+ * Name:	z_path_is_inherited
+ * Description:	Determine if the specified path is in a file system that is
+ *		in the internal list of inherited file systems
+ * Arguments:	a_path - pointer to string representing path to verify
+ *		a_ftype - file "type" if known otherwise '\0'
+ *			Type can be "f" (file), or "d" (directory)
+ *		a_rootDir - pointer to string representing root directory where
+ *			a_path is relative to - typically this would either be
+ *			"/" or the path specified as an alternative root to -R
+ * Returns:	boolean_t
+ *			B_TRUE - the path is in inherited file system space
+ *			B_FALSE - the path is NOT in inherited file system space
+ */
+
+boolean_t
+z_path_is_inherited(char *a_path, char a_ftype, char *a_rootDir)
+{
+	int		n;
+
+	/* entry assertions */
+
+	assert(a_path != (char *)NULL);
+	assert(*a_path != '\0');
+
+	/* if no inherited file systems, there can be no match */
+
+	if (numInheritedFileSystems == 0) {
+		_z_echoDebug(DBG_PATHS_NOT_INHERITED, a_path);
+		return (B_FALSE);
+	}
+
+	/* normalize root directory */
+
+	if ((a_rootDir == (char *)NULL) || (*a_rootDir == '\0')) {
+		a_rootDir = "/";
+	}
+
+	/*
+	 * if a_path resides on an inherited filesystem then
+	 * it must be read-only.
+	 */
+
+	if (z_isPathWritable(a_path) != 0) {
+		return (B_FALSE);
+	}
+
+	/*
+	 * remove the root path from the target path before comparing:
+	 * Example 1:
+	 * -- path is "/export/zone1/root/usr/test"
+	 * -- root path is "/export/zone1/root"
+	 * --- final path should be "/usr/test"
+	 * Example 2:
+	 * -- path is "/usr/test"
+	 * -- root path is "/"
+	 * --- final path should be "/usr/test"
+	 */
+
+	/* advance past given root directory if path begins with it */
+
+	n = strlen(a_rootDir);
+	if (strncmp(a_rootDir, a_path, n) == 0) {
+		char	*p;
+
+		/* advance past the root path */
+
+		p = a_path + n;
+
+		/* go back to the first occurance of the path separator */
+
+		while ((*p != '/') && (p > a_path)) {
+			p--;
+		}
+
+		/* use this location in the path to compare */
+
+		a_path = p;
+	}
+
+	/*
+	 * see if this path is in any inherited file system path
+	 * note that all paths in the inherited list are directories
+	 * so they end in "/" to prevent a partial match, such as
+	 * comparing "/usr/libx" with "/usr/lib" - by making the comparison
+	 * "/usr/libx" with "/usr/lib/" the partial false positive will not
+	 * occur. This complicates matters when the object to compare is a
+	 * directory - in this case, comparing "/usr" with "/usr/" will fail,
+	 * so if the object is a directory, compare one less byte from the
+	 * inherited file system so that the trailing "/" is ignored.
+	 */
+
+	for (n = 0; n < numInheritedFileSystems; n++) {
+		int	fslen;
+
+		/* get target fs len; adjust -1 if directory */
+
+		fslen = inheritedFileSystemsLen[n];
+		if ((a_ftype == 'd') && (fslen > 1)) {
+			fslen--;
+		}
+
+		if (strncmp(a_path, inheritedFileSystems[n], fslen) == 0) {
+			_z_echoDebug(DBG_PATHS_IS_INHERITED, a_path,
+			    inheritedFileSystems[n]);
+			return (B_TRUE);
+		}
+	}
+
+	/* path is not in inherited file system space */
+
+	_z_echoDebug(DBG_PATHS_IS_NOT_INHERITED, a_path, a_rootDir);
+
+	return (B_FALSE);
+}
+
+/*
+ * Name:	z_make_zone_root
+ * Description:	Given its zonepath, generate a string representing the
+ *              mountpoint of where the root path for a nonglobal zone is
+ *              mounted.  The zone is mounted using 'zoneadm', which mounts
+ *              the zone's filesystems wrt <zonepath>/lu/a
+ * Arguments:	zone_path - non-NULL pointer to string representing zonepath
+ * Returns:	char *	- pointer to string representing zonepath of zone
+ *		NULL	- if zone_path is NULL.
+ * Notes:	The string returned is in static storage and should not be
+ *              free()ed by the caller.
+ */
+char *
+z_make_zone_root(char *zone_path)
+{
+	static char	zone_root_buf[MAXPATHLEN];
+
+	if (zone_path == NULL)
+		return (NULL);
+
+	(void) snprintf(zone_root_buf, MAXPATHLEN, "%s%slu/a", zone_path,
+	    (zone_path[0] != '\0' &&
+	    zone_path[strlen(zone_path) - 1] == '/') ? "" : "/");
+
+	return (zone_root_buf);
+}
+
+void
+z_path_canonize(char *a_file)
+{
+	char	*pt;
+	char	*last;
+	int	level;
+
+	/* remove references such as "./" and "../" and "//" */
+	for (pt = a_file; *pt; /* void */) {
+		if (isdot(pt)) {
+			(void) strcpy(pt, pt[1] ? pt+2 : pt+1);
+		} else if (isdotdot(pt)) {
+			level = 0;
+			last = pt;
+			do {
+				level++;
+				last += 2;
+				if (*last) {
+					last++;
+				}
+			} while (isdotdot(last));
+			--pt; /* point to previous '/' */
+			while (level--) {
+				if (pt <= a_file) {
+					return;
+				}
+				while ((*--pt != '/') && (pt > a_file))
+					;
+			}
+			if (*pt == '/') {
+				pt++;
+			}
+			(void) strcpy(pt, last);
+		} else {
+			while (*pt && (*pt != '/')) {
+				pt++;
+			}
+			if (*pt == '/') {
+				while (pt[1] == '/') {
+					(void) strcpy(pt, pt+1);
+				}
+				pt++;
+			}
+		}
+	}
+
+	if ((--pt > a_file) && (*pt == '/')) {
+		*pt = '\0';
+	}
+}
+
+void
+z_canoninplace(char *src)
+{
+	char *dst;
+	char *src_start;
+
+	/* keep a ptr to the beginning of the src string */
+	src_start = src;
+
+	dst = src;
+	while (*src) {
+		if (*src == '/') {
+			*dst++ = '/';
+			while (*src == '/')
+				src++;
+		} else
+			*dst++ = *src++;
+	}
+
+	/*
+	 * remove any trailing slashes, unless the whole string is just "/".
+	 * If the whole string is "/" (i.e. if the last '/' cahr in dst
+	 * in the beginning of the original string), just terminate it
+	 * and return "/".
+	 */
+	if ((*(dst - 1) == '/') && ((dst - 1) != src_start))
+		dst--;
+	*dst = '\0';
+}
+
+void
+z_free_inherited_file_systems(void)
+{
+	int i;
+
+	for (i = 0; i < numInheritedFileSystems; i++) {
+		free(inheritedFileSystems[i]);
+	}
+	free(inheritedFileSystems);
+	inheritedFileSystems = NULL;
+	free(inheritedFileSystemsLen);
+	inheritedFileSystemsLen = NULL;
+	numInheritedFileSystems = 0;
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/usr/src/lib/libinstzones/common/zones_states.c	Fri Jun 05 10:28:40 2009 -0400
@@ -0,0 +1,585 @@
+/*
+ * 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
+ * or http://www.opensolaris.org/os/licensing.
+ * 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 2009 Sun Microsystems, Inc.  All rights reserved.
+ * Use is subject to license terms.
+ */
+
+
+/*
+ * Module:	zones_states.c
+ * Group:	libinstzones
+ * Description:	Provide "zones" state interfaces for install consolidation code
+ *
+ * Public Methods:
+ *
+ *  z_make_zone_running - change state of non-global zone to "running"
+ * _z_make_zone_ready - change state of non-global zone to "ready"
+ * _z_make_zone_down - change state of non-global zone to "down"
+ */
+
+/*
+ * System includes
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <fcntl.h>
+#include <ctype.h>
+#include <sys/types.h>
+#include <sys/param.h>
+#include <string.h>
+#include <strings.h>
+#include <sys/stat.h>
+#include <stdarg.h>
+#include <limits.h>
+#include <errno.h>
+#include <stropts.h>
+#include <libintl.h>
+#include <locale.h>
+#include <assert.h>
+
+/*
+ * local includes
+ */
+
+#include "instzones_lib.h"
+#include "zones_strings.h"
+
+/*
+ * Private structures
+ */
+
+/*
+ * Library Function Prototypes
+ */
+
+/*
+ * Local Function Prototypes
+ */
+
+/*
+ * global internal (private) declarations
+ */
+
+/*
+ * *****************************************************************************
+ * global external (public) functions
+ * *****************************************************************************
+ */
+
+/*
+ * Name:	_z_make_zone_running
+ * Description:	Given a zone element entry for the non-global zone to affect,
+ *		change the state of that non-global zone to "running"
+ * Arguments:	a_zlem - [RO, *RW] - (zoneListElement_t)
+ *			Zone list element describing the non-global zone to
+ *			make running
+ * Returns:	boolean_t
+ *			B_TRUE - non-global zone state changed successfully
+ *			B_FALSE - failed to make the non-global zone run
+ */
+
+boolean_t
+_z_make_zone_running(zoneListElement_t *a_zlem)
+{
+	FILE		*fp;
+	argArray_t	*args;
+	char		 zonename[ZONENAME_MAX];
+	char		*results = (char *)NULL;
+	int		ret;
+	int		status = 0;
+
+	/* entry assertions */
+
+	assert(a_zlem != NULL);
+
+	/* act based on the zone's current kernel state */
+
+	switch (a_zlem->_zlCurrKernelStatus) {
+	case ZONE_STATE_RUNNING:
+	case ZONE_STATE_MOUNTED:
+		/* already running */
+		return (B_TRUE);
+
+	case ZONE_STATE_READY:
+		/* This should never happen */
+		if (zonecfg_in_alt_root())
+			return (B_FALSE);
+
+		/*
+		 * We're going to upset the zone anyway, so might as well just
+		 * halt it now and fall through to normal mounting.
+		 */
+
+		_z_echoDebug(DBG_TO_ZONEHALT, a_zlem->_zlName);
+
+		args = _z_new_args(5);		/* generate new arg list */
+		(void) _z_add_arg(args, ZONEADM_CMD);
+		(void) _z_add_arg(args, "-z");
+		(void) _z_add_arg(args, a_zlem->_zlName);
+		(void) _z_add_arg(args, "halt");
+
+		ret = z_ExecCmdArray(&status, &results, (char *)NULL,
+		    ZONEADM_CMD, _z_get_argv(args));
+
+		/* free generated argument list */
+
+		_z_free_args(args);
+
+		if (ret != 0) {
+			_z_program_error(ERR_ZONEHALT_EXEC, ZONEADM_CMD,
+			    strerror(errno));
+			free(results);
+			return (B_FALSE);
+		}
+		if (status != 0) {
+			if (status == -1) {
+				_z_program_error(ERR_ZONEBOOT_CMD_SIGNAL,
+				    ZONEADM_CMD, a_zlem->_zlName);
+			} else {
+				_z_program_error(ERR_ZONEBOOT_CMD_ERROR,
+				    ZONEADM_CMD, a_zlem->_zlName, status,
+				    results == NULL ? "" : "\n",
+				    results == NULL ? "" : results);
+			}
+			free(results);
+			return (B_FALSE);
+		}
+
+		free(results);
+
+		a_zlem->_zlCurrKernelStatus = ZONE_STATE_INSTALLED;
+		/* FALLTHROUGH */
+
+	case ZONE_STATE_INSTALLED:
+	case ZONE_STATE_DOWN:
+		/* return false if the zone cannot be booted */
+
+		if (a_zlem->_zlStatus & ZST_NOT_BOOTABLE) {
+			return (B_FALSE);
+		}
+
+		_z_echoDebug(DBG_TO_ZONERUNNING, a_zlem->_zlName);
+
+		/* these states can be booted - do so */
+
+		args = _z_new_args(10);		/* generate new arg list */
+		(void) _z_add_arg(args, ZONEADM_CMD);
+		if (zonecfg_in_alt_root()) {
+			(void) _z_add_arg(args, "-R");
+			(void) _z_add_arg(args, "%s",
+			    (char *)zonecfg_get_root());
+		}
+
+		(void) _z_add_arg(args, "-z");
+		(void) _z_add_arg(args, "%s", a_zlem->_zlName);
+		(void) _z_add_arg(args, "mount");
+
+		ret = z_ExecCmdArray(&status, &results, (char *)NULL,
+		    ZONEADM_CMD, _z_get_argv(args));
+
+		/* free generated argument list */
+
+		_z_free_args(args);
+
+		if (ret != 0) {
+			_z_program_error(ERR_ZONEBOOT_EXEC, ZONEADM_CMD,
+			    strerror(errno));
+			free(results);
+			return (B_FALSE);
+		}
+
+		if (status != 0) {
+			if (status == -1) {
+				_z_program_error(ERR_ZONEBOOT_CMD_SIGNAL,
+				    ZONEADM_CMD, a_zlem->_zlName);
+			} else {
+				_z_program_error(ERR_ZONEBOOT_CMD_ERROR,
+				    ZONEADM_CMD, a_zlem->_zlName, status,
+				    results == NULL ? "" : "\n",
+				    results == NULL ? "" : results);
+			}
+			free(results);
+
+			/* remember this zone cannot be booted */
+
+			a_zlem->_zlStatus |= ZST_NOT_BOOTABLE;
+
+			return (B_FALSE);
+		}
+		free(results);
+
+		if (zonecfg_in_alt_root()) {
+			if ((fp = zonecfg_open_scratch("", B_FALSE)) == NULL ||
+			    zonecfg_find_scratch(fp, a_zlem->_zlName,
+			    zonecfg_get_root(), zonename,
+			    sizeof (zonename)) == -1) {
+				_z_program_error(ERR_ZONEBOOT_DIDNT_BOOT,
+				    a_zlem->_zlName);
+				if (fp != NULL)
+					zonecfg_close_scratch(fp);
+				return (B_FALSE);
+			}
+			zonecfg_close_scratch(fp);
+			free(a_zlem->_zlScratchName);
+			a_zlem->_zlScratchName = _z_strdup(zonename);
+		}
+		a_zlem->_zlCurrKernelStatus = ZONE_STATE_MOUNTED;
+		return (B_TRUE);
+
+	case ZONE_STATE_CONFIGURED:
+	case ZONE_STATE_INCOMPLETE:
+	case ZONE_STATE_SHUTTING_DOWN:
+	default:
+		/* cannot transition (boot) these states */
+		return (B_FALSE);
+	}
+}
+
+/*
+ * Name:	_z_make_zone_ready
+ * Description:	Given a zone element entry for the non-global zone to affect,
+ *		restore the ready state of the zone when the zone is currently
+ *		in the running state.
+ * Arguments:	a_zlem - [RO, *RW] - (zoneListElement_t)
+ *			Zone list element describing the non-global zone to
+ *			make ready
+ * Returns:	boolean_t
+ *			B_TRUE - non-global zone state changed successfully
+ *			B_FALSE - failed to make the non-global zone ready
+ */
+
+boolean_t
+_z_make_zone_ready(zoneListElement_t *a_zlem)
+{
+	argArray_t	*args;
+	char		*results = (char *)NULL;
+	int		status = 0;
+	int		i;
+	int		ret;
+	zone_state_t	st;
+
+	/* entry assertions */
+
+	assert(a_zlem != (zoneListElement_t *)NULL);
+
+	/* act based on the zone's current kernel state */
+
+	switch (a_zlem->_zlCurrKernelStatus) {
+	case ZONE_STATE_DOWN:
+	case ZONE_STATE_READY:
+		/* already down */
+		return (B_TRUE);
+
+	case ZONE_STATE_MOUNTED:
+		_z_echoDebug(DBG_TO_ZONEUNMOUNT, a_zlem->_zlName);
+
+		args = _z_new_args(10);		/* generate new arg list */
+		(void) _z_add_arg(args, ZONEADM_CMD);
+		(void) _z_add_arg(args, "-z");
+		(void) _z_add_arg(args, "%s", a_zlem->_zlName);
+		(void) _z_add_arg(args, "unmount");
+		ret = z_ExecCmdArray(&status, &results, NULL,
+		    ZONEADM_CMD, _z_get_argv(args));
+		if (ret != 0) {
+			_z_program_error(ERR_ZONEUNMOUNT_EXEC,
+			    ZONEADM_CMD, strerror(errno));
+			free(results);
+			_z_free_args(args);
+			return (B_FALSE);
+		}
+		if (status != 0) {
+			if (status == -1) {
+				_z_program_error(ERR_ZONEUNMOUNT_CMD_SIGNAL,
+				    ZONEADM_CMD, a_zlem->_zlName);
+			} else {
+				_z_program_error(ERR_ZONEUNMOUNT_CMD_ERROR,
+				    ZONEADM_CMD, a_zlem->_zlName, status,
+				    results == NULL ? "" : "\n",
+				    results == NULL ? "" : results);
+			}
+			if (results != NULL) {
+				free(results);
+			}
+			_z_free_args(args);
+			return (B_FALSE);
+		}
+		if (results != NULL) {
+			free(results);
+		}
+		_z_free_args(args);
+		a_zlem->_zlCurrKernelStatus = ZONE_STATE_INSTALLED;
+		_z_echoDebug(DBG_TO_ZONEREADY, a_zlem->_zlName);
+
+		args = _z_new_args(10);		/* generate new arg list */
+		(void) _z_add_arg(args, ZONEADM_CMD);
+		(void) _z_add_arg(args, "-z");
+		(void) _z_add_arg(args, "%s", a_zlem->_zlName);
+		(void) _z_add_arg(args, "ready");
+
+		ret = z_ExecCmdArray(&status, &results, NULL,
+		    ZONEADM_CMD, _z_get_argv(args));
+		if (ret != 0) {
+			_z_program_error(ERR_ZONEREADY_EXEC, ZONEADM_CMD,
+			    strerror(errno));
+			free(results);
+			_z_free_args(args);
+			return (B_FALSE);
+		}
+		if (status != 0) {
+			_z_program_error(ERR_ZONEREADY_CMDFAIL, ZONEADM_CMD,
+			    a_zlem->_zlName, strerror(errno),
+			    results == NULL ? "" : "\n",
+			    results == NULL ? "" : results);
+			if (results != NULL) {
+				free(results);
+			}
+			_z_free_args(args);
+			return (B_FALSE);
+		}
+		if (results != NULL) {
+			free(results);
+		}
+		/* success - zone is now in the ready state */
+		a_zlem->_zlCurrKernelStatus = ZONE_STATE_READY;
+		return (B_TRUE);
+
+	case ZONE_STATE_RUNNING:
+
+		_z_echoDebug(DBG_TO_ZONEREADY, a_zlem->_zlName);
+
+		args = _z_new_args(10);		/* generate new arg list */
+		(void) _z_add_arg(args, ZONEADM_CMD);
+		(void) _z_add_arg(args, "-z");
+		(void) _z_add_arg(args, "%s", a_zlem->_zlName);
+		(void) _z_add_arg(args, "ready");
+
+		ret = z_ExecCmdArray(&status, &results, (char *)NULL,
+		    ZONEADM_CMD, _z_get_argv(args));
+
+		/* free generated argument list */
+
+		_z_free_args(args);
+
+		if (ret != 0) {
+			_z_program_error(ERR_ZONEREADY_EXEC, ZONEADM_CMD,
+			    strerror(errno));
+			free(results);
+			_z_free_args(args);
+			return (B_FALSE);
+		}
+		if (status != 0) {
+			_z_program_error(ERR_ZONEREADY_CMDFAIL, ZONEADM_CMD,
+			    a_zlem->_zlName, strerror(errno),
+			    results == (char *)NULL ? "" : "\n",
+			    results == (char *)NULL ? "" : results);
+			if (results != (char *)NULL) {
+				(void) free(results);
+			}
+			return (B_FALSE);
+		}
+
+		if (results != (char *)NULL) {
+			(void) free(results);
+		}
+
+		for (i = 0; i < MAX_RETRIES; i++) {
+			if (zone_get_state(a_zlem->_zlName, &st) != Z_OK) {
+				break;
+			}
+			if ((st == ZONE_STATE_DOWN) ||
+			    (st == ZONE_STATE_INSTALLED)||
+			    (st == ZONE_STATE_READY)) {
+				break;
+			}
+			(void) sleep(RETRY_DELAY_SECS);
+		}
+
+		/* failure if maximum retries reached */
+
+		if (i >= MAX_RETRIES) {
+			_z_program_error(ERR_ZONEREADY_DIDNT_READY,
+			    a_zlem->_zlName);
+			a_zlem->_zlCurrKernelStatus = st;
+			return (B_FALSE);
+		}
+
+		/* success - zone is now in the ready state  */
+
+		a_zlem->_zlCurrKernelStatus = ZONE_STATE_READY;
+
+		return (B_TRUE);
+
+	case ZONE_STATE_INSTALLED:
+	case ZONE_STATE_CONFIGURED:
+	case ZONE_STATE_INCOMPLETE:
+	case ZONE_STATE_SHUTTING_DOWN:
+	default:
+		return (B_FALSE);
+	}
+}
+
+/*
+ * Name:	_z_make_zone_down
+ * Description:	Given a zone element entry for the non-global zone to affect,
+ *		change the state of that non-global zone to "down"
+ * Arguments:	a_zlem - [RO, *RW] - (zoneListElement_t)
+ *			Zone list element describing the non-global zone to
+ *			make down
+ * Returns:	boolean_t
+ *			B_TRUE - non-global zone state changed successfully
+ *			B_FALSE - failed to make the non-global zone down
+ */
+
+boolean_t
+_z_make_zone_down(zoneListElement_t *a_zlem)
+{
+	argArray_t	*args;
+	char		*results = (char *)NULL;
+	int		status = 0;
+	int		ret;
+
+	/* entry assertions */
+
+	assert(a_zlem != NULL);
+
+	/* act based on the zone's current kernel state */
+
+	switch (a_zlem->_zlCurrKernelStatus) {
+	case ZONE_STATE_DOWN:
+	case ZONE_STATE_READY:
+	case ZONE_STATE_RUNNING:
+		/* shouldn't be touched */
+		return (B_TRUE);
+
+	case ZONE_STATE_MOUNTED:
+
+		_z_echoDebug(DBG_TO_ZONEHALT, a_zlem->_zlName);
+
+		/* these states can be halted - do so */
+
+		args = _z_new_args(10);		/* generate new arg list */
+		(void) _z_add_arg(args, ZONEADM_CMD);
+
+		if (zonecfg_in_alt_root()) {
+			(void) _z_add_arg(args, "-R");
+			(void) _z_add_arg(args, "%s",
+			    (char *)zonecfg_get_root());
+		}
+
+		(void) _z_add_arg(args, "-z");
+		(void) _z_add_arg(args, "%s", a_zlem->_zlName);
+		(void) _z_add_arg(args, "unmount");
+
+		ret = z_ExecCmdArray(&status, &results, (char *)NULL,
+		    ZONEADM_CMD, _z_get_argv(args));
+
+		/* free generated argument list */
+
+		_z_free_args(args);
+
+		if (ret != 0) {
+			_z_program_error(ERR_ZONEHALT_EXEC, ZONEADM_CMD,
+			    strerror(errno));
+			free(results);
+			return (B_FALSE);
+		}
+		if (status != 0) {
+			if (status == -1) {
+				_z_program_error(ERR_ZONEBOOT_CMD_SIGNAL,
+				    ZONEADM_CMD, a_zlem->_zlName);
+			} else {
+				_z_program_error(ERR_ZONEBOOT_CMD_ERROR,
+				    ZONEADM_CMD, a_zlem->_zlName, status,
+				    results == NULL ? "" : "\n",
+				    results == NULL ? "" : results);
+			}
+			free(results);
+			return (B_FALSE);
+		}
+
+		free(results);
+
+		a_zlem->_zlCurrKernelStatus = ZONE_STATE_INSTALLED;
+		/*
+		 * Leave the scratch name in place because the upper level
+		 * software may have used it to construct file names and the
+		 * like.
+		 */
+		return (B_TRUE);
+
+	case ZONE_STATE_INSTALLED:
+	case ZONE_STATE_CONFIGURED:
+	case ZONE_STATE_INCOMPLETE:
+	case ZONE_STATE_SHUTTING_DOWN:
+	default:
+		return (B_FALSE);
+	}
+}
+
+/*
+ * Function:    UmountAllZones
+ * Description: Unmount all mounted zones under a specified directory.
+ *
+ * Scope:   public
+ * Parameters:  mntpnt  [RO, *RO]
+ *          Non-NULL pointer to name of directory to be unmounted.
+ * Return:   0  - successfull
+ *      -1  - unmount failed; see errno for reason
+ */
+int
+UmountAllZones(char *mntpnt) {
+
+	zoneList_t  zlst;
+	int	 k;
+	int  ret = 0;
+
+	if (z_zones_are_implemented()) {
+
+		z_set_zone_root(mntpnt);
+
+		zlst = z_get_nonglobal_zone_list();
+		if (zlst == (zoneList_t)NULL) {
+			return (0);
+		}
+
+		for (k = 0; z_zlist_get_zonename(zlst, k) != (char *)NULL;
+		    k++) {
+			if (z_zlist_get_current_state(zlst, k) >
+			    ZONE_STATE_INSTALLED) {
+				if (!z_zlist_change_zone_state(zlst, k,
+				    ZONE_STATE_INSTALLED)) {
+					ret = -1;
+					break;
+				}
+			}
+		}
+
+		/* Free zlst */
+		z_free_zone_list(zlst);
+	}
+
+	return (ret);
+
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/usr/src/lib/libinstzones/common/zones_str.c	Fri Jun 05 10:28:40 2009 -0400
@@ -0,0 +1,711 @@
+/*
+ * 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
+ * or http://www.opensolaris.org/os/licensing.
+ * 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 2009 Sun Microsystems, Inc.  All rights reserved.
+ * Use is subject to license terms.
+ */
+
+
+
+/*
+ * Module:	zones_str.c
+ * Group:	libinstzones
+ * Description:	Private functions used by zones library functions to manipulate
+ *		strings
+ *
+ * Public Methods:
+ *
+ * _z_strAddToken - Add a token to a string
+ * _z_strContainsToken - Does a given string contain a specified substring
+ * _z_strGetToken - Get a separator delimited token from a string
+ * _z_strGetToken_r - Get separator delimited token from string to fixed buffer
+ * _z_strPrintf - Create string from printf style format and arguments
+ * _z_strPrintf_r - Create string from printf style format and arguments
+ * _z_strRemoveLeadingWhitespace - Remove leading whitespace from string
+ * _z_strRemoveToken - Remove a token from a string
+ */
+
+/*
+ * System includes
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <ctype.h>
+#include <sys/types.h>
+#include <sys/param.h>
+#include <string.h>
+#include <strings.h>
+#include <stdarg.h>
+#include <limits.h>
+#include <stropts.h>
+#include <libintl.h>
+#include <locale.h>
+#include <assert.h>
+
+/*
+ * local includes
+ */
+
+#include "instzones_lib.h"
+#include "zones_strings.h"
+
+/*
+ * Private structures
+ */
+
+/*
+ * Library Function Prototypes
+ */
+
+/*
+ * Local Function Prototypes
+ */
+
+/*
+ * Global internal (private) declarations
+ */
+
+/*
+ * *****************************************************************************
+ * global external (public) functions
+ * *****************************************************************************
+ */
+
+/*
+ * Name:	_z_strAddToken
+ * Synopsis:	Add a token to a string
+ * Description:	Append a token (sequence of one or more characters) to a
+ *		string that is in allocated space - create new string if
+ *		no string to append to exists
+ * Arguments:	a_old - [RO, *RW] - (char **)
+ *			- Pointer to handle to string to append token to
+ *			  == NULL - new string is created
+ *		a_new - [RO, *RO] - (char *)
+ *			- Pointer to string representing token to append
+ *			  to the end of the "a_old" string
+ *			  == NULL - no action is performed
+ *			  a_new[0] == '\0' - no action is performed
+ *		a_separator - [RO, *RO] - (char)
+ *			- One character placed between the old (existing)
+ *			  string and the new token to be added IF the old
+ *			  string exists and is not empty (zero length)
+ * Returns:	void
+ * CAUTION:	The old (existing) string must be allocated space (via lu_mem*
+ *		or _z_str* methods) - it must not be a static or inline
+ *		character string
+ * NOTE:	The old (existing) string may be freed with 'free'
+ *		if a token is appended to it
+ * NOTE:    	Any string returned in 'a_old' is placed in new storage for the
+ *		calling method. The caller must use 'free' to dispose
+ *		of the storage once the token string is no longer needed.
+ */
+
+void
+_z_strAddToken(char **a_old, char *a_new, char a_separator)
+{
+	/* entry assertions */
+
+	assert(a_old != NULL);
+	assert(a_separator != '\0');
+
+	/* if token to add is null or token is zero length, just return */
+
+	if (a_new == NULL || *a_new == '\0') {
+		return;
+	}
+
+	/* make sure that new token does not contain the separator */
+
+	assert(strchr(a_new, (int)a_separator) == NULL);
+
+	/* if old string is empty (zero length), deallocate */
+
+	if ((*a_old != NULL) && ((*a_old)[0] == '\0')) {
+		/* *a_old is set to NULL by free */
+		free(*a_old);
+		*a_old = NULL;
+	}
+
+	/* if old string exists, append separator and token */
+
+	if (*a_old != NULL) {
+		char *p;
+		p = _z_strPrintf("%s%c%s", *a_old, a_separator, a_new);
+		free(*a_old);
+		*a_old = p;
+		return;
+	}
+
+	/* old string does not exist - return duplicate of token */
+
+	assert(*a_old == NULL);
+	*a_old = _z_strdup(a_new);
+}
+
+/*
+ * Name:	_z_strContainsToken
+ * Synopsis:	Does a given string contain a specified substring
+ * Description:	Determine if a given substring exists in a larger string
+ * Arguments:	a_string - [RO, *RO] - (char *)
+ *			Pointer to string to look for substring in
+ *		a_token - [RO, *RO] - (char *)
+ *			Pointer to substring to look for in larger string
+ * Results:	boolean_t
+ *			B_TRUE - substring exists in larger string
+ *			B_FALSE - substring does NOT exist in larger string
+ * NOTE:	The substring must match on a "token" basis; that is, the
+ *		substring must exist in the larger string delineated with
+ *		either spaces or tabs to match.
+ */
+
+boolean_t
+_z_strContainsToken(char *a_string, char *a_token, char *a_separators)
+{
+	char	*lasts;
+	char	*current;
+	char	*p;
+
+	/* entry assertions */
+
+	assert(a_separators != NULL);
+	assert(*a_separators != '\0');
+
+	/*
+	 * if token is not supplied, no string provided,
+	 * or the string is an empty string, return false
+	 */
+
+	if (a_token == NULL || a_string == NULL || *a_string == '\0') {
+		return (B_FALSE);
+	}
+
+	/* if no string provided, return false */
+
+	/* if string empty (zero length), return false */
+
+	/* duplicate larger string because strtok_r changes it */
+
+	p = _z_strdup(a_string);
+
+	lasts = p;
+
+	/* scan each token looking for a match */
+
+	while ((current = strtok_r(NULL, a_separators, &lasts)) !=
+	    NULL) {
+		if (strcmp(current, a_token) == 0) {
+			free(p);
+			return (B_TRUE);
+		}
+	}
+
+	/* free up temporary storage */
+
+	free(p);
+
+	/* not found */
+
+	return (B_FALSE);
+}
+
+/*
+ * Name:	_z_strGetToken
+ * Synopsis:	Get a separator delimited token from a string
+ * Description:	Given a string and a list of one or more separators,
+ *		return the position specified token (sequence of one or
+ *		more characters that do not include any of the separators)
+ * Arguments:	r_sep - [*RW] - (char *)
+ *			- separator that ended the token returned
+ *			- NOTE: this is a pointer to a "char", e.g.:
+ *				- char a;
+ *				- _z_strGetToken(&a, ...)
+ *		a_string - [RO, *RO] - (char *)
+ *			- pointer to string to extract token from
+ *		a_index - [RO, *RO] - (int)
+ *			- Index of token to return; '0' is first matching
+ *			  token, '1' is second matching token, etc.
+ *		a_separators - [RO, *RO] - (char *)
+ *			- String containing one or more characters that
+ *			  can separate one "token" from another
+ * Returns:	char *
+ *			== NULL - no token matching criteria found
+ *			!= NULL - token matching criteria
+ * NOTE:    	Any token string returned is placed in new storage for the
+ *		calling method. The caller must use 'free' to dispose
+ *		of the storage once the token string is no longer needed.
+ */
+
+char *
+_z_strGetToken(char *r_sep, char *a_string, int a_index, char *a_separators)
+{
+	char	*p;
+	char	*q;
+	char	*lasts;
+
+	/* entry assertions */
+
+	assert(a_string != NULL);
+	assert(a_index >= 0);
+	assert(a_separators != NULL);
+	assert(*a_separators != '\0');
+
+	/* if returned separator requested, reset to null until token found */
+
+	if (r_sep != NULL) {
+		*r_sep = '\0';
+	}
+
+	/* duplicate original string before breaking down into tokens */
+
+	p = _z_strdup(a_string);
+
+	lasts = p;
+
+	/* scan for separators and return 'index'th token found */
+
+	while (q = strtok_r(NULL, a_separators, &lasts)) {
+		/* retrieve separator if requested */
+
+		if (r_sep != NULL) {
+			char	*x;
+
+			x = strpbrk(a_string, a_separators);
+			if (x != NULL) {
+				*r_sep = *x;
+			}
+		}
+
+		/* if this is the 'index'th token requested return it */
+
+		if (a_index-- == 0) {
+			char	*tmp;
+
+			/* duplicate token into its own storage */
+
+			tmp = _z_strdup(q);
+
+			/* free up copy of original input string */
+
+			free(p);
+
+			/* return token found */
+
+			return (tmp);
+		}
+	}
+
+	/*
+	 * token not found
+	 */
+
+	/* free up copy of original input string */
+
+	free(p);
+
+	/* return NULL pointer (token not found) */
+
+	return (NULL);
+}
+
+/*
+ * Name:	_z_strGetToken_r
+ * Synopsis:	Get separator delimited token from a string into a fixed buffer
+ * Description:	Given a string and a list of one or more separators,
+ *		return the position specified token (sequence of one or
+ *		more characters that do not include any of the separators)
+ *		into a specified buffer of a fixed maximum size
+ * Arguments:	r_sep - [*RW] - (char *)
+ *			- separator that ended the token returned
+ *			- NOTE: this is a pointer to a "char", e.g.:
+ *				- char a;
+ *				- _z_strGetToken(&a, ...)
+ *		a_string - [RO, *RO] - (char *)
+ *			- pointer to string to extract token from
+ *		a_index - [RO, *RO] - (int)
+ *			- Index of token to return; '0' is first matching
+ *			  token, '1' is second matching token, etc.
+ *		a_separators - [RO, *RO] - (char *)
+ *			- String containing one or more characters that
+ *			  can separate one "token" from another
+ *		a_buf - [RO, *RW] - (char *)
+ *			- Pointer to buffer used as storage space for the
+ *			  returned token - the returned token is always
+ *			  null terminated
+ *			  a_buf[0] == '\0' - no token meeting criteria found
+ *			  a_buf[0] != '\0' - token meeting criteria returned
+ *		a_bufLen - [RO, *RO] - (int)
+ *			- Size of 'a_buf' in bytes - a maximum of 'a_bufLen-1'
+ *			  bytes will be placed in 'a_buf' - the returned
+ *			  token is always null terminated
+ * Returns:	void
+ */
+
+void
+_z_strGetToken_r(char *r_sep, char *a_string, int a_index,
+    char *a_separators, char *a_buf, int a_bufLen)
+{
+	char	*p;
+	char	*q;
+	char	*lasts;
+
+	/* entry assertions */
+
+	assert(a_string != NULL);
+	assert(a_index >= 0);
+	assert(a_separators != NULL);
+	assert(*a_separators != '\0');
+	assert(a_buf != NULL);
+	assert(a_bufLen > 0);
+
+	/* reset returned separator */
+
+	if (r_sep != NULL) {
+		*r_sep = '\0';
+	}
+
+	/* zero out contents of return buffer */
+
+	bzero(a_buf, a_bufLen);
+
+	/* duplicate original string before breaking down into tokens */
+
+	p = _z_strdup(a_string);
+
+	lasts = p;
+
+	/* scan for separators and return 'index'th token found */
+
+	while (q = strtok_r(NULL, a_separators, &lasts)) {
+		/* retrieve separator if requested */
+
+		if (r_sep != NULL) {
+			char	*x;
+			x = strpbrk(a_string, a_separators);
+			if (x != NULL) {
+				*r_sep = *x;
+			}
+		}
+
+		/* if this is the 'index'th token requested return it */
+
+		if (a_index-- == 0) {
+			/* copy as many characters as possible to return buf */
+
+			(void) strncpy(a_buf, q, a_bufLen-1);
+			break;
+		}
+	}
+
+	/* free up copy of original input string */
+
+	free(p);
+}
+
+/*
+ * Name:	_z_strPrintf
+ * Synopsis:	Create string from printf style format and arguments
+ * Description:	Call to convert a printf style format and arguments into a
+ *		string of characters placed in allocated storage
+ * Arguments:	format - [RO, RO*] (char *)
+ *			printf-style format for string to be formatted
+ *		VARG_LIST - [RO] (?)
+ *			arguments as appropriate to 'format' specified
+ * Returns:	char *
+ *			A string representing the printf conversion results
+ * NOTE:    	Any string returned is placed in new storage for the
+ *		calling method. The caller must use 'free' to dispose
+ *		of the storage once the string is no longer needed.
+ * Errors:	If the string cannot be created, the process exits
+ */
+
+/*PRINTFLIKE1*/
+char *
+_z_strPrintf(char *a_format, ...)
+{
+	va_list		ap;
+	size_t		vres = 0;
+	char		bfr[1];
+	char		*rstr = NULL;
+
+	/* entry assertions */
+
+	assert(a_format != NULL);
+	assert(*a_format != '\0');
+
+	/* determine size of the message in bytes */
+
+	va_start(ap, a_format);
+	vres = vsnprintf(bfr, 1, a_format, ap);
+	va_end(ap);
+
+	assert(vres > 0);
+	assert(vres < LINE_MAX);
+
+	/* allocate storage to hold the message */
+
+	rstr = (char *)_z_calloc(vres+2);
+
+	/* generate the results of the printf conversion */
+
+	va_start(ap, a_format);
+	vres = vsnprintf(rstr, vres+1, a_format, ap);
+	va_end(ap);
+
+	assert(vres > 0);
+	assert(vres < LINE_MAX);
+	assert(*rstr != '\0');
+
+	/* return the results */
+
+	return (rstr);
+}
+
+/*
+ * Name:	_z_strPrintf_r
+ * Synopsis:	Create string from printf style format and arguments
+ * Description:	Call to convert a printf style format and arguments into a
+ *		string of characters placed in allocated storage
+ * Arguments:	a_buf - [RO, *RW] - (char *)
+ *			- Pointer to buffer used as storage space for the
+ *			  returned string created
+ *		a_bufLen - [RO, *RO] - (int)
+ *			- Size of 'a_buf' in bytes - a maximum of 'a_bufLen-1'
+ *			  bytes will be placed in 'a_buf' - the returned
+ *			  string is always null terminated
+ *		a_format - [RO, RO*] (char *)
+ *			printf-style format for string to be formatted
+ *		VARG_LIST - [RO] (?)
+ *			arguments as appropriate to 'format' specified
+ * Returns:	void
+ */
+
+/*PRINTFLIKE3*/
+void
+_z_strPrintf_r(char *a_buf, int a_bufLen, char *a_format, ...)
+{
+	va_list		ap;
+	size_t		vres = 0;
+
+	/* entry assertions */
+
+	assert(a_format != NULL);
+	assert(*a_format != '\0');
+	assert(a_buf != NULL);
+	assert(a_bufLen > 1);
+
+	/* generate the results of the printf conversion */
+
+	va_start(ap, a_format);
+	vres = vsnprintf(a_buf, a_bufLen-1, a_format, ap);
+	va_end(ap);
+
+	assert(vres > 0);
+	assert(vres < a_bufLen);
+
+	a_buf[a_bufLen-1] = '\0';
+}
+
+/*
+ * Name:	_z_strRemoveLeadingWhitespace
+ * Synopsis:	Remove leading whitespace from string
+ * Description:	Remove all leading whitespace characters from a string
+ * Arguments:	a_str - [RO, *RW] - (char **)
+ *			Pointer to handle to string (in allocated storage) to
+ *			remove all leading whitespace from
+ * Returns:	void
+ *			The input string is modified as follows:
+ *			== NULL:
+ *				- input string was NULL
+ *				- input string is all whitespace
+ *			!= NULL:
+ *				- copy of input string with leading
+ *				  whitespace removed
+ * CAUTION:	The input string must be allocated space (via mem* or
+ *		_z_str* methods) - it must not be a static or inline
+ *		character string
+ * NOTE:	The input string a_str will be freed with 'free'
+ *		if it is all whitespace, or if it contains any leading
+ *		whitespace characters
+ * NOTE:    	Any string returned is placed in new storage for the
+ *		calling method. The caller must use 'free' to dispose
+ *		of the storage once the string is no longer needed.
+ * Errors:	If the string cannot be created, the process exits
+ */
+
+void
+_z_strRemoveLeadingWhitespace(char **a_str)
+{
+	char	*o_str;
+
+	/* entry assertions */
+
+	assert(a_str != NULL);
+
+	/* if string is null, just return */
+
+	if (*a_str == NULL) {
+		return;
+	}
+	o_str = *a_str;
+
+	/* if string is empty, deallocate and return NULL */
+
+	if (*o_str == '\0') {
+		/* free string - handle is not reset to NULL by free */
+		free(*a_str);
+		*a_str = NULL;
+		return;
+	}
+
+	/* if first character is not a space, just return */
+
+	if (!isspace(*o_str)) {
+		return;
+	}
+
+	/* advance past all space characters */
+
+	while ((*o_str != '\0') && (isspace(*o_str))) {
+		o_str++;
+	}
+
+	/* if string was all space characters, deallocate and return NULL */
+
+	if (*o_str == '\0') {
+		/* free string - *a_str is not reset to NULL by free */
+		free(*a_str);
+		*a_str = NULL;
+		return;
+	}
+
+	/* have non-space/null byte, return dup, deallocate original */
+
+	free(*a_str);
+	*a_str = _z_strdup(o_str);
+}
+
+/*
+ * Name:	_z_strRemoveToken
+ * Synopsis:	Remove a token from a string
+ * Description:	Remove a token (sequence of one or more characters) from a
+ *		string that is in allocated space
+ * Arguments:	r_string - [RO, *RW] - (char **)
+ *			- Pointer to handle to string to remove token from
+ *		a_token - [RO, *RO] - (char *)
+ *			Pointer to token (substring) to look for and remove
+ *			from r_string provided
+ *		a_separators - [RO, *RO] - (char *)
+ *			- String containing one or more characters that
+ *			  separate one "token" from another in r_string
+ *		a_index - [RO, *RO] - (int)
+ *			- Index of token to remove; '0' is first matching
+ *			  token, '1' is second matching token, etc.
+ * Returns:	void
+ * CAUTION:	The input string must be allocated space (via lu_mem* or
+ *		_z_str* methods) - it must not be a static or inline
+ *		character string
+ * NOTE:	The input string r_string will be freed with 'free'
+ *		if the token to be removed is found
+ * NOTE:    	Any token string returned is placed in new storage for the
+ *		calling method. The caller must use 'free' to dispose
+ *		of the storage once the token string is no longer needed.
+ * Errors:	If the new token string cannot be created, the process exits
+ */
+
+void
+_z_strRemoveToken(char **r_string, char *a_token, char *a_separators,
+	int a_index)
+{
+	char	*a_string;
+	char	*copyString;
+	char	sep = 0;
+	int	copyLength;
+	int	i;
+
+	/* entry assertions */
+
+	assert(r_string != NULL);
+	assert(a_token != NULL);
+	assert(*a_token != '\0');
+	assert(a_separators != NULL);
+	assert(*a_separators != '\0');
+
+	/* simple case: input string is null; return empty string */
+
+	a_string = *r_string;
+	if (*a_string == '\0') {
+		return;
+	}
+
+	/* simple case: token == input string; return empty string */
+
+	if (strcmp(a_string, a_token) == 0) {
+		/*
+		 * deallocate input string; free doesn't
+		 * set *r_string to NULL
+		 */
+		free(*r_string);
+		*r_string = NULL;
+		return;
+	}
+
+	/* simple case: token not in input string: return */
+
+	if (!_z_strContainsToken(a_string, a_token, a_separators)) {
+		return;
+	}
+
+	/*
+	 * Pick apart the old string building the new one as we go along
+	 * removing the first occurance of the token provided
+	 */
+
+	copyLength = (strlen(a_string)-strlen(a_token))+2;
+	copyString = (char *)_z_calloc(copyLength);
+
+	for (i = 0; ; i++) {
+		char	*p;
+
+		p = _z_strGetToken(&sep, a_string, i, a_separators);
+		if (p == NULL) {
+			break;
+		}
+
+		if ((strcmp(p, a_token) == 0) && (a_index-- == 0)) {
+			free(p);
+			continue;
+		}
+
+		if (*copyString) {
+			assert(sep != '\0');
+			(void) strncat(copyString, &sep, 1);
+		}
+
+		(void) strcat(copyString, p);
+		free(p);
+	}
+
+	free(*r_string);
+	assert(*copyString);
+	*r_string = copyString;
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/usr/src/lib/libinstzones/common/zones_strings.h	Fri Jun 05 10:28:40 2009 -0400
@@ -0,0 +1,238 @@
+/*
+ * 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
+ * or http://www.opensolaris.org/os/licensing.
+ * 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 2007 Sun Microsystems, Inc.  All rights reserved.
+ * Use is subject to license terms.
+ */
+
+
+
+#ifndef _ZONES_STRINGS_H
+#define	_ZONES_STRINGS_H
+
+
+/*
+ * Module:	zones_strings.h
+ * Group:	libinstzones
+ * Description:	This header contains strings used in libinstzones
+ *		library modules.
+ */
+
+#include <libintl.h>
+
+/*
+ * C++ prefix
+ */
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/* constants */
+
+#ifndef	TEXT_DOMAIN
+#define	TEXT_DOMAIN	"SUNW_INSTALL_LIBZONES"
+#endif
+
+#ifndef ILIBSTR
+#define	ILIBSTR(x)	dgettext(TEXT_DOMAIN, x)
+#endif
+
+/*
+ * message strings
+ */
+
+/* BEGIN CSTYLED */
+
+/*
+ * I18N: these messages are debugging message and are only displayed
+ * when special debugging output has been enabled - these messages
+ * will never be displayed during normal product usage
+ */
+
+#define	DBG_ARG				ILIBSTR("argument <%d> = <%s>")
+#define	DBG_LIBRARY_NOT_FOUND		ILIBSTR("unable to dlopen library <%s>: %s")
+#define	DBG_MNTPT_NAMES			ILIBSTR("mount point for global zone path <%s> in zone <%s> is global zone mount point <%s> non-global zone mount point <%s>")
+#define	DBG_PATHS_ADD_FS		ILIBSTR("add inherited file system entry <%d> path <%s>")
+#define	DBG_PATHS_IS_INHERITED		ILIBSTR("path <%s> is inherited from <%s>")
+#define	DBG_PATHS_IS_NOT_INHERITED	ILIBSTR("path <%s> in root <%s> not inherited")
+#define	DBG_PATHS_NOT_INHERITED		ILIBSTR("path <%s> not inherited: no inherited file systems")
+#define	DBG_TO_ZONEHALT			ILIBSTR("halting zone <%s>")
+#define	DBG_TO_ZONEREADY		ILIBSTR("readying zone <%s>")
+#define	DBG_TO_ZONERUNNING		ILIBSTR("running zone <%s>")
+#define	DBG_TO_ZONEUNMOUNT		ILIBSTR("unmounting zone <%s>")
+#define	DBG_UNMOUNTING_DEV		ILIBSTR("unmounting package device <%s>")
+#define	DBG_ZONES_ADJLCKOBJ_EXIT	ILIBSTR("lock object <%s> adjusted to <%s> for root path <%s> resolved <%s>")
+#define	DBG_ZONES_APLK			ILIBSTR("acquire lock zone <%s> lock <%s> pid <%ld>")
+#define	DBG_ZONES_APLK_EXIT		ILIBSTR("acquire lock failure zone <%s> lock <%s> pid <%ld>: return <%d> status <%d> <%s>")
+#define	DBG_ZONES_APLK_RESULTS		ILIBSTR("acquire lock success zone <%s> lock <%s> key <%s> results <%s>")
+#define	DBG_ZONES_ARE_IMPLEMENTED	ILIBSTR("zones are implemented")
+#define	DBG_ZONES_CHG_Z_STATE		ILIBSTR("change zone <%s> from state <%d> to state <%d>")
+#define	DBG_ZONES_CHG_Z_STATE_ENTRY	ILIBSTR("change zone <%d> to state <%d>")
+#define	DBG_ZONES_GET_ZONE_STATE	ILIBSTR("state of zone <%s> is <%ld>")
+#define	DBG_ZONES_LCK_OBJ		ILIBSTR("lock zone object <%s> zone <%s> pid <%ld> locks <%s>")
+#define	DBG_ZONES_LCK_OBJ_FOUND		ILIBSTR("lock zone examining object <%s> key <%s>: match")
+#define	DBG_ZONES_LCK_OBJ_NOTFOUND	ILIBSTR("lock zone examining object <%s> key <%s>: NO MATCH")
+#define	DBG_ZONES_LCK_OBJ_NOTHELD	ILIBSTR("object <%s> not locked on zone <%s>")
+#define	DBG_ZONES_LCK_THIS		ILIBSTR("lock this zone flags <0x%08lx>")
+#define	DBG_ZONES_LCK_ZONE		ILIBSTR("lock zone <%s> flags <0x%08lx>")
+#define	DBG_ZONES_LCK_ZONES		ILIBSTR("lock zones flags <0x%08lx>")
+#define	DBG_ZONES_LCK_ZONES_EXIST	ILIBSTR("locking all non-global zones defined")
+#define	DBG_ZONES_LCK_ZONES_NOZONES	ILIBSTR("no zones locked: no non-global zones exist")
+#define	DBG_ZONES_LCK_ZONES_UNIMP	ILIBSTR("no zones locked: zones are not implemented")
+#define	DBG_ZONES_LCK_ZONE_PATCHADM	ILIBSTR("locking patch administration: zone <%s> object <%s>")
+#define	DBG_ZONES_LCK_ZONE_PKGADM	ILIBSTR("locking package administration: zone <%s> object <%s>")
+#define	DBG_ZONES_LCK_ZONE_ZONEADM	ILIBSTR("locking zone administration: zone <%s> object <%s>")
+#define	DBG_ZONES_MOUNT_IN_LZ_ENTRY	ILIBSTR("mount in non-global zone: zone <%s> global-zone path <%s>")
+#define	DBG_ZONES_NGZ_LIST_STATES	ILIBSTR("non-global zone <%s> current state <%d> kernel status <%d>")
+#define	DBG_ZONES_NOT_IMPLEMENTED	ILIBSTR("zones are NOT implemented")
+#define	DBG_ZONES_RELK			ILIBSTR("release lock zone <%s> lock <%s> key <%s>")
+#define	DBG_ZONES_RELK_EXIT		ILIBSTR("release lock <%s> key <%s> to zone <%s>: return <%d> status <%d> results <%s>")
+#define	DBG_ZONES_ULK_OBJ		ILIBSTR("unlock zone object <%s> zone <%s> locks <%s>")
+#define	DBG_ZONES_ULK_OBJ_FOUND		ILIBSTR("unlock zone examining object <%s> key <%s>: match")
+#define	DBG_ZONES_ULK_OBJ_NONE		ILIBSTR("no objects locked on zone <%s>")
+#define	DBG_ZONES_ULK_OBJ_NOTFOUND	ILIBSTR("unlock zone examining object <%s> key <%s>: NO MATCH")
+#define	DBG_ZONES_ULK_OBJ_NOTHELD	ILIBSTR("object <%s> not locked on zone <%s>")
+#define	DBG_ZONES_ULK_THIS		ILIBSTR("unlock this zone flags <0x%08lx>")
+#define	DBG_ZONES_ULK_ZONE		ILIBSTR("unlock zone <%s> flags <0x%08lx>")
+#define	DBG_ZONES_ULK_ZONES		ILIBSTR("unlock zones flags <0x%08lx>")
+#define	DBG_ZONES_ULK_ZONES_EXIST	ILIBSTR("unlocking all non-global zones defined")
+#define	DBG_ZONES_ULK_ZONES_NOZONES	ILIBSTR("no zones unlocked: no non-global zones exist")
+#define	DBG_ZONES_ULK_ZONES_UNIMP	ILIBSTR("no zones unlocked: zones are not implemented")
+#define	DBG_ZONES_ULK_ZONE_PATCHADM	ILIBSTR("unlocking patch administration: zone <%s> object <%s>")
+#define	DBG_ZONES_ULK_ZONE_PKGADM	ILIBSTR("unlocking package administration: zone <%s> object <%s>")
+#define	DBG_ZONES_ULK_ZONE_ZONEADM	ILIBSTR("unlocking zone administration: zone <%s> object <%s>")
+#define	DBG_ZONES_UNMOUNT_FROM_LZ_ENTRY	ILIBSTR("unmount non-global zone: mount point <%s>")
+#define	DBG_ZONE_EXEC_CMD_ENTER		ILIBSTR("execute command <%s> on zone <%s> this zone <%s>")
+#define DBG_BRANDS_ARE_IMPLEMENTED	ILIBSTR("brands are implemented")
+#define DBG_BRANDS_NOT_IMPLEMENTED	ILIBSTR("brands are NOT implemented")
+
+/*
+ * I18N: these messages are error messages that can be displayed
+ * during the normal usage of the products
+ */
+
+#define	ERR_CANNOT_CREATE_CONTRACT	ILIBSTR("unable to create contract: %s")
+#define	ERR_CAPTURE_FILE		ILIBSTR("unable to open command output capture file <%s>: %s")
+#define	ERR_FORK			ILIBSTR("unable to create new process: %s")
+#define	ERR_GET_ZONEID			ILIBSTR("unable to get id of zone <%s>: %s")
+#define	ERR_GZMOUNT_FAILED		ILIBSTR("unable to mount global path <%s> local path <%s> zone <%s>: %s")
+#define	ERR_GZMOUNT_RESOLVEPATH		ILIBSTR("unable to determine zone <%s> dev path from <%s>: %s")
+#define	ERR_GZMOUNT_SNPRINTFGMP_FAILED	ILIBSTR("unable to create global zone mount point <%s> from <%s> <%s> <%s>: combined path exceeds maximum length of <%ld>")
+#define	ERR_GZMOUNT_SNPRINTFLMP_FAILED	ILIBSTR("unable to create local zone mount point <%s> from <%s>: combined path exceeds maximum length of <%ld>")
+#define	ERR_GZMOUNT_SNPRINTFUUID_FAILED	ILIBSTR("unable to create uuid <%s>: combined uuid exceeds maximum length of <%ld>")
+#define	ERR_GZMOUNT_SNPRINTF_FAILED	ILIBSTR("unable to create path <%s> from <%s>: combined path exceeds maximum length of <%ld>")
+#define	ERR_GZPATH_NOT_ABSOLUTE		ILIBSTR("unable to mount global zone path <%s>: path must be absolute")
+#define	ERR_GZPATH_NOT_DIR		ILIBSTR("unable to mount global zone path <%s>: %s")
+#define	ERR_GZUMOUNT_FAILED		ILIBSTR("unable to unmount <%s>: %s")
+#define	ERR_INHERITED_PATH_NOT_ABSOLUTE	ILIBSTR("inherited file system must be absolute path: <%s>")
+#define	ERR_INHERITED_PATH_NOT_DIR	ILIBSTR("inherited file system <%s> must be absolute path to directory: %s")
+#define	ERR_INHERITED_PATH_NULL		ILIBSTR("empty path specified for inherited file system: must be absolute path")
+#define	ERR_LZMNTPT_NOTDIR		ILIBSTR("unable to unmount global zone mount point <%s>: %s")
+#define	ERR_LZMNTPT_NOT_ABSOLUTE	ILIBSTR("unable to unmount <%s>: path must be absolute")
+#define	ERR_LZROOT_NOTDIR		ILIBSTR("unable to use <%s> as zone root path: %s")
+#define	ERR_MALLOC			ILIBSTR("unable to allocate %s memory, errno %d: %s")
+#define	ERR_MEM				ILIBSTR("unable to allocate memory.")
+#define	ERR_MEMORY	 		ILIBSTR("memory allocation failure, errno=%d")
+#define	ERR_MNTPT_MKDIR			ILIBSTR("unable to create temporary mount point <%s> in zone <%s>: %s")
+#define	ERR_NO_ZONE_ROOTPATH		ILIBSTR("unable to get root path of zone <%s>: %s")
+#define	ERR_PKGDIR_GETHANDLE		ILIBSTR("unable to get inherited directories: zonecfg_get_handle: %s")
+#define	ERR_PKGDIR_NOHANDLE		ILIBSTR("unable to get inherited directories: zonecfg_init_handle: %s")
+#define	ERR_PKGDIR_SETIPDENT		ILIBSTR("unable to get inherited directories: zonecfg_setipdent: %s")
+#define	ERR_ROOTPATH_EMPTY		ILIBSTR("unable to get root path of zone <%s>: empty path returned")
+#define	ERR_ZEXEC_ASSEMBLE		ILIBSTR("unable to establish connection with zone <%s>: could not assemble new environment")
+#define	ERR_ZEXEC_BADSTATE		ILIBSTR("unable to establish connection with zone <%s>: zone is in state '%s'")
+#define	ERR_ZEXEC_BADZONE		ILIBSTR("unable to establish connection with zone <%s>: no such zone")
+#define	ERR_ZEXEC_EFAULT		ILIBSTR("one or more file descriptors may be non-local (such as open across nfs): %s")
+#define	ERR_ZEXEC_EXECFAILURE		ILIBSTR("unable to establish connection with zone <%s>: exec failure: %s")
+#define	ERR_ZEXEC_GETPPRIV		ILIBSTR("unable to establish connection with zone <%s>: getppriv failed: %s")
+#define	ERR_ZEXEC_GZUSED		ILIBSTR("unable to establish connection with zone <%s>: global zone specified")
+#define	ERR_ZEXEC_NOROOTPATH		ILIBSTR("unable to establish connection with zone <%s>: cannot get root path: %s")
+#define	ERR_ZEXEC_NOTRUNNING		ILIBSTR("unable to establish connection with zone <%s>: not running - in state '%s'")
+#define	ERR_ZEXEC_NOT_IN_GZ		ILIBSTR("unable to establish connection with zone <%s>: not in the global zone")
+#define	ERR_ZEXEC_NOZONEID		ILIBSTR("unable to establish connection with zone <%s>: cannot get zone id: %s")
+#define	ERR_ZEXEC_PRIVS			ILIBSTR("unable to establish connection with zone <%s>: you lack sufficient privilege to access the zone")
+#define	ERR_ZEXEC_PRIV_ALLOCSET		ILIBSTR("unable to establish connection with zone <%s>o: priv_allocset failed: %s")
+#define	ERR_ZEXEC_ZONEENTER		ILIBSTR("unable to establish connection with zone <%s>: could not enter zone: %s")
+#define	ERR_ZONEBOOT_CMD_ERROR		ILIBSTR("unable to boot zone: problem running <%s> on zone <%s>: error %d%s%s")
+#define	ERR_ZONEBOOT_CMD_SIGNAL		ILIBSTR("unable to boot zone: problem running <%s> on zone <%s>: terminated by signal")
+#define	ERR_ZONEBOOT_DIDNT_BOOT		ILIBSTR("unable to boot zone <%s>: zone failed to transition to running state")
+#define	ERR_ZONEBOOT_EXEC               ILIBSTR("unable to boot zone: could not execute zone administration command <%s>: %s")
+#define	ERR_ZONEHALT_EXEC		ILIBSTR("unable to halt zone: could not execute zone administration command <%s>: %s")
+#define	ERR_ZONEINDEX_OPEN		ILIBSTR("unable to open zone index file %s: %s")
+#define	ERR_ZONEREADY_CMDFAIL		ILIBSTR("unable to ready zone: problem running <%s> on zone <%s>: %s%s%s")
+#define	ERR_ZONEREADY_DIDNT_READY	ILIBSTR("unable to ready zone <%s>: zone failed to transition to ready state")
+#define	ERR_ZONEREADY_EXEC		ILIBSTR("unable to ready zone: could not execute zone administration command <%s>: %s")
+#define	ERR_ZONEROOT_NOTDIR		ILIBSTR("unable to use temporary mount point <%s> in zone <%s>: %s")
+#define	ERR_ZONES_LCK_THIS_PATCHADM	ILIBSTR("Unable to acquire patch administration lock for this system; try again later")
+#define	ERR_ZONES_LCK_THIS_PKGADM	ILIBSTR("Unable to acquire package administration lock for this system; try again later")
+#define	ERR_ZONES_LCK_THIS_ZONEADM	ILIBSTR("Unable to acquire zone administration lock for this system; please try again later")
+#define	ERR_ZONES_LCK_ZONES_FAILED	ILIBSTR("Unable to acquire lock on non-global zone <%s>: releasing all locks")
+#define	ERR_ZONES_LCK_ZONE_PATCHADM	ILIBSTR("Unable to acquire patch administration lock for zone <%s>; please try again later")
+#define	ERR_ZONES_LCK_ZONE_PKGADM	ILIBSTR("Unable to acquire package administration lock for zone <%s>; please try again later")
+#define	ERR_ZONES_LCK_ZONE_ZONEADM	ILIBSTR("Unable to acquire zone administration lock for zone <%s>; please try again later")
+#define	ERR_ZONES_NOT_IMPLEMENTED	ILIBSTR("error: zones not implemented")
+#define	ERR_ZONES_ULK_THIS_PACKAGE	ILIBSTR("Unable to release package administration lock for this system; try again later")
+#define	ERR_ZONES_ULK_THIS_PATCH	ILIBSTR("Unable to release patch administration lock for this system; try again later")
+#define	ERR_ZONES_ULK_THIS_ZONES	ILIBSTR("Unable to release zone administration lock for this system; please try again later")
+#define	ERR_ZONE_LIST_EMPTY		ILIBSTR("empty zone list specified")
+#define	ERR_ZONE_NAME_ILLEGAL		ILIBSTR("illegal zone name %.*s")
+#define	ERR_ZONE_NONEXISTENT		ILIBSTR("zone %s does not exist")
+#define ERR_INHERITED_PATH_TOO_LONG     ILIBSTR("inherited path too long current length <%d> maximum length <%d> bytes: <%s>")
+#define	ERR_OPEN_READ			ILIBSTR("unable to open <%s> for reading: (%d) %s")
+#define	ERR_ZONEUNMOUNT_CMD_SIGNAL	ILIBSTR("unable to unmount zone: problem running <%s> on zone <%s>: terminated by signal")
+#define	ERR_ZONEUNMOUNT_EXEC		ILIBSTR("unable to unmount zone: could not execute zone administration command <%s>: %s")
+#define	ERR_ZONEUNMOUNT_CMD_ERROR	ILIBSTR("unable to unmount zone: problem running <%s> on zone <%s>: error %d%s%s")
+#define ERR_BRAND_GETBRAND	ILIBSTR("unable to get zone brand: zonecfg_get_brand: %s")
+
+/*
+ * I18N: these are messages that can be displayed during the normal
+ * usage of the products
+ */
+
+#define	MSG_PROG_ERR			ILIBSTR("ERROR: %s")
+#define	MSG_ZONES_LCK_THIS_PATCHADM	ILIBSTR("## Waiting for up to <%ld> seconds for patch administration commands to become available (another user is administering patches)")
+#define	MSG_ZONES_LCK_THIS_PKGADM	ILIBSTR("## Waiting for up to <%ld> seconds for package administration commands to become available (another user is administering packages)")
+#define	MSG_ZONES_LCK_THIS_ZONEADM	ILIBSTR("## Waiting for up to <%ld> seconds for zone administration commands to become available (another user is administering zones)")
+#define	MSG_ZONES_LCK_ZONE_PATCHADM	ILIBSTR("## Waiting for up to <%ld> seconds for patch administration commands to become available (another user is administering patches on zone <%s>)")
+#define	MSG_ZONES_LCK_ZONE_PKGADM	ILIBSTR("## Waiting for up to <%ld> seconds for package administration commands to become available (another user is administering packages on zone <%s>)")
+#define	MSG_ZONES_LCK_ZONE_ZONEADM	ILIBSTR("## Waiting for up to <%ld> seconds for zone administration commands to become available (another user is administering zones on zone <%s>)")
+
+/*
+ * I18N: these messages are warning messages that can be displayed
+ * during the normal usage of the products
+ */
+
+#define	WRN_ZONES_ULK_ZONE_PATCHADM	ILIBSTR("WARNING: Unable to release patch administration lock for zone <%s>")
+#define	WRN_ZONES_ULK_ZONE_PKGADM	ILIBSTR("WARNING: Unable to release package administration lock for zone <%s>")
+#define	WRN_ZONES_ULK_ZONE_ZONEADM	ILIBSTR("WARNING: Unable to release zone administration lock for zone <%s>")
+
+/* END CSTYLED */
+
+/*
+ * C++ postfix
+ */
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif	/* _ZONES_STRINGS_H */
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/usr/src/lib/libinstzones/common/zones_utils.c	Fri Jun 05 10:28:40 2009 -0400
@@ -0,0 +1,689 @@
+/*
+ * 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
+ * or http://www.opensolaris.org/os/licensing.
+ * 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 2009 Sun Microsystems, Inc.  All rights reserved.
+ * Use is subject to license terms.
+ */
+
+
+
+/*
+ * Module:	zones.c
+ * Group:	libinstzones
+ * Description:	Provide "zones" interface for install consolidation code
+ *
+ * Public Methods:
+ *
+ *  _z_close_file_descriptors - close a file descriptor "a_fd" not in the
+ *	list "a_fds"
+ *  _z_echo - Output an interactive message if interaction is enabled
+ *  _z_echoDebug - Output a debugging message if debugging is enabled
+ *  _z_get_inherited_dirs - return array of directories inherited by
+ *	specified zone
+ *  _z_is_directory - determine if specified path exists and is a directory
+ *  _z_program_error - Output an error message to the appropriate destinations
+ *  _z_pluginCatchSigint - SIGINT/SIGHUP interrupt handler
+ *  _z_running_in_global_zone - Determine if this process is running in the
+ *	global zone
+ *  _z_zones_are_implemented - Determine if zones are supported by the
+ *	current system
+ *  _z_brands_are_implemented - determine if branded zones are implemented on
+ *		this system
+ */
+
+/*
+ * System includes
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <fcntl.h>
+#include <ctype.h>
+#include <sys/types.h>
+#include <sys/param.h>
+#include <string.h>
+#include <strings.h>
+#include <sys/stat.h>
+#include <stdarg.h>
+#include <limits.h>
+#include <errno.h>
+#include <signal.h>
+#include <stropts.h>
+#include <libintl.h>
+#include <locale.h>
+#include <assert.h>
+#include <dlfcn.h>
+
+/*
+ * local includes
+ */
+
+#include "instzones_lib.h"
+#include "zones_strings.h"
+
+/*
+ * Private structures
+ */
+
+/*
+ * these dynamic libraries are required in order to use the branded zones
+ * functionality.  If these libraries are not available at runtime,
+ * then the zones we find are assumed to be native zones.
+ */
+
+#define	BRAND1_LIBRARY	"libbrand.so.1"
+#define	BRAND_LIBRARY	"libbrand.so"
+
+/*
+ * Library Function Prototypes
+ */
+
+/*
+ * Local Function Prototypes
+ */
+
+static void	error_and_exit(int error_num);
+
+static void 	(*fatal_err_func)() = &error_and_exit;
+
+/* ----------------------- private functions -------------------------- */
+/*
+ * error_and_exit()
+ *	Abort routine. An exit code of '2' is used by all applications
+ *	to indicate a non-recoverable fatal error.
+ * Parameters:
+ *	error_num - error index number:
+ *			ERR_MALLOC_FAIL
+ * Return:
+ *	none
+ * Status:
+ *	private
+ */
+static void
+error_and_exit(int error_num)
+{
+	if (error_num == ERR_MALLOC_FAIL)
+		(void) fprintf(stderr, "Allocation of memory failed\n");
+	else
+		(void) fprintf(stderr, "ERROR: code %d\n", error_num);
+	exit(2);
+}
+
+/*
+ * *****************************************************************************
+ * global external (public) functions
+ * *****************************************************************************
+ */
+
+/*
+ * Name:	_z_close_file_descriptors
+ * Description:	close a file descriptor "a_fd" not in the list "a_fds"
+ *		This function is called from the fdwalk() library function.
+ *		If the file descriptor passed in is NOT in the list passed in,
+ *		the file is closed.
+ * Arguments:	a_fds - [RO, *RO] - (void *)
+ *			Pointer to list of file descriptors to keep open
+ *		a_fd - [RO, *RO] - (int)
+ *			File descriptor to check
+ * Returns:	int
+ *			0 - success
+ */
+
+int
+_z_close_file_descriptors(void *a_fds, int a_fd)
+{
+	int	*fds;
+	int	i;
+
+	/* do not close standard input, output, or error file descriptors */
+
+	if (a_fd == STDIN_FILENO || a_fd == STDOUT_FILENO ||
+	    a_fd == STDERR_FILENO) {
+		return (0);
+	}
+
+	/* if no file descriptor retention list, close this file */
+
+	if (a_fds == (void *)NULL) {
+		(void) close(a_fd);
+		return (0);
+	}
+
+	/*
+	 * retention list provided, skip this descriptor if its in the list
+	 */
+
+	fds = (int *)a_fds;
+
+	for (i = 0; fds[i] != -1; i++) {
+		if (fds[i] == a_fd) {
+			return (0);
+		}
+	}
+
+	/* this descriptor not in retention list - close this file */
+
+	(void) close(a_fd);
+
+	return (0);
+}
+
+/*
+ * Name:	_z_echo
+ * Synopsis:	Output an interactive message if interaction is enabled
+ * Description:	Main method for outputting an interactive message; call to
+ *		output interactive message if interation has not been disabled
+ *		by a previous call to echoSetFlag(0).
+ * Arguments:	format - [RO, RO*] (char *)
+ *			printf-style format for debugging message to be output
+ *		VARG_LIST - [RO] (?)
+ *			arguments as appropriate to 'format' specified
+ * Returns:	void
+ */
+
+/*PRINTFLIKE1*/
+void
+_z_echo(char *a_format, ...)
+{
+	va_list ap;
+	char	message[MAX_MESSAGE_SIZE];
+
+	/* entry assertions */
+
+	assert(a_format != NULL);
+
+	/* return if no progerr function registered */
+
+	if (_z_global_data._z_echo == NULL) {
+		return;
+	}
+
+	/* capture message */
+
+	va_start(ap, a_format);
+	(void) vsnprintf(message, sizeof (message), a_format, ap);
+	va_end(ap);
+
+	/* pass message to registered function */
+
+	(_z_global_data._z_echo)("%s", message);
+}
+
+/*
+ * Name:	_z_echoDebug
+ * Synopsis:	Output a debugging message if debugging is enabled
+ * Description:	Main method for outputting a debugging message; call to
+ *		output debugging message if debugging has been enabled
+ *		by a previous call to _z_echoDebugSetFlag(1).
+ * Arguments:	format - [RO, RO*] (char *)
+ *			printf-style format for debugging message to be output
+ *		VARG_LIST - [RO] (?)
+ *			arguments as appropriate to 'format' specified
+ * Returns:	void
+ * NOTE:	format of message will be:
+ *			# [ aaa bbb ccc ] message
+ *		where:	aaa - process i.d.
+ *			bbb - zone i.d.
+ *			ccc - name of program
+ * 		for example:
+ *			# [ 25685   0 pkgadd     ] unable to get package list
+ */
+
+/*PRINTFLIKE1*/
+void
+_z_echoDebug(char *a_format, ...)
+{
+	va_list ap;
+	char	message[MAX_MESSAGE_SIZE];
+
+	/* entry assertions */
+
+	assert(a_format != NULL);
+
+	/* return if no progerr function registered */
+
+	if (_z_global_data._z_echo_debug == NULL) {
+		return;
+	}
+
+	/* capture message */
+
+	va_start(ap, a_format);
+	(void) vsnprintf(message, sizeof (message), a_format, ap);
+	va_end(ap);
+
+	/* pass message to registered function */
+
+	(_z_global_data._z_echo_debug)("%s", message);
+}
+
+/*
+ * Name:	_z_get_inherited_dirs
+ * Description:	return array of directories inherited by specified zone
+ * Arguments:	a_zoneName - [RO, *RO] - (char *)
+ *			Pointer to string representing the name of the zone
+ *			to return the list of inherited directories for
+ * Returns:	char **
+ *			!= NULL - list of inherited directories, terminated
+ *					by a NULL pointer
+ *			== NULL - error - unable to retrieve list
+ */
+
+char **
+_z_get_inherited_dirs(char *a_zoneName)
+{
+	char			**dirs = NULL;
+	int			err;
+	int			numIpdents = 0;
+	struct zone_fstab	lookup;
+	zone_dochandle_t	handle = NULL;
+
+	/* entry assertions */
+
+	assert(a_zoneName != NULL);
+	assert(*a_zoneName != '\0');
+
+	/* initialize the zone configuration interface handle */
+
+	handle = zonecfg_init_handle();
+	if (handle == NULL) {
+		_z_program_error(ERR_PKGDIR_NOHANDLE,
+		    zonecfg_strerror(Z_NOMEM));
+		return (NULL);
+	}
+
+	/* get handle to configuration information for the specified zone */
+
+	err = zonecfg_get_handle(a_zoneName, handle);
+	if (err != Z_OK) {
+		/* If there was no zone before, that's OK */
+		if (err != Z_NO_ZONE) {
+			_z_program_error(ERR_PKGDIR_GETHANDLE,
+			    zonecfg_strerror(err));
+			zonecfg_fini_handle(handle);
+			return (NULL);
+		}
+	}
+	assert(handle != NULL);
+
+	/* get handle to non-global zone ipd enumerator */
+
+	err = zonecfg_setipdent(handle);
+	if (err != Z_OK) {
+		_z_program_error(ERR_PKGDIR_SETIPDENT, zonecfg_strerror(err));
+		zonecfg_fini_handle(handle);
+		return (NULL);
+	}
+
+	/* enumerate the non-global zone ipd's */
+
+	while (zonecfg_getipdent(handle, &lookup) == Z_OK) {
+		dirs = _z_realloc(dirs, sizeof (char **)*(numIpdents+1));
+		dirs[numIpdents++] = strdup(lookup.zone_fs_dir);
+	}
+
+	if (dirs != NULL) {
+		dirs = _z_realloc(dirs, sizeof (char **)*(numIpdents+1));
+		dirs[numIpdents] = NULL;
+	}
+
+	/* toss non-global zone ipd enumerator handle */
+
+	(void) zonecfg_endipdent(handle);
+
+	return (dirs);
+}
+
+/*
+ * Name:	_z_is_directory
+ * Description:	determine if specified path exists and is a directory
+ * Arguments:	path - pointer to string representing the path to verify
+ * returns: 0 - directory exists
+ *	    1 - directory does not exist or is not a directory
+ * NOTE:	errno is set appropriately
+ */
+
+int
+_z_is_directory(char *path)
+{
+	struct stat statbuf;
+
+	/* entry assertions */
+
+	assert(path != NULL);
+	assert(*path != '\0');
+
+	/* return error if path does not exist */
+
+	if (stat(path, &statbuf) != 0) {
+		return (1);
+	}
+
+	/* return error if path is not a directory */
+
+	if ((statbuf.st_mode & S_IFMT) != S_IFDIR) {
+		errno = ENOTDIR;
+		return (1);
+	}
+
+	/* path exists and is a directory */
+
+	return (0);
+}
+
+/*
+ * Name:	_z_pluginCatchSigint
+ * Synopsis:	SIGINT/SIGHUP interrupt handler
+ * Description:	Catch the "SIGINT" and "SIGHUP" signals:
+ *		-> increment _z_SigReceived global variable
+ *		-> propagate signal to "_z_ChildProcessId" if registered (!= -1)
+ * Arguments:	signo - [RO, *RO] - (int)
+ *			Signal number that was caught
+ * Returns:	void
+ */
+
+void
+_z_sig_trap(int a_signo)
+{
+	/* bump signals received count */
+
+	_z_global_data._z_SigReceived++;
+
+	/* if child process registered, propagate signal to child */
+
+	if (_z_global_data._z_ChildProcessId > 0) {
+		(void) kill(_z_global_data._z_ChildProcessId, a_signo);
+	}
+}
+
+/*
+ * Name:	_z_program_error
+ * Description:	Output an error message to the appropriate destinations
+ * Arguments:	format - [RO, RO*] (char *)
+ *			printf-style format for debugging message to be output
+ *		VARG_LIST - [RO] (?)
+ *			arguments as appropriate to 'format' specified
+ * Returns:	void
+ * NOTE:	format of message will be:
+ *			[aaa: ] ERROR: message
+ *		where:	aaa - program name (if set)
+ *			message - results of format and arguments
+ * 		for example:
+ *			ERROR: unable to get package list
+ */
+
+/*PRINTFLIKE1*/
+void
+_z_program_error(char *a_format, ...)
+{
+	va_list ap;
+	char	message[MAX_MESSAGE_SIZE];
+
+	/* entry assertions */
+
+	assert(a_format != NULL);
+
+	/* return if no progerr function registered */
+
+	if (_z_global_data._z_progerr == NULL) {
+		return;
+	}
+
+	/* capture message */
+
+	va_start(ap, a_format);
+	(void) vsnprintf(message, sizeof (message), a_format, ap);
+	va_end(ap);
+
+	/* pass message to registered function */
+
+	(_z_global_data._z_progerr)(MSG_PROG_ERR, message);
+}
+
+/*
+ * Name:	_z_running_in_global_zone
+ * Synopsis:	Determine if this process is running in the global zone
+ * Arguments:	void
+ * Returns:	boolean_t
+ *			== B_TRUE - this process is running in the global zone
+ *			== B_FALSE - this process is running in a nonglobal zone
+ */
+
+boolean_t
+_z_running_in_global_zone(void)
+{
+	zoneid_t	zoneid = (zoneid_t)-1;
+
+	/*
+	 * if zones are not implemented, there is no way to tell if zones
+	 * are supported or not - in this case, we can only be running in the
+	 * global zone (since non-global zones cannot exist) so return TRUE
+	 */
+
+	if (z_zones_are_implemented() == B_FALSE) {
+		return (B_TRUE);
+	}
+
+	/* get the zone i.d. of the current zone */
+
+	zoneid = getzoneid();
+
+	/* return TRUE if this is the global zone i.d. */
+
+	if (zoneid == GLOBAL_ZONEID) {
+		return (B_TRUE);
+	}
+
+	/* return FALSE - not in the global zone */
+
+	return (B_FALSE);
+}
+
+/*
+ * Name:	_z_zones_are_implemented
+ * Synopsis:	Determine if zones are supported by the current system
+ * Arguments:	void
+ * Returns:	boolean_t
+ *			== B_TRUE - zones are supported
+ *			== B_FALSE - zones are not supported
+ */
+
+boolean_t
+_z_zones_are_implemented(void)
+{
+	void	*libptr = NULL;
+
+	/* locate zone cfg library */
+
+	libptr = dlopen(ZONECFG_LIBRARY, RTLD_NOW|RTLD_GLOBAL);
+	if (libptr == (void *)NULL) {
+		_z_echoDebug(DBG_LIBRARY_NOT_FOUND, ZONECFG_LIBRARY, dlerror());
+		libptr = dlopen(ZONECFG1_LIBRARY, RTLD_NOW|RTLD_GLOBAL);
+	}
+
+	/* return false if library not available */
+
+	if (libptr == (void *)NULL) {
+		_z_echoDebug(DBG_LIBRARY_NOT_FOUND, ZONECFG1_LIBRARY,
+		    dlerror());
+		return (B_FALSE);
+	}
+
+	/* library available - close handle */
+
+	(void) dlclose(libptr);
+
+	/* locate contract filesystem library */
+
+	libptr = dlopen(CONTRACT_LIBRARY, RTLD_NOW|RTLD_GLOBAL);
+	if (libptr == (void *)NULL) {
+		_z_echoDebug(DBG_LIBRARY_NOT_FOUND, CONTRACT_LIBRARY,
+		    dlerror());
+		libptr = dlopen(CONTRACT1_LIBRARY, RTLD_NOW|RTLD_GLOBAL);
+	}
+
+	/* return false if library not available */
+
+	if (libptr == (void *)NULL) {
+		_z_echoDebug(DBG_LIBRARY_NOT_FOUND, CONTRACT1_LIBRARY,
+		    dlerror());
+		return (B_FALSE);
+	}
+
+	/* library available - close handle */
+
+	(void) dlclose(libptr);
+
+	/* return success */
+
+	return (B_TRUE);
+}
+
+boolean_t
+_z_brands_are_implemented(void)
+{
+	void	*libptr;
+
+	/* locate brand library */
+
+	libptr = dlopen(BRAND_LIBRARY, RTLD_NOW|RTLD_GLOBAL);
+	if (libptr == NULL) {
+		_z_echoDebug(DBG_LIBRARY_NOT_FOUND, BRAND_LIBRARY, dlerror());
+		libptr = dlopen(BRAND1_LIBRARY, RTLD_NOW|RTLD_GLOBAL);
+	}
+
+	/* return false if library not available */
+
+	if (libptr == NULL) {
+		_z_echoDebug(DBG_LIBRARY_NOT_FOUND, BRAND1_LIBRARY, dlerror());
+		return (B_FALSE);
+	}
+
+	/* library available - close handle */
+
+	(void) dlclose(libptr);
+
+	/* return success */
+
+	return (B_TRUE);
+}
+
+/*
+ * z_calloc()
+ * 	Allocate 'size' bytes from the heap using calloc()
+ * Parameters:
+ *	size	- number of bytes to allocate
+ * Return:
+ *	NULL	- calloc() failure
+ *	void *	- pointer to allocated structure
+ * Status:
+ *	public
+ */
+void *
+_z_calloc(size_t size)
+{
+	void *	tmp;
+
+	if ((tmp = (void *) malloc(size)) == NULL) {
+		fatal_err_func(ERR_MALLOC_FAIL);
+		return (NULL);
+	}
+
+	(void) memset(tmp, 0, size);
+	return (tmp);
+}
+
+/*
+ * z_malloc()
+ * 	Alloc 'size' bytes from heap using malloc()
+ * Parameters:
+ *	size	- number of bytes to malloc
+ * Return:
+ *	NULL	- malloc() failure
+ *	void *	- pointer to allocated structure
+ * Status:
+ *	public
+ */
+void *
+_z_malloc(size_t size)
+{
+	void *tmp;
+
+	if ((tmp = (void *) malloc(size)) == NULL) {
+		fatal_err_func(ERR_MALLOC_FAIL);
+		return (NULL);
+	} else
+		return (tmp);
+}
+
+/*
+ * _z_realloc()
+ *	Calls realloc() with the specfied parameters. _z_realloc()
+ *	checks for realloc failures and adjusts the return value
+ *	automatically.
+ * Parameters:
+ *	ptr	- pointer to existing data block
+ * 	size	- number of bytes additional
+ * Return:
+ *	NULL	- realloc() failed
+ *	void *	- pointer to realloc'd structured
+ * Status:
+ *	public
+ */
+void *
+_z_realloc(void *ptr, size_t size)
+{
+	void *tmp;
+
+	if ((tmp = (void *)realloc(ptr, size)) == (void *)NULL) {
+		fatal_err_func(ERR_MALLOC_FAIL);
+		return ((void *)NULL);
+	} else
+		return (tmp);
+}
+
+/*
+ * z_strdup()
+ *	Allocate space for the string from the heap, copy 'str' into it,
+ *	and return a pointer to it.
+ * Parameters:
+ *	str	- string to duplicate
+ * Return:
+ *	NULL	- duplication failed or 'str' was NULL
+ * 	char *	- pointer to newly allocated/initialized structure
+ * Status:
+ *	public
+ */
+void *
+_z_strdup(char *str)
+{
+	char *tmp;
+
+	if (str == NULL)
+		return ((char *)NULL);
+
+	if ((tmp = strdup(str)) == NULL) {
+		fatal_err_func(ERR_MALLOC_FAIL);
+		return ((char *)NULL);
+	} else
+		return (tmp);
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/usr/src/lib/libinstzones/i386/Makefile	Fri Jun 05 10:28:40 2009 -0400
@@ -0,0 +1,28 @@
+#
+# 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
+# or http://www.opensolaris.org/os/licensing.
+# 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 2009 Sun Microsystems, Inc.  All rights reserved.
+# Use is subject to license terms.
+#
+
+include ../Makefile.com
+
+install: all $(ROOTLIBS) $(ROOTLINKS) $(ROOTLINT)
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/usr/src/lib/libinstzones/sparc/Makefile	Fri Jun 05 10:28:40 2009 -0400
@@ -0,0 +1,28 @@
+#
+# 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
+# or http://www.opensolaris.org/os/licensing.
+# 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 2009 Sun Microsystems, Inc.  All rights reserved.
+# Use is subject to license terms.
+#
+
+include ../Makefile.com
+
+install: all $(ROOTLIBS) $(ROOTLINKS) $(ROOTLINT)
--- a/usr/src/lib/libntfs/THIRDPARTYLICENSE	Fri Jun 05 10:27:16 2009 -0400
+++ b/usr/src/lib/libntfs/THIRDPARTYLICENSE	Fri Jun 05 10:28:40 2009 -0400
@@ -1,3 +1,11 @@
+"GPL Disclaimer
+For the avoidance of doubt, except that if any license choice other than GPL or
+LGPL is available it will apply instead, Sun elects to use only the General
+Public License version 2 (GPLv2) at this time for any software where a choice of
+GPL license versions is made available with the language indicating that GPLv2
+or any later version may be used, or where a choice of which version of the GPL
+is applied is otherwise unspecified."
+
 		    GNU GENERAL PUBLIC LICENSE
 		       Version 2, June 1991
 
--- a/usr/src/lib/libntfs/THIRDPARTYLICENSE.readme	Fri Jun 05 10:27:16 2009 -0400
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,7 +0,0 @@
-"GPL Disclaimer
-For the avoidance of doubt, except that if any license choice other than GPL or
-LGPL is available it will apply instead, Sun elects to use only the General
-Public License version 2 (GPLv2) at this time for any software where a choice of
-GPL license versions is made available with the language indicating that GPLv2
-or any later version may be used, or where a choice of which version of the GPL
-is applied is otherwise unspecified."
--- a/usr/src/lib/libparted/THIRDPARTYLICENSE	Fri Jun 05 10:27:16 2009 -0400
+++ b/usr/src/lib/libparted/THIRDPARTYLICENSE	Fri Jun 05 10:28:40 2009 -0400
@@ -1,3 +1,10 @@
+"For the avoidance of doubt, except that if any license choice other 
+than GPL or LGPL is available it will apply instead, Sun elects to 
+use only the General Public License version 3 (GPLv3) at this time 
+for any software where a choice of GPL license versions is made 
+available with the language indicating that GPLv3 or any later 
+version may be used, or where a choice of which version of the GPL 
+is applied is otherwise unspecified."
 
 		    GNU GENERAL PUBLIC LICENSE
 		       Version 3, 29 June 2007
--- a/usr/src/lib/libparted/THIRDPARTYLICENSE.readme	Fri Jun 05 10:27:16 2009 -0400
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,7 +0,0 @@
-"For the avoidance of doubt, except that if any license choice other 
-than GPL or LGPL is available it will apply instead, Sun elects to 
-use only the General Public License version 3 (GPLv3) at this time 
-for any software where a choice of GPL license versions is made 
-available with the language indicating that GPLv3 or any later 
-version may be used, or where a choice of which version of the GPL 
-is applied is otherwise unspecified."
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/usr/src/lib/libpkg/Makefile	Fri Jun 05 10:28:40 2009 -0400
@@ -0,0 +1,43 @@
+#
+# 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
+# or http://www.opensolaris.org/os/licensing.
+# 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 2009 Sun Microsystems, Inc.  All rights reserved.
+# Use is subject to license terms.
+#
+
+SUBDIRS =	$(MACH) 
+
+all :=		TARGET = all
+install :=	TARGET = install
+clean :=	TARGET = clean
+clobber :=	TARGET = clobber
+_msg :=		TARGET = _msg
+lint :=		TARGET = lint
+
+
+.KEEP_STATE:
+
+all clean clobber install _msg lint: $(SUBDIRS)
+
+$(SUBDIRS): FRC
+	@cd $@; pwd; $(MAKE) $(TARGET)
+
+FRC:
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/usr/src/lib/libpkg/Makefile.com	Fri Jun 05 10:28:40 2009 -0400
@@ -0,0 +1,85 @@
+#
+# 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
+# or http://www.opensolaris.org/os/licensing.
+# 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 2009 Sun Microsystems, Inc.  All rights reserved.
+# Use is subject to license terms.
+#
+
+LIBRARY= libpkg.a
+VERS=	.1
+
+# include library definitions
+OBJECTS=	\
+		canonize.o   ckparam.o    ckvolseq.o \
+		devtype.o    dstream.o    gpkglist.o \
+		gpkgmap.o    isdir.o      logerr.o \
+		mappath.o    ncgrpw.o     nhash.o \
+		pkgexecl.o   pkgexecv.o   pkgmount.o \
+		pkgtrans.o   pkgxpand.o   ppkgmap.o \
+		progerr.o    putcfile.o   rrmdir.o \
+		runcmd.o     srchcfile.o  tputcfent.o \
+		verify.o     security.o   pkgweb.o \
+		pkgerr.o     keystore.o   p12lib.o \
+		vfpops.o     fmkdir.o     pkgstr.o \
+		handlelocalfs.o
+
+
+# include library definitions
+include $(SRC)/lib/Makefile.lib
+
+SRCDIR=		../common
+
+POFILE =	libpkg.po
+MSGFILES =	$(OBJECTS:%.o=../common/%.i)
+CLEANFILES +=   $(MSGFILES)
+
+# This library is NOT lint clean
+
+# openssl forces us to ignore dubious pointer casts, thanks to its clever
+# use of macros for stack management.
+LINTFLAGS=      -umx -errtags \
+		-erroff=E_BAD_PTR_CAST_ALIGN,E_BAD_PTR_CAST
+$(LINTLIB):=	SRCS = $(SRCDIR)/$(LINTSRC)
+
+
+LIBS = $(DYNLIB) $(LINTLIB)
+
+
+LDLIBS +=	-lc -lssl -lwanboot -lcrypto -lscf -ladm
+
+CFLAGS +=	$(CCVERBOSE)
+CPPFLAGS +=	-I$(SRCDIR) -D_FILE_OFFSET_BITS=64
+
+.KEEP_STATE:
+
+all:	$(LIBS)
+
+$(POFILE): $(MSGFILES)
+	$(BUILDPO.msgfiles)
+
+_msg: $(MSGDOMAINPOFILE)
+
+lint: lintcheck
+
+# include library targets
+include $(SRC)/lib/Makefile.targ
+include $(SRC)/Makefile.msg.targ
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/usr/src/lib/libpkg/THIRDPARTYLICENSE	Fri Jun 05 10:28:40 2009 -0400
@@ -0,0 +1,51 @@
+ * ====================================================================
+ * Copyright (c) 1999 The OpenSSL Project.  All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in
+ *    the documentation and/or other materials provided with the
+ *    distribution.
+ *
+ * 3. All advertising materials mentioning features or use of this
+ *    software must display the following acknowledgment:
+ *    "This product includes software developed by the OpenSSL Project
+ *    for use in the OpenSSL Toolkit. (http://www.OpenSSL.org/)"
+ *
+ * 4. The names "OpenSSL Toolkit" and "OpenSSL Project" must not be used to
+ *    endorse or promote products derived from this software without
+ *    prior written permission. For written permission, please contact
+ *    licensing@OpenSSL.org.
+ *
+ * 5. Products derived from this software may not be called "OpenSSL"
+ *    nor may "OpenSSL" appear in their names without prior written
+ *    permission of the OpenSSL Project.
+ *
+ * 6. Redistributions of any form whatsoever must retain the following
+ *    acknowledgment:
+ *    "This product includes software developed by the OpenSSL Project
+ *    for use in the OpenSSL Toolkit (http://www.OpenSSL.org/)"
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE OpenSSL PROJECT ``AS IS'' AND ANY
+ * EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE OpenSSL PROJECT OR
+ * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+ * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
+ * OF THE POSSIBILITY OF SUCH DAMAGE.
+ * ====================================================================
+ *
+ * This product includes cryptographic software written by Eric Young
+ * (eay@cryptsoft.com).  This product includes software written by Tim
+ * Hudson (tjh@cryptsoft.com).
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/usr/src/lib/libpkg/THIRDPARTYLICENSE.descrip	Fri Jun 05 10:28:40 2009 -0400
@@ -0,0 +1,1 @@
+PORTIONS OF THE SVR4 PACKAGE LIBRARY (LIBPKG)
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/usr/src/lib/libpkg/common/canonize.c	Fri Jun 05 10:28:40 2009 -0400
@@ -0,0 +1,97 @@
+/*
+ * 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
+ * or http://www.opensolaris.org/os/licensing.
+ * 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 2009 Sun Microsystems, Inc.  All rights reserved.
+ * Use is subject to license terms.
+ */
+
+/* Copyright (c) 1984, 1986, 1987, 1988, 1989 AT&T */
+/* All Rights Reserved */
+
+
+#include <string.h>
+
+#define	isdot(x)	((x[0] == '.') && (!x[1] || (x[1] == '/')))
+#define	isdotdot(x)	((x[0] == '.') && (x[1] == '.') && \
+			    (!x[2] || (x[2] == '/')))
+
+void
+canonize(char *file)
+{
+	char *pt, *last;
+	int level;
+
+	/* remove references such as "./" and "../" and "//" */
+	for (pt = file; *pt; /* void */) {
+		if (isdot(pt))
+			(void) strcpy(pt, pt[1] ? pt+2 : pt+1);
+		else if (isdotdot(pt)) {
+			level = 0;
+			last = pt;
+			do {
+				level++;
+				last += 2;
+				if (*last)
+					last++;
+			} while (isdotdot(last));
+			--pt; /* point to previous '/' */
+			while (level--) {
+				if (pt <= file)
+					return;
+				while ((*--pt != '/') && (pt > file))
+					;
+			}
+			if (*pt == '/')
+				pt++;
+			(void) strcpy(pt, last);
+		} else {
+			while (*pt && (*pt != '/'))
+				pt++;
+			if (*pt == '/') {
+				while (pt[1] == '/')
+					(void) strcpy(pt, pt+1);
+				pt++;
+			}
+		}
+	}
+	if ((--pt > file) && (*pt == '/'))
+		*pt = '\0';
+}
+
+void
+canonize_slashes(char *file)
+{
+	char *pt;
+
+	/* remove references such as "//" */
+	for (pt = file; *pt; /* void */) {
+		while (*pt && (*pt != '/'))
+			pt++;
+		if (*pt == '/') {
+			while (pt[1] == '/')
+				(void) strcpy(pt, pt+1);
+			pt++;
+		}
+	}
+	if ((--pt > file) && (*pt == '/'))
+		*pt = '\0';
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/usr/src/lib/libpkg/common/cfext.h	Fri Jun 05 10:28:40 2009 -0400
@@ -0,0 +1,83 @@
+/*
+ * 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
+ * or http://www.opensolaris.org/os/licensing.
+ * 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 2006 Sun Microsystems, Inc.  All rights reserved.
+ * Use is subject to license terms.
+ */
+
+#ifndef	_CFEXT_H
+#define	_CFEXT_H
+
+
+#ifdef	__cplusplus
+extern "C" {
+#endif
+
+#include	<pkgstrct.h>
+
+struct mergstat {
+	unsigned setuid:1;  /* pkgmap entry has setuid */
+	unsigned setgid:1;  /* ... and/or setgid bit set */
+	unsigned contchg:1; /* contents of the files different */
+	unsigned attrchg:1; /* attributes are different */
+	unsigned shared:1;  /* > 1 pkg associated with this */
+	unsigned osetuid:1; /* installed set[ug]id process ... */
+	unsigned osetgid:1; /* ... being overwritten by pkg. */
+	unsigned rogue:1;   /* conflicting file not owned by a package */
+	unsigned dir2nondir:1;  /* was a directory & now a non-directory */
+	unsigned replace:1; /* merge makes no sense for this object pair */
+	unsigned denied:1;  /* for some reason this was not allowed in */
+	unsigned preloaded:1;   /* already checked in a prior pkg op */
+	unsigned processed:1;   /* already installed or removed */
+	unsigned parentsyml2dir:1;
+	/* parent directory changed from symlink to a directory */
+};
+
+/*
+ * This is information required by pkgadd for fast operation. A
+ * cfextra struct is tagged to each cfent structure requiring
+ * processing. This is how we avoid some unneeded repetition. The
+ * entries incorporating the word 'local' refer to the path that
+ * gets us to the delivered package file. In other words, to install
+ * a file we usually copy from 'local' to 'path' below. In the case
+ * of a link, where no actual copying takes place, local is the source
+ * of the link. Note that environment variables are not evaluated in
+ * the locals unless they are links since the literal path is how
+ * pkgadd finds the entry under the reloc directory.
+ */
+struct cfextra {
+	struct cfent cf_ent;	/* basic contents file entry */
+	struct mergstat mstat;  /* merge status for installs */
+	short   fsys_value; /* fstab[] entry index */
+	short   fsys_base;  /* actual base filesystem in fs_tab[] */
+	char	*client_path;   /* the client-relative path */
+	char	*server_path;   /* the server-relative path */
+	char	*map_path;  /* as read from the pkgmap */
+	char	*client_local;  /* client_relative local */
+	char	*server_local;  /* server relative local */
+};
+
+#ifdef	__cplusplus
+}
+#endif
+
+#endif	/* _CFEXT_H */
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/usr/src/lib/libpkg/common/ckparam.c	Fri Jun 05 10:28:40 2009 -0400
@@ -0,0 +1,196 @@
+/*
+ * 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
+ * or http://www.opensolaris.org/os/licensing.
+ * 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 2009 Sun Microsystems, Inc.  All rights reserved.
+ * Use is subject to license terms.
+ */
+
+/* Copyright (c) 1984, 1986, 1987, 1988, 1989 AT&T */
+/* All Rights Reserved */
+
+
+
+#include <ctype.h>
+#include <string.h>
+#include <sys/types.h>
+#include "pkglib.h"
+#include "pkglibmsgs.h"
+#include "pkglocale.h"
+
+#define	MAXLEN 256
+#define	TOKLEN 16
+
+static int	proc_name(char *param, char *value);
+static int	proc_arch(char *param, char *value);
+static int	proc_version(char *param, char *value);
+static int	proc_category(char *param, char *value);
+static int	bad_first_char(char *param, char *value);
+static int	not_alnum(char *param, char *pt);
+static int	not_ascii(char *param, char *pt);
+static int	too_long(char *param, char *pt, int len);
+static int	isnull(char *param, char *pt);
+
+int
+ckparam(char *param, char *val)
+{
+	char *value = strdup(val);
+	int ret_val = 0;	/* return value */
+
+	if (strcmp(param, "NAME") == 0)
+		ret_val = proc_name(param, value);
+
+	else if (strcmp(param, "ARCH") == 0)
+		ret_val = proc_arch(param, value);
+
+	else if (strcmp(param, "VERSION") == 0)
+		ret_val = proc_version(param, value);
+
+	else if (strcmp(param, "CATEGORY") == 0)
+		ret_val = proc_category(param, value);
+
+	/* param does not match existing parameters */
+	free(value);
+	return (ret_val);
+}
+
+static int
+proc_name(char *param, char *value)
+{
+	int ret_val;
+
+	if (!(ret_val = isnull(param, value))) {
+		ret_val += too_long(param, value, MAXLEN);
+		ret_val += not_ascii(param, value);
+	}
+
+	return (ret_val);
+}
+
+static int
+proc_arch(char *param, char *value)
+{
+	int ret_val;
+	char *token;
+
+	if (!(ret_val = isnull(param, value))) {
+		token = strtok(value, ", ");
+
+		while (token) {
+			ret_val += too_long(param, token, TOKLEN);
+			ret_val += not_ascii(param, token);
+			token = strtok(NULL, ", ");
+		}
+	}
+
+	return (ret_val);
+}
+
+static int
+proc_version(char *param, char *value)
+{
+	int ret_val;
+
+	if (!(ret_val = isnull(param, value))) {
+		ret_val += bad_first_char(param, value);
+		ret_val += too_long(param, value, MAXLEN);
+		ret_val += not_ascii(param, value);
+	}
+
+	return (ret_val);
+}
+
+static int
+proc_category(char *param, char *value)
+{
+	int ret_val;
+	char *token;
+
+	if (!(ret_val = isnull(param, value))) {
+		token = strtok(value, ", ");
+
+		while (token) {
+			ret_val += too_long(param, token, TOKLEN);
+			ret_val += not_alnum(param, token);
+			token = strtok(NULL, ", ");
+		}
+	}
+
+	return (ret_val);
+}
+
+static int
+bad_first_char(char *param, char *value)
+{
+	if (*value == '(') {
+		progerr(pkg_gt(ERR_CHAR), param);
+		return (1);
+	}
+
+	return (0);
+}
+
+static int
+isnull(char *param, char *pt)
+{
+	if (!pt || *pt == '\0') {
+		progerr(pkg_gt(ERR_UNDEF), param);
+		return (1);
+	}
+	return (0);
+}
+
+static int
+too_long(char *param, char *pt, int len)
+{
+	if (strlen(pt) > (size_t)len) {
+		progerr(pkg_gt(ERR_LEN), pt);
+		return (1);
+	}
+	return (0);
+}
+
+static int
+not_ascii(char *param, char *pt)
+{
+	while (*pt) {
+		if (!(isascii(*pt))) {
+			progerr(pkg_gt(ERR_ASCII), param);
+			return (1);
+		}
+		pt++;
+	}
+	return (0);
+}
+
+static int
+not_alnum(char *param, char *pt)
+{
+	while (*pt) {
+		if (!(isalnum(*pt))) {
+			progerr(pkg_gt(ERR_ALNUM), param);
+			return (1);
+		}
+		pt++;
+	}
+
+	return (0);
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/usr/src/lib/libpkg/common/ckvolseq.c	Fri Jun 05 10:28:40 2009 -0400
@@ -0,0 +1,109 @@
+/*
+ * 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
+ * or http://www.opensolaris.org/os/licensing.
+ * 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 2009 Sun Microsystems, Inc.  All rights reserved.
+ * Use is subject to license terms.
+ */
+
+/* Copyright (c) 1984, 1986, 1987, 1988, 1989 AT&T */
+/* All Rights Reserved */
+
+
+
+#include <stdio.h>
+#include <limits.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <sys/types.h>
+#include "pkgstrct.h"
+#include "pkglib.h"
+#include "pkglibmsgs.h"
+#include "pkglocale.h"
+
+#define	PKGMAP	"pkgmap"
+#define	PKGINFO	"pkginfo"
+
+int
+ckvolseq(char *dir, int part, int nparts)
+{
+	static struct cinfo cinfo;
+	char	ftype, path[PATH_MAX];
+
+	if (part > 0) {
+		ftype = 'f';
+		if (part == 1) {
+			/*
+			 * save stats about content information of pkginfo
+			 * file in order to verify multi-volume packages
+			 */
+			cinfo.cksum = cinfo.size = cinfo.modtime = (-1L);
+			(void) snprintf(path, sizeof (path), "%s/pkginfo", dir);
+			if (cverify(0, &ftype, path, &cinfo, 1)) {
+				logerr(pkg_gt(ERR_BADPKGINFO), path);
+				logerr(getErrbufAddr());
+				return (1);
+			}
+			(void) snprintf(path, sizeof (path), "%s/pkgmap", dir);
+			if (access(path, 0)) {
+				logerr(pkg_gt(ERR_NOPKGMAP), path);
+				return (2);
+			}
+		} else {
+			/* temp fix due to summit problem */
+			cinfo.modtime = (-1);
+
+			/* pkginfo file doesn't match first floppy */
+			(void) snprintf(path, sizeof (path), "%s/pkginfo", dir);
+			if (cverify(0, &ftype, path, &cinfo, 1)) {
+				logerr(pkg_gt(MSG_CORRUPT));
+				logerr(getErrbufAddr());
+				return (1);
+			}
+		}
+	} else
+		part = (-part);
+
+	/*
+	 * each volume in a multi-volume package must
+	 * contain either the root.n or reloc.n directories
+	 */
+	if (nparts != 1) {
+		/* look for multi-volume specification */
+		(void) snprintf(path, sizeof (path), "%s/root.%d", dir, part);
+		if (access(path, 0) == 0)
+			return (0);
+		(void) snprintf(path, sizeof (path), "%s/reloc.%d", dir, part);
+		if (access(path, 0) == 0)
+			return (0);
+		if (part == 1) {
+			(void) snprintf(path, sizeof (path), "%s/install",
+								dir, part);
+			if (access(path, 0) == 0)
+				return (0);
+		}
+		if (nparts) {
+			logerr(pkg_gt(MSG_SEQ));
+			return (2);
+		}
+	}
+	return (0);
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/usr/src/lib/libpkg/common/devtype.c	Fri Jun 05 10:28:40 2009 -0400
@@ -0,0 +1,115 @@
+/*
+ * 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
+ * or http://www.opensolaris.org/os/licensing.
+ * 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 2009 Sun Microsystems, Inc.  All rights reserved.
+ * Use is subject to license terms.
+ */
+
+/* Copyright (c) 1984, 1986, 1987, 1988, 1989 AT&T */
+/* All Rights Reserved */
+
+
+
+#include <stdio.h>
+#include <string.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include "pkgdev.h"
+#include "pkglib.h"
+
+extern char	*devattr(char *device, char *attribute); 	/* libadm.a */
+
+int
+devtype(char *alias, struct pkgdev *devp)
+{
+	char *name;
+	devp->mntflg = 0;
+	devp->name = alias;
+	devp->dirname = devp->pathname = devp->mount = NULL;
+	devp->fstyp = devp->cdevice = devp->bdevice = devp->norewind = NULL;
+	devp->rdonly = 0;
+	devp->capacity = 0;
+
+	/* see if alias represents an existing file */
+	if (alias[0] == '/') {
+		if (!isdir(alias)) {
+			devp->dirname = devp->name;
+			return (0);
+		}
+	}
+
+	/* see if alias represents a mountable device (e.g., a floppy) */
+	if ((devp->mount = devattr(alias, "mountpt")) != NULL &&
+	    devp->mount[0] != NULL) {
+		devp->bdevice = devattr(alias, "bdevice");
+		if (!devp->bdevice || !devp->bdevice[0]) {
+			if (devp->bdevice) {
+				free(devp->bdevice);
+				devp->bdevice = NULL;
+			}
+			return (-1);
+		}
+		devp->dirname = devp->mount;
+	} else if (devp->mount) {
+		free(devp->mount);
+		devp->mount = NULL;
+	}
+
+	devp->cdevice = devattr(alias, "cdevice");
+	if (devp->cdevice && devp->cdevice[0])  {
+		/* check for capacity */
+		if (name = devattr(alias, "capacity")) {
+			if (name[0])
+				devp->capacity = atoll(name);
+			free(name);
+		}
+		/* check for norewind device */
+		devp->norewind = devattr(alias, "norewind");
+		if (devp->norewind && !devp->norewind[0]) {
+			free(devp->norewind);
+			devp->norewind = NULL;
+		}
+
+		/* mountable devices will always have associated raw device */
+		return (0);
+	}
+	if (devp->cdevice) {
+		free(devp->cdevice);
+		devp->cdevice = NULL;
+	}
+	/*
+	 * if it is not a raw device, it must be a directory or a regular file
+	 */
+	name = devattr(alias, "pathname");
+	if (!name || !name[0]) {
+		/* Assume a regular file */
+		if (name)
+			free(name);
+		devp->pathname = alias;
+		return (0);
+	}
+	if (!isdir(name))
+		devp->dirname = name;
+	else
+		devp->pathname = name;
+	return (0);
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/usr/src/lib/libpkg/common/dstream.c	Fri Jun 05 10:28:40 2009 -0400
@@ -0,0 +1,1036 @@
+/*
+ * 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
+ * or http://www.opensolaris.org/os/licensing.
+ * 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 2009 Sun Microsystems, Inc.  All rights reserved.
+ * Use is subject to license terms.
+ */
+
+/* Copyright (c) 1984, 1986, 1987, 1988, 1989 AT&T */
+/* All Rights Reserved */
+
+
+
+#include <stdio.h>
+#include <string.h>
+#include <signal.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <sys/types.h>
+#include <sys/param.h>
+#include <sys/sysmacros.h>
+#include <errno.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <sys/statvfs.h>
+#include <fcntl.h>
+#ifdef u3b2
+#include <sys/sys3b.h>
+#endif	/* u3b2 */
+#include <openssl/err.h>
+#include "pkglib.h"
+#include "pkglibmsgs.h"
+#include "pkglocale.h"
+#ifdef u3b2
+static
+struct stat	orig_st_buf; /* Stat structure of original file (3B2/CTC) */
+static char	ds_ctcflg;
+#endif	/* u3b2 */
+
+/* libadm.a */
+extern char	*devattr(char *device, char *attribute);
+extern int	pkgnmchk(register char *pkg, register char *spec,
+				int presvr4flg);
+extern int	getvol(char *device, char *label, int options, char *prompt);
+
+#define	CMDSIZ	512
+#define	LSIZE	128
+#define	DDPROC		"/usr/bin/dd"
+#define	CPIOPROC	"/usr/bin/cpio"
+
+/* device types */
+
+#define	G_TM_TAPE	1   /* Tapemaster controller */
+#define	G_XY_DISK	3   /* xy disks */
+#define	G_SD_DISK	7   /* scsi sd disk */
+#define	G_XT_TAPE	8   /* xt tapes */
+#define	G_SF_FLOPPY	9   /* sf floppy */
+#define	G_XD_DISK	10  /* xd disks */
+#define	G_ST_TAPE	11  /* scsi tape */
+#define	G_NS		12  /* noswap pseudo-dev */
+#define	G_RAM		13  /* ram pseudo-dev */
+#define	G_FT		14  /* tftp */
+#define	G_HD		15  /* 386 network disk */
+#define	G_FD		16  /* 386 AT disk */
+#define	G_FILE		28  /* file, not a device */
+#define	G_NO_DEV	29  /* device does not require special treatment */
+#define	G_DEV_MAX	30  /* last valid device type */
+
+struct dstoc {
+	int	cnt;
+	char	pkg[NON_ABI_NAMELNGTH];
+	int	nparts;
+	long	maxsiz;
+	char    volnos[128];
+	struct dstoc *next;
+} *ds_head, *ds_toc;
+
+#define	ds_nparts	ds_toc->nparts
+#define	ds_maxsiz	ds_toc->maxsiz
+
+int	ds_totread; 	/* total number of parts read */
+int	ds_fd = -1;
+int	ds_curpartcnt = -1;
+
+int	ds_next(char *device, char *instdir);
+int	ds_ginit(char *device);
+int	ds_close(int pkgendflg);
+
+static FILE	*ds_pp;
+static int	ds_realfd = -1; 	/* file descriptor for real device */
+static int	ds_read; 	/* number of parts read for current package */
+static int	ds_volno; 	/* volume number of current volume */
+static int	ds_volcnt; 	/* total number of volumes */
+static char	ds_volnos[128]; 	/* parts/volume info */
+static char	*ds_device;
+static int	ds_volpart;	/* number of parts read in current volume, */
+						/* including skipped parts */
+static int	ds_bufsize;
+static int	ds_skippart; 	/* number of parts skipped in current volume */
+
+static int	ds_getnextvol(char *device);
+static int	ds_skip(char *device, int nskip);
+
+void
+ds_order(char *list[])
+{
+	struct dstoc *toc_pt;
+	register int j, n;
+	char	*pt;
+
+	toc_pt = ds_head;
+	n = 0;
+	while (toc_pt) {
+		for (j = n; list[j]; j++) {
+			if (strcmp(list[j], toc_pt->pkg) == 0) {
+				/* just swap places in the array */
+				pt = list[n];
+				list[n++] = list[j];
+				list[j] = pt;
+			}
+		}
+		toc_pt = toc_pt->next;
+	}
+}
+
+static char *pds_header;
+static char *ds_header;
+static char *ds_header_raw;
+static int ds_headsize;
+
+static char *
+ds_gets(char *buf, int size)
+{
+	int length;
+	char *nextp;
+
+	nextp = strchr(pds_header, '\n');
+	if (nextp == NULL) {
+		length = strlen(pds_header);
+		if (length > size)
+			return (0);
+		if ((ds_header = (char *)realloc(ds_header,
+		    ds_headsize + BLK_SIZE)) == NULL)
+			return (0);
+		if (read(ds_fd, ds_header + ds_headsize, BLK_SIZE) < BLK_SIZE)
+			return (0);
+		ds_headsize += BLK_SIZE;
+		nextp = strchr(pds_header, '\n');
+		if (nextp == NULL)
+			return (0);
+		*nextp = '\0';
+		if (length + (int)strlen(pds_header) > size)
+			return (0);
+		(void) strncpy(buf + length, pds_header, strlen(pds_header));
+		buf[length + strlen(pds_header)] = '\0';
+		pds_header = nextp + 1;
+		return (buf);
+	}
+	*nextp = '\0';
+	if ((int)strlen(pds_header) > size)
+		return (0);
+	(void) strncpy(buf, pds_header, strlen(pds_header));
+	buf[strlen(pds_header)] = '\0';
+	pds_header = nextp + 1;
+	return (buf);
+}
+
+/*
+ * function to determine if media is datastream or mounted
+ * floppy
+ */
+int
+ds_readbuf(char *device)
+{
+	char buf[BLK_SIZE];
+
+	if (ds_fd >= 0)
+		(void) close(ds_fd);
+	if ((ds_fd = open(device, O_RDONLY)) >= 0 &&
+	    read(ds_fd, buf, BLK_SIZE) == BLK_SIZE &&
+	    strncmp(buf, HDR_PREFIX, 20) == 0) {
+		if ((ds_header = (char *)calloc(BLK_SIZE, 1)) == NULL) {
+			progerr(pkg_gt(ERR_UNPACK));
+			logerr(pkg_gt(MSG_MEM));
+			(void) ds_close(0);
+			return (0);
+		}
+		memcpy(ds_header, buf, BLK_SIZE);
+		ds_headsize = BLK_SIZE;
+
+		if (ds_ginit(device) < 0) {
+			progerr(pkg_gt(ERR_UNPACK));
+			logerr(pkg_gt(MSG_OPEN), device, errno);
+			(void) ds_close(0);
+			return (0);
+		}
+		return (1);
+	} else if (ds_fd >= 0) {
+		(void) close(ds_fd);
+		ds_fd = -1;
+	}
+	return (0);
+}
+
+/*
+ * Determine how many additional volumes are needed for current package.
+ * Note: a 0 will occur as first volume number when the package begins
+ * on the next volume.
+ */
+static int
+ds_volsum(struct dstoc *toc)
+{
+	int curpartcnt, volcnt;
+	char volnos[128], tmpvol[128];
+	if (toc->volnos[0]) {
+		int index, sum;
+		sscanf(toc->volnos, "%d %[ 0-9]", &curpartcnt, volnos);
+		volcnt = 0;
+		sum = curpartcnt;
+		while (sum < toc->nparts && sscanf(volnos, "%d %[ 0-9]",
+		    &index, tmpvol) >= 1) {
+			(void) strcpy(volnos, tmpvol);
+			volcnt++;
+			sum += index;
+		}
+		/* side effect - set number of parts read on current volume */
+		ds_volpart = index;
+		return (volcnt);
+	}
+	ds_volpart += toc->nparts;
+	return (0);
+}
+
+/* initialize ds_curpartcnt and ds_volnos */
+static void
+ds_pkginit(void)
+{
+	if (ds_toc->volnos[0])
+		sscanf(ds_toc->volnos, "%d %[ 0-9]", &ds_curpartcnt, ds_volnos);
+	else
+		ds_curpartcnt = -1;
+}
+
+/*
+ * functions to pass current package info to exec'ed program
+ */
+void
+ds_putinfo(char *buf)
+{
+	(void) sprintf(buf, "%d %d %d %d %d %d %d %d %d %d %s",
+	    ds_fd, ds_realfd, ds_volcnt, ds_volno, ds_totread, ds_volpart,
+	    ds_skippart, ds_bufsize, ds_toc->nparts, ds_toc->maxsiz,
+	    ds_toc->volnos);
+}
+
+int
+ds_getinfo(char *string)
+{
+	ds_toc = (struct dstoc *)calloc(1, sizeof (struct dstoc));
+	(void) sscanf(string, "%d %d %d %d %d %d %d %d %d %d %[ 0-9]",
+	    &ds_fd, &ds_realfd, &ds_volcnt, &ds_volno, &ds_totread,
+	    &ds_volpart, &ds_skippart, &ds_bufsize, &ds_toc->nparts,
+	    &ds_toc->maxsiz, ds_toc->volnos);
+	ds_pkginit();
+	return (ds_toc->nparts);
+}
+
+/*
+ * Return true if the file descriptor (ds_fd) is open on the package stream.
+ */
+boolean_t
+ds_fd_open(void)
+{
+	return (ds_fd >= 0 ? B_TRUE : B_FALSE);
+}
+
+/*
+ * Read the source device. Acquire the header data and check it for validity.
+ */
+int
+ds_init(char *device, char **pkg, char *norewind)
+{
+	struct dstoc *tail, *toc_pt;
+	char	*ret;
+	char	cmd[CMDSIZ];
+	char	line[LSIZE+1];
+	int	i, n, count = 0, header_size = BLK_SIZE;
+
+	if (!ds_header) { 	/* If the header hasn't been read yet */
+		if (ds_fd >= 0)
+			(void) ds_close(0);
+
+		/* always start with rewind device */
+		if ((ds_fd = open(device, O_RDONLY)) < 0) {
+			progerr(pkg_gt(ERR_UNPACK));
+			logerr(pkg_gt(MSG_OPEN), device, errno);
+			return (-1);
+		}
+
+		/* allocate room for the header equivalent to a block */
+		if ((ds_header = (char *)calloc(BLK_SIZE, 1)) == NULL) {
+			progerr(pkg_gt(ERR_UNPACK));
+			logerr(pkg_gt(MSG_MEM));
+			return (-1);
+		}
+
+		/* initialize the device */
+		if (ds_ginit(device) < 0) {
+			(void) ds_close(0);
+			progerr(pkg_gt(ERR_UNPACK));
+			logerr(pkg_gt(MSG_OPEN), device, errno);
+			return (-1);
+		}
+
+		/* read a logical block from the source device */
+		if (read(ds_fd, ds_header, BLK_SIZE) != BLK_SIZE) {
+			rpterr();
+			progerr(pkg_gt(ERR_UNPACK));
+			logerr(pkg_gt(MSG_TOC));
+			(void) ds_close(0);
+			return (-1);
+		}
+
+		/*
+		 * This loop scans the medium for the start of the header.
+		 * If the above read worked, we skip this. If it did't, this
+		 * loop will retry the read ten times looking for the header
+		 * marker string.
+		 */
+		while (strncmp(ds_header, HDR_PREFIX, 20) != 0) {
+			/* only ten tries iff the device rewinds */
+			if (!norewind || count++ > 10) {
+				progerr(pkg_gt(ERR_UNPACK));
+				logerr(pkg_gt(MSG_TOC));
+				(void) ds_close(0);
+				return (-1);
+			}
+
+			/* read through to the last block */
+			if (count > 1)
+				while (read(ds_fd, ds_header, BLK_SIZE) > 0)
+					;
+
+			/* then close the device */
+			(void) ds_close(0);
+
+			/* and reopen it */
+			if ((ds_fd = open(norewind, O_RDONLY)) < 0) {
+				progerr(pkg_gt(ERR_UNPACK));
+				logerr(pkg_gt(MSG_OPEN), device, errno);
+				(void) free(ds_header);
+				return (-1);
+			}
+
+			/* initialize the device */
+			if (ds_ginit(device) < 0) {
+				(void) ds_close(0);
+				progerr(pkg_gt(ERR_UNPACK));
+				logerr(pkg_gt(MSG_OPEN), device, errno);
+				return (-1);
+			}
+
+			/* read the block again */
+			if (read(ds_fd, ds_header, BLK_SIZE) != BLK_SIZE) {
+				rpterr();
+				progerr(pkg_gt(ERR_UNPACK));
+				logerr(pkg_gt(MSG_TOC));
+				(void) ds_close(0);
+				return (-1);
+			}
+		}
+
+		/* Now keep scanning until the whole header is in place. */
+		while (strstr(ds_header, HDR_SUFFIX) == NULL) {
+			/* We need a bigger buffer */
+			if ((ds_header = (char *)realloc(ds_header,
+			    header_size + BLK_SIZE)) == NULL) {
+				progerr(pkg_gt(ERR_UNPACK));
+				logerr(pkg_gt(MSG_MEM));
+				(void) ds_close(0);
+				return (1);
+			}
+
+			/* clear the new memory */
+			(void) memset(ds_header + header_size, '\0',
+			    BLK_SIZE);
+
+
+			/* read a logical block from the source device */
+			if (read(ds_fd, ds_header + header_size, BLK_SIZE) !=
+			    BLK_SIZE) {
+				rpterr();
+				progerr(pkg_gt(ERR_UNPACK));
+				logerr(pkg_gt(MSG_TOC));
+				(void) ds_close(0);
+				return (-1);
+			} else
+				header_size += BLK_SIZE;	/* new size */
+		}
+
+		/*
+		 * remember rewind device for ds_close to rewind at
+		 * close
+		 */
+		if (count >= 1)
+			ds_device = device;
+		ds_headsize = header_size;
+
+	}
+
+	pds_header = ds_header;
+
+	/* save raw copy of header for later use in BIO_dump_header */
+	if ((ds_header_raw = (char *)malloc(header_size)) == NULL) {
+		progerr(pkg_gt(ERR_UNPACK));
+		logerr(pkg_gt(MSG_MEM));
+		(void) ds_close(0);
+		return (1);
+	}
+	memcpy(ds_header_raw, ds_header, header_size);
+
+	/* read datastream table of contents */
+	ds_head = tail = (struct dstoc *)0;
+	ds_volcnt = 1;
+
+	while (ret = ds_gets(line, LSIZE)) {
+		if (strcmp(line, HDR_SUFFIX) == 0)
+			break;
+		if (!line[0] || line[0] == '#')
+			continue;
+		toc_pt = (struct dstoc *)calloc(1, sizeof (struct dstoc));
+		if (!toc_pt) {
+			progerr(pkg_gt(ERR_UNPACK));
+			logerr(pkg_gt(MSG_MEM));
+			ecleanup();
+			(void) free(ds_header);
+			return (-1);
+		}
+		if (sscanf(line, "%s %d %d %[ 0-9]", toc_pt->pkg,
+		    &toc_pt->nparts, &toc_pt->maxsiz, toc_pt->volnos) < 3) {
+			progerr(pkg_gt(ERR_UNPACK));
+			logerr(pkg_gt(MSG_TOC));
+			free(toc_pt);
+			(void) free(ds_header);
+			ecleanup();
+			return (-1);
+		}
+		if (tail) {
+			tail->next = toc_pt;
+			tail = toc_pt;
+		} else
+			ds_head = tail = toc_pt;
+		ds_volcnt += ds_volsum(toc_pt);
+	}
+	if (!ret) {
+		progerr(pkg_gt(ERR_UNPACK));
+		logerr(pkg_gt(MSG_TOC));
+		(void) free(ds_header);
+		return (-1);
+	}
+	sighold(SIGINT);
+	sigrelse(SIGINT);
+	if (!ds_head) {
+		progerr(pkg_gt(ERR_UNPACK));
+		logerr(pkg_gt(MSG_EMPTY));
+		(void) free(ds_header);
+		return (-1);
+	}
+	/* this could break, thanks to cpio command limit */
+#ifndef SUNOS41
+	(void) sprintf(cmd, "%s -icdumD -C %d", CPIOPROC, (int)BLK_SIZE);
+#else
+	(void) sprintf(cmd, "%s -icdum -C %d", CPIOPROC, (int)BLK_SIZE);
+#endif
+	n = 0;
+	for (i = 0; pkg[i]; i++) {
+		if (strcmp(pkg[i], "all") == 0)
+			continue;
+		if (n == 0) {
+			strcat(cmd, " ");
+			n = 1;
+		}
+		strlcat(cmd, pkg[i], CMDSIZ);
+		strlcat(cmd, "'/*' ", CMDSIZ);
+
+		/* extract signature too, if present. */
+		strlcat(cmd, SIGNATURE_FILENAME, CMDSIZ);
+		strlcat(cmd, " ", CMDSIZ);
+	}
+
+	/*
+	 * if we are extracting all packages (pkgs == NULL),
+	 * signature will automatically be extracted
+	 */
+	if (n = esystem(cmd, ds_fd, -1)) {
+		rpterr();
+		progerr(pkg_gt(ERR_UNPACK));
+		logerr(pkg_gt(MSG_CMDFAIL), cmd, n);
+		(void) free(ds_header);
+		return (-1);
+	}
+
+	ds_toc = ds_head;
+	ds_totread = 0;
+	ds_volno = 1;
+	return (0);
+}
+
+int
+ds_findpkg(char *device, char *pkg)
+{
+	char	*pkglist[2];
+	int	nskip, ods_volpart;
+
+	if (ds_head == NULL) {
+		pkglist[0] = pkg;
+		pkglist[1] = NULL;
+		if (ds_init(device, pkglist, NULL))
+			return (-1);
+	}
+
+	if (!pkg || pkgnmchk(pkg, "all", 0)) {
+		progerr(pkg_gt(ERR_UNPACK));
+		logerr(pkg_gt(MSG_PKGNAME));
+		return (-1);
+	}
+
+	nskip = 0;
+	ds_volno = 1;
+	ds_volpart = 0;
+	ds_toc = ds_head;
+	while (ds_toc) {
+		if (strcmp(ds_toc->pkg, pkg) == 0)
+			break;
+		nskip += ds_toc->nparts;
+		ds_volno += ds_volsum(ds_toc);
+		ds_toc = ds_toc->next;
+	}
+	if (!ds_toc) {
+		progerr(pkg_gt(ERR_UNPACK));
+		logerr(pkg_gt(MSG_NOPKG), pkg);
+		return (-1);
+	}
+
+	ds_pkginit();
+	ds_skippart = 0;
+	if (ds_curpartcnt > 0) {
+		ods_volpart = ds_volpart;
+		/*
+		 * skip past archives belonging to last package on current
+		 * volume
+		 */
+		if (ds_volpart > 0 && ds_getnextvol(device))
+			return (-1);
+		ds_totread = nskip - ods_volpart;
+		if (ds_skip(device, ods_volpart))
+			return (-1);
+	} else if (ds_curpartcnt < 0) {
+		if (ds_skip(device, nskip - ds_totread))
+			return (-1);
+	} else
+		ds_totread = nskip;
+	ds_read = 0;
+	return (ds_nparts);
+}
+
+/*
+ * Get datastream part
+ * Call for first part should be preceded by
+ * call to ds_findpkg
+ */
+
+int
+ds_getpkg(char *device, int n, char *dstdir)
+{
+	struct statvfs64 svfsb;
+	u_longlong_t free_blocks;
+
+	if (ds_read >= ds_nparts)
+		return (2);
+
+	if (ds_read == n)
+		return (0);
+	else if ((ds_read > n) || (n > ds_nparts))
+		return (2);
+
+	if (ds_maxsiz > 0) {
+		if (statvfs64(".", &svfsb)) {
+			progerr(pkg_gt(ERR_UNPACK));
+			logerr(pkg_gt(MSG_STATFS), errno);
+			return (-1);
+		}
+#ifdef SUNOS41
+		free_blocks = svfsb.f_bfree * howmany(svfsb.f_bsize, DEV_BSIZE);
+#else	/* !SUNOS41 */
+		free_blocks = (((long)svfsb.f_frsize > 0) ?
+			    howmany(svfsb.f_frsize, DEV_BSIZE) :
+			    howmany(svfsb.f_bsize, DEV_BSIZE)) * svfsb.f_bfree;
+#endif	/* SUNOS41 */
+		if ((ds_maxsiz + 50) > free_blocks) {
+			progerr(pkg_gt(ERR_UNPACK));
+			logerr(pkg_gt(MSG_NOSPACE), ds_maxsiz+50, free_blocks);
+			return (-1);
+		}
+	}
+	return (ds_next(device, dstdir));
+}
+
+static int
+ds_getnextvol(char *device)
+{
+	char prompt[128];
+	int n;
+
+	if (ds_close(0))
+		return (-1);
+	(void) sprintf(prompt,
+	    pkg_gt("Insert %%v %d of %d into %%p"),
+	    ds_volno, ds_volcnt);
+	if (n = getvol(device, NULL, NULL, prompt))
+		return (n);
+	if ((ds_fd = open(device, O_RDONLY)) < 0)
+		return (-1);
+	if (ds_ginit(device) < 0) {
+		(void) ds_close(0);
+		return (-1);
+	}
+	ds_volpart = 0;
+	return (0);
+}
+
+/*
+ * called by ds_findpkg to skip past archives for unwanted packages
+ * in current volume
+ */
+static int
+ds_skip(char *device, int nskip)
+{
+	char	cmd[CMDSIZ];
+	int	n, onskip = nskip;
+
+	while (nskip--) {
+		/* skip this one */
+#ifndef SUNOS41
+		(void) sprintf(cmd, "%s -ictD -C %d > /dev/null",
+#else
+		(void) sprintf(cmd, "%s -ict -C %d > /dev/null",
+#endif
+		    CPIOPROC, (int)BLK_SIZE);
+		if (n = esystem(cmd, ds_fd, -1)) {
+			rpterr();
+			progerr(pkg_gt(ERR_UNPACK));
+			logerr(pkg_gt(MSG_CMDFAIL), cmd, n);
+			nskip = onskip;
+			if (ds_volno == 1 || ds_volpart > 0)
+				return (n);
+			if (n = ds_getnextvol(device))
+				return (n);
+		}
+	}
+	ds_totread += onskip;
+	ds_volpart = onskip;
+	ds_skippart = onskip;
+	return (0);
+}
+
+/* skip to end of package if necessary */
+void
+ds_skiptoend(char *device)
+{
+	if (ds_read < ds_nparts && ds_curpartcnt < 0)
+		(void) ds_skip(device, ds_nparts - ds_read);
+}
+
+int
+ds_next(char *device, char *instdir)
+{
+	char	cmd[CMDSIZ], tmpvol[128];
+	int	nparts, n, index;
+
+	/*CONSTCOND*/
+	while (1) {
+		if (ds_read + 1 > ds_curpartcnt && ds_curpartcnt >= 0) {
+			ds_volno++;
+			if (n = ds_getnextvol(device))
+				return (n);
+			(void) sscanf(ds_volnos, "%d %[ 0-9]", &index, tmpvol);
+			(void) strcpy(ds_volnos, tmpvol);
+			ds_curpartcnt += index;
+		}
+#ifndef SUNOS41
+		(void) sprintf(cmd, "%s -icdumD -C %d",
+#else
+		(void) sprintf(cmd, "%s -icdum -C %d",
+#endif
+		    CPIOPROC, (int)BLK_SIZE);
+		if (n = esystem(cmd, ds_fd, -1)) {
+			rpterr();
+			progerr(pkg_gt(ERR_UNPACK));
+			logerr(pkg_gt(MSG_CMDFAIL), cmd, n);
+		}
+		if (ds_read == 0)
+			nparts = 0;
+		else
+			nparts = ds_toc->nparts;
+		if (n || (n = ckvolseq(instdir, ds_read + 1, nparts))) {
+			if (ds_volno == 1 || ds_volpart > ds_skippart)
+				return (-1);
+
+			if (n = ds_getnextvol(device))
+				return (n);
+			continue;
+		}
+		ds_read++;
+		ds_totread++;
+		ds_volpart++;
+
+		return (0);
+	}
+	/*NOTREACHED*/
+}
+
+/*
+ * Name:		BIO_ds_dump
+ * Description:	Dumps all data from the static 'ds_fd' file handle into
+ *		the supplied BIO.
+ *
+ * Arguments:	err - where to record any errors.
+ *		device - Description of device being dumped into,
+ *			for error reporting
+ *		bio - BIO object to dump data into
+ *
+ * Returns :	zero - successfully dumped all data to EOF
+ *		non-zero - some failure occurred.
+ */
+int
+BIO_ds_dump(PKG_ERR *err, char *device, BIO *bio)
+{
+	int	amtread;
+	char	readbuf[BLK_SIZE];
+
+	/*
+	 * note this will read to the end of the device, so it won't
+	 * work for character devices since we don't know when the
+	 * end of the CPIO archive is
+	 */
+	while ((amtread = read(ds_fd, readbuf, BLK_SIZE)) != 0) {
+		if (BIO_write(bio, readbuf, amtread) != amtread) {
+			pkgerr_add(err, PKGERR_WRITE, ERR_WRITE, device,
+			    ERR_error_string(ERR_get_error(), NULL));
+			return (1);
+		}
+	}
+
+	return (0);
+	/*NOTREACHED*/
+}
+
+
+/*
+ * Name:		BIO_ds_dump_header
+ * Description:	Dumps all ds_headsize bytes from the
+ *		static 'ds_header_raw' character array
+ *		to the supplied BIO.
+ *
+ * Arguments:	err - where to record any errors.
+ *		bio - BIO object to dump data into
+ *
+ * Returns :	zero - successfully dumped all raw
+ *		header characters
+ *		non-zero - some failure occurred.
+ */
+int
+BIO_ds_dump_header(PKG_ERR *err, BIO *bio)
+{
+
+	char	zeros[BLK_SIZE];
+
+	memset(zeros, 0, BLK_SIZE);
+
+	if (BIO_write(bio, ds_header_raw, ds_headsize) != ds_headsize) {
+		pkgerr_add(err, PKGERR_WRITE, ERR_WRITE, "bio",
+		    ERR_error_string(ERR_get_error(), NULL));
+		return (1);
+	}
+
+	return (0);
+}
+
+/*
+ * ds_ginit: Determine the device being accessed, set the buffer size,
+ * and perform any device specific initialization.  For the 3B2,
+ * a device with major number of 17 (0x11) is an internal hard disk,
+ * unless the minor number is 128 (0x80) in which case it is an internal
+ * floppy disk.  Otherwise, get the system configuration
+ * table and check it by comparing slot numbers to major numbers.
+ * For the special case of the 3B2 CTC several unusual things must be done.
+ * To enable
+ * streaming mode on the CTC, the file descriptor must be closed, re-opened
+ * (with O_RDWR and O_CTSPECIAL flags set), the STREAMON ioctl(2) command
+ * issued, and the file descriptor re-re-opened either read-only or write_only.
+ */
+
+int
+ds_ginit(char *device)
+{
+#ifdef u3b2
+	major_t maj;
+	minor_t min;
+	int nflag, i, count, size;
+	struct s3bconf *buffer;
+	struct s3bc *table;
+	struct stat st_buf;
+	int devtype;
+	char buf[BLK_SIZE];
+	int fd2, fd;
+#endif	/* u3b2 */
+	int oflag;
+	char *pbufsize, cmd[CMDSIZ];
+	int fd2, fd;
+
+	if ((pbufsize = devattr(device, "bufsize")) != NULL) {
+		ds_bufsize = atoi(pbufsize);
+		(void) free(pbufsize);
+	} else
+		ds_bufsize = BLK_SIZE;
+	oflag = fcntl(ds_fd, F_GETFL, 0);
+#ifdef u3b2
+	devtype = G_NO_DEV;
+	if (fstat(ds_fd, &st_buf) == -1)
+		return (-1);
+	if (!S_ISCHR(st_buf.st_mode) && !S_ISBLK(st_buf.st_mode))
+		goto lab;
+
+	/*
+	 * We'll have to add a remote attribute to stat but this should
+	 * work for now.
+	 */
+	else if (st_buf.st_dev & 0x8000)	/* if remote  rdev */
+		goto lab;
+
+	maj = major(st_buf.st_rdev);
+	min = minor(st_buf.st_rdev);
+	if (maj == 0x11) { /* internal hard or floppy disk */
+		if (min & 0x80)
+			devtype = G_3B2_FD; /* internal floppy disk */
+		else
+			devtype = G_3B2_HD; /* internal hard disk */
+	} else {
+		if (sys3b(S3BCONF, (struct s3bconf *)&count, sizeof (count)) ==
+		    -1)
+			return (-1);
+		size = sizeof (int) + (count * sizeof (struct s3bconf));
+		buffer = (struct s3bconf *)malloc((unsigned)size);
+		if (sys3b(S3BCONF, buffer, size) == -1)
+			return (-1);
+		table = (struct s3bc *)((char *)buffer + sizeof (int));
+		for (i = 0; i < count; i++) {
+			if (maj == (int)table->board) {
+				if (strncmp(table->name, "CTC", 3) == 0) {
+					devtype = G_3B2_CTC;
+					break;
+				} else if (strncmp(table->name, "TAPE", 4)
+						== 0) {
+					devtype = G_TAPE;
+					break;
+				}
+				/* other possible devices can go here */
+			}
+			table++;
+		}
+	}
+	switch (devtype) {
+		case G_3B2_CTC:	/* do special CTC initialization */
+			ds_bufsize = pbufsize ? ds_bufsize : 15872;
+			if (fstat(ds_fd, &orig_st_buf) < 0) {
+				ds_bufsize = -1;
+				break;
+			}
+			nflag = (O_RDWR | O_CTSPECIAL);
+			(void) close(ds_fd);
+			if ((ds_fd = open(device, nflag, 0666)) != -1) {
+				if (ioctl(ds_fd, STREAMON) != -1) {
+					(void) close(ds_fd);
+					nflag = (oflag == O_WRONLY) ?
+					    O_WRONLY : O_RDONLY;
+					if ((ds_fd =
+					    open(device, nflag, 0666)) == -1) {
+						rpterr();
+						progerr(
+						    pkg_gt(ERR_TRANSFER));
+						logerr(pkg_gt(MSG_OPEN),
+						    device, errno);
+						return (-1);
+					}
+					ds_bufsize = 15872;
+				}
+			} else
+				ds_bufsize = -1;
+			if (oflag == O_RDONLY && ds_header && ds_totread == 0)
+				/* Have already read in first block of header */
+				read(ds_fd, buf, BLK_SIZE);
+			ds_ctcflg = 1;
+
+			break;
+		case G_NO_DEV:
+		case G_3B2_HD:
+		case G_3B2_FD:
+		case G_TAPE:
+		case G_SCSI_HD: /* not developed yet */
+		case G_SCSI_FD:
+		case G_SCSI_9T:
+		case G_SCSI_Q24:
+		case G_SCSI_Q120:
+		case G_386_HD:
+		case G_386_FD:
+		case G_386_Q24:
+			ds_bufsize = pbufsize ? ds_bufsize : BLK_SIZE;
+			break;
+		default:
+			ds_bufsize = -1;
+			errno = ENODEV;
+	} /* devtype */
+lab:
+#endif	/* u3b2 */
+	if (ds_bufsize > BLK_SIZE) {
+		if (oflag & O_WRONLY)
+			fd = 1;
+		else
+			fd = 0;
+		fd2 = fcntl(fd, F_DUPFD, fd);
+		(void) close(fd);
+		fcntl(ds_fd, F_DUPFD, fd);
+		if (fd)
+			sprintf(cmd, "%s obs=%d 2>/dev/null", DDPROC,
+			    ds_bufsize);
+		else
+			sprintf(cmd, "%s ibs=%d 2>/dev/null", DDPROC,
+			    ds_bufsize);
+		if ((ds_pp = popen(cmd, fd ? "w" : "r")) == NULL) {
+			progerr(pkg_gt(ERR_TRANSFER));
+			logerr(pkg_gt(MSG_POPEN), cmd, errno);
+			return (-1);
+		}
+		(void) close(fd);
+		fcntl(fd2, F_DUPFD, fd);
+		(void) close(fd2);
+		ds_realfd = ds_fd;
+		ds_fd = fileno(ds_pp);
+	}
+	return (ds_bufsize);
+}
+
+int
+ds_close(int pkgendflg)
+{
+#ifdef u3b2
+	int cnt, mode;
+	char *ptr;
+	struct stat statbuf;
+#endif	/* u3b2 */
+	int n, ret = 0;
+
+#ifdef u3b2
+	if (ds_pp && ds_ctcflg) {
+		ds_ctcflg = 0;
+		if ((mode = fcntl(ds_realfd, F_GETFL, 0)) < 0) {
+			ret = -1;
+		} else if (mode & O_WRONLY) {
+		/*
+		 * pipe to dd write process,
+		 * make sure one more buffer
+		 * gets written out
+		 */
+			if ((ptr = calloc(BLK_SIZE, 1)) == NULL) {
+				ret = -1;
+			/* pad to bufsize */
+			} else {
+				cnt = ds_bufsize;
+				while (cnt > 0) {
+					if ((n = write(ds_fd, ptr,
+					    BLK_SIZE)) < 0) {
+						ret = -1;
+						break;
+					}
+					cnt -= n;
+				}
+				(void) free(ptr);
+			}
+		}
+	}
+#endif
+	if (pkgendflg) {
+		if (ds_header)
+			(void) free(ds_header);
+		ds_header = (char *)NULL;
+		ds_totread = 0;
+	}
+
+	if (ds_pp) {
+		(void) pclose(ds_pp);
+		ds_pp = 0;
+		(void) close(ds_realfd);
+		ds_realfd = -1;
+		ds_fd = -1;
+	} else if (ds_fd >= 0) {
+		(void) close(ds_fd);
+		ds_fd = -1;
+	}
+
+	if (ds_device) {
+		/* rewind device */
+		if ((n = open(ds_device, 0)) >= 0)
+			(void) close(n);
+		ds_device = NULL;
+	}
+	return (ret);
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/usr/src/lib/libpkg/common/fmkdir.c	Fri Jun 05 10:28:40 2009 -0400
@@ -0,0 +1,65 @@
+/*
+ * 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
+ * or http://www.opensolaris.org/os/licensing.
+ * 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 2009 Sun Microsystems, Inc.  All rights reserved.
+ * Use is subject to license terms.
+ */
+
+
+
+#include <stdio.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <limits.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <string.h>
+#include "pkglib.h"
+
+/*
+ * Name:	fmkdir
+ * Description:	force the creation of a directory, even if the current
+ *		node exists and is not a directory
+ * Arguments:	a_path - pointer to string representing the path to the
+ *			directory to create
+ *		a_mode - mode(2) bits to set the path to if created
+ * returns: 0 - directory created
+ *	    1 - could not remove existing non-directory node
+ *	    2 - could not create specified new directory
+ */
+int
+fmkdir(char *a_path, int a_mode)
+{
+	if (access(a_path, F_OK) == 0) {
+		if (rrmdir(a_path) != 0) {
+			return (1);
+		}
+	}
+
+	if (mkdir(a_path, a_mode) != 0) {
+		return (2);
+	}
+
+	return (0);
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/usr/src/lib/libpkg/common/gpkglist.c	Fri Jun 05 10:28:40 2009 -0400
@@ -0,0 +1,359 @@
+/*
+ * 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
+ * or http://www.opensolaris.org/os/licensing.
+ * 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 2009 Sun Microsystems, Inc.  All rights reserved.
+ * Use is subject to license terms.
+ */
+
+/* Copyright (c) 1984, 1986, 1987, 1988, 1989 AT&T */
+/* All Rights Reserved */
+
+
+
+#include <stdio.h>
+#include <ctype.h>
+#include <string.h>
+#include <signal.h>
+#include <errno.h>
+#include <stdlib.h>
+#include <valtools.h>
+#include "pkginfo.h"
+#include "pkglib.h"
+#include "pkglibmsgs.h"
+#include "pkgstrct.h"
+#include "pkglocale.h"
+
+extern char	*pkgdir; 		/* WHERE? */
+
+/* libadm.a */
+extern CKMENU	*allocmenu(char *label, int attr);
+extern int	ckitem(CKMENU *menup, char *item[], short max, char *defstr,
+				char *error, char *help, char *prompt);
+extern int	pkgnmchk(register char *pkg, register char *spec,
+				int presvr4flg);
+extern int	fpkginfo(struct pkginfo *info, char *pkginst);
+extern char	*fpkginst(char *pkg, ...);
+extern int	setinvis(CKMENU *menup, char *choice);
+extern int	setitem(CKMENU *menup, char *choice);
+
+#define	CMDSIZ			512
+#define	LSIZE			256
+#define	MAXSIZE			128
+#define	MALLOCSIZ		128
+#define	MAX_CAT_ARGS	64
+#define	MAX_CAT_LEN		16
+
+static int	cont_in_list = 0;	/* live continuation */
+static char	cont_keyword[PKGSIZ+1];	/* the continuation keyword */
+
+/*
+ * Allocate memory for the next package name. This function attempts the
+ * allocation and if that succeeds, returns a pointer to the new memory
+ * location and increments "n". Otherwise, it returens NULL and n is
+ * unchanged.
+ */
+static char **
+next_n(int *n, char **nwpkg)
+{
+	int loc_n = *n;
+
+	if ((++loc_n % MALLOCSIZ) == 0) {
+		nwpkg = (char **)realloc(nwpkg,
+			(loc_n+MALLOCSIZ) * sizeof (char **));
+		if (nwpkg == NULL) {
+			progerr(pkg_gt(ERR_MEMORY), errno);
+			errno = ENOMEM;
+			return (NULL);
+		}
+	}
+
+	*n = loc_n;
+	return (nwpkg);
+}
+
+/*
+ * This informs gpkglist() to put a keyword at the head of the pkglist. This
+ * was originally intended for live continue, but it may have other
+ * applications as well.
+ */
+void
+pkglist_cont(char *keyword)
+{
+	cont_in_list = 1;
+	(void) strncpy(cont_keyword, keyword, PKGSIZ);
+}
+
+/*
+ * This function constructs the list of packages that the user wants managed.
+ * It may be a list on the command line, it may be some or all of the
+ * packages in a directory or it may be a continuation from a previous
+ * dryrun. It may also be a list of pkgs gathered from the CATEGORY parameter
+ * in a spooled or installed pkginfo file.
+ */
+char **
+gpkglist(char *dir, char **pkg, char **catg)
+{
+	struct _choice_ *chp;
+	struct pkginfo info;
+	char	*inst;
+	CKMENU	*menup;
+	char	temp[LSIZE];
+	char	*savedir, **nwpkg;
+	int	i, n;
+
+	savedir = pkgdir;
+	pkgdir = dir;
+
+	info.pkginst = NULL; /* initialize for memory handling */
+	if (pkginfo(&info, "all", NULL, NULL)) {
+		errno = ENOPKG; /* contains no valid packages */
+		pkgdir = savedir;
+		return (NULL);
+	}
+
+	/*
+	 * If no explicit list was provided and this is not a continuation
+	 * (implying a certain level of direction on the caller's part)
+	 * present a menu of available packages for installation.
+	 */
+	if (pkg[0] == NULL && !cont_in_list) {
+		menup = allocmenu(pkg_gt(HEADER), CKALPHA);
+		if (setinvis(menup, "all")) {
+			errno = EFAULT;
+			return (NULL);
+		}
+		do {
+			/* bug id 1087404 */
+			if (!info.pkginst || !info.name || !info.arch ||
+			    !info.version)
+				continue;
+			(void) sprintf(temp, "%s %s\n(%s) %s", info.pkginst,
+				info.name, info.arch, info.version);
+			if (setitem(menup, temp)) {
+				errno = EFAULT;
+				return (NULL);
+			}
+		} while (pkginfo(&info, "all", NULL, NULL) == 0);
+		/* clear memory usage by pkginfo */
+		(void) pkginfo(&info, NULL, NULL, NULL);
+		pkgdir = savedir; 	/* restore pkgdir to orig value */
+
+		nwpkg = (char **)calloc(MALLOCSIZ, sizeof (char **));
+		n = ckitem(menup, nwpkg, MALLOCSIZ, "all", NULL,
+		    pkg_gt(HELP), pkg_gt(PROMPT));
+		if (n) {
+			free(nwpkg);
+			errno = ((n == 3) ? EINTR : EFAULT);
+			pkgdir = savedir;
+			return (NULL);
+		}
+		if (strcmp(nwpkg[0], "all") == 0) {
+			chp = menup->choice;
+			for (n = 0; chp; /* void */) {
+				nwpkg[n] = strdup(chp->token);
+				nwpkg = next_n(&n, nwpkg);
+				chp = chp->next;
+				nwpkg[n] = NULL;
+			}
+		} else {
+			for (n = 0; nwpkg[n]; n++)
+				nwpkg[n] = strdup(nwpkg[n]);
+		}
+		(void) setitem(menup, NULL); /* free resources */
+		free(menup);
+		pkgdir = savedir;
+		return (nwpkg);
+	}
+
+	/* clear memory usage by pkginfo */
+	(void) pkginfo(&info, NULL, NULL, NULL);
+
+	nwpkg = (char **)calloc(MALLOCSIZ, sizeof (char **));
+
+	/*
+	 * pkg array contains the instance identifiers to
+	 * be selected, or possibly wildcard definitions
+	 */
+	i = n = 0;
+	do {
+		if (cont_in_list) {	/* This is a live continuation. */
+			nwpkg[n] = strdup(cont_keyword);
+			nwpkg = next_n(&n, nwpkg);
+			nwpkg[n] = NULL;
+			cont_in_list = 0;	/* handled */
+
+			if (pkg[0] == NULL) {	/* It's just a continuation. */
+				break;
+			}
+		} else if (pkgnmchk(pkg[i], "all", 1)) {
+			/* wildcard specification */
+			(void) fpkginst(NULL);
+			inst = fpkginst(pkg[i], NULL, NULL);
+			if (inst == NULL) {
+				progerr(pkg_gt(ERR_NOPKG), pkg[i]);
+				free(nwpkg);
+				nwpkg = NULL;
+				errno = ESRCH;
+				break;
+			}
+			do {
+				if (catg != NULL) {
+					pkginfo(&info, inst, NULL, NULL);
+					if (!is_same_CATEGORY(catg,
+							info.catg))
+						continue;
+				}
+				nwpkg[n] = strdup(inst);
+				nwpkg = next_n(&n, nwpkg);
+				nwpkg[n] = NULL;
+			} while (inst = fpkginst(pkg[i], NULL, NULL));
+		} else {
+			if (fpkginfo(&info, pkg[i])) {
+				progerr(pkg_gt(ERR_NOPKG), pkg[i]);
+				free(nwpkg);
+				nwpkg = NULL;
+				errno = ESRCH;
+				break;
+			}
+			nwpkg[n] = strdup(pkg[i]);
+			nwpkg = next_n(&n, nwpkg);
+			nwpkg[n] = NULL;
+		}
+	} while (pkg[++i]);
+
+	(void) fpkginst(NULL);
+	(void) fpkginfo(&info, NULL);
+	pkgdir = savedir; 	/* restore pkgdir to orig value */
+
+	if (catg != NULL) {
+		if (nwpkg[0] == NULL) {
+
+			/*
+			 * No pkgs in the spooled directory matched the
+			 * category specified by the user.
+			 */
+
+			free(nwpkg);
+			return (NULL);
+		}
+	}
+	return (nwpkg);
+}
+
+/*
+ * Check category passed in on the command line to see if it is valid.
+ *
+ * returns 0 if the category is valid
+ * returns 1 if the category is invalid
+ */
+
+int
+is_not_valid_category(char **category, char *progname)
+{
+	if (strcasecmp(progname, "pkgrm") == 0) {
+		if (is_same_CATEGORY(category, "system"))
+			return (1);
+	}
+
+	return (0);
+}
+
+/*
+ * Check category length
+ *
+ * returns 0 if the category length is valid
+ * returns 1 if a category has length > 16 chars as defined by the SVr4 ABI
+ */
+
+int
+is_not_valid_length(char **category)
+{
+	int i;
+
+	for (i = 0; category[i] != NULL; i++) {
+		if (strlen(category[i]) > MAX_CAT_LEN)
+			return (1);
+	}
+
+	return (0);
+}
+
+/*
+ * Check category passed in on the command line against the CATEGORY in the
+ * spooled or installed packages pkginfo file.
+ *
+ * returns 0 if categories match
+ * returns 1 if categories don't match
+ */
+
+int
+is_same_CATEGORY(char **category, char *persistent_category)
+{
+	int i, j, n = 0;
+	char *pers_catg, **pers_catgs;
+
+	pers_catg = strdup(persistent_category);
+
+	pers_catgs = (char **)calloc(MAX_CAT_LEN, sizeof (char **));
+
+	pers_catgs[n++] = strtok(pers_catg, " \t\n, ");
+	while (pers_catgs[n] = strtok(NULL, " \t\n, "))
+		n++;
+
+	for (i = 0; category[i] != NULL; i++) {
+		for (j = 0; j < n; j++) {
+			if (strcasecmp(category[i], pers_catgs[j]) == 0) {
+				return (1);
+			}
+		}
+	}
+
+	return (0);
+}
+
+/*
+ * Given a string of categories, construct a null-terminated array of
+ * categories.
+ *
+ * returns the array of categories or NULL
+ */
+
+char **
+get_categories(char *catg_arg)
+{
+	int n = 0;
+	char *tmp_catg;
+	char **catgs;
+
+	tmp_catg = strdup(catg_arg);
+
+	catgs = (char **)calloc(MAX_CAT_LEN, sizeof (char **));
+
+	catgs[n++] = strtok(tmp_catg, " \t\n, ");
+	while (catgs[n] = strtok(NULL, " \t\n, "))
+		n++;
+
+	if (*catgs == NULL)
+		return (NULL);
+	else
+		return (catgs);
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/usr/src/lib/libpkg/common/gpkgmap.c	Fri Jun 05 10:28:40 2009 -0400
@@ -0,0 +1,1275 @@
+/*
+ * 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
+ * or http://www.opensolaris.org/os/licensing.
+ * 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 2009 Sun Microsystems, Inc.  All rights reserved.
+ * Use is subject to license terms.
+ */
+
+/* Copyright (c) 1984, 1986, 1987, 1988, 1989 AT&T */
+/* All Rights Reserved */
+
+
+
+#include <stdio.h>
+#include <limits.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <string.h>
+#include <ctype.h>
+#include <fcntl.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <errno.h>
+#include "pkgstrct.h"
+#include "pkglib.h"
+#include "pkglibmsgs.h"
+#include "pkglocale.h"
+
+#define	ERR_CANT_READ_LCLPATH		"unable to read local pathname"
+#define	ERR_BAD_VOLUME_NUMBER		"bad volume number"
+#define	ERR_CANNOT_READ_PATHNAME_FIELD	"unable to read pathname field"
+#define	ERR_CANNOT_READ_CONTENT_INFO	"unable to read content info"
+#define	ERR_EXTRA_TOKENS_PRESENT	"extra tokens on input line"
+#define	ERR_CANNOT_READ_CLASS_TOKEN	"unable to read class token"
+#define	ERR_BAD_LINK_SPEC		"missing or invalid link specification"
+#define	ERR_UNKNOWN_FTYPE		"unknown ftype"
+#define	ERR_NO_LINKSOURCE		"no link source specified"
+#define	ERR_CANNOT_READ_MM_DEVNUMS	"unable to read major/minor "\
+					"device numbers"
+static int	eatwhite(FILE *fp);
+static int	getend(FILE *fp);
+static int	getstr(FILE *fp, char *sep, int n, char *str);
+static int	getnum(FILE *fp, int base, long *d, long bad);
+static int	getlnum(FILE *fp, int base, fsblkcnt_t *d, long bad);
+static int	getvalmode(FILE *fp, mode_t *d, long bad, int map);
+
+static int	getendvfp(char **cp);
+static void	findendvfp(char **cp);
+static int	getstrvfp(char **cp, char *sep, int n, char *str);
+static int	getvalmodevfp(char **cp, mode_t *d, long bad, int map);
+int		getnumvfp(char **cp, int base, long *d, long bad);
+int		getlnumvfp(char **cp, int base, fsblkcnt_t *d, long bad);
+
+static char	mypath[PATH_MAX];
+static char	mylocal[PATH_MAX];
+static int	mapmode = MAPNONE;
+static char	*maptype = "";
+static mode_t	d_mode = BADMODE;
+static char 	*d_owner = BADOWNER;
+static char	*d_group = BADGROUP;
+
+/*
+ * These determine how gpkgmap() deals with mode, owner and group defaults.
+ * It is assumed that the owner and group arguments represent static fields
+ * which will persist until attrdefault() is called.
+ */
+void
+attrpreset(int mode, char *owner, char *group)
+{
+	d_mode = mode;
+	d_owner = owner;
+	d_group = group;
+}
+
+void
+attrdefault()
+{
+	d_mode = NOMODE;
+	d_owner = NOOWNER;
+	d_group = NOGROUP;
+}
+
+/*
+ * This determines how gpkgmap() deals with environment variables in the
+ * mode, owner and group. Path is evaluated at a higher level based upon
+ * other circumstances.
+ */
+void
+setmapmode(int mode)
+{
+	if (mode >= 0 || mode <= 3) {
+		mapmode = mode;
+		if (mode == MAPBUILD)
+			maptype = " build";
+		else if (mode == MAPINSTALL)
+			maptype = " install";
+		else
+			maptype = "";
+	}
+}
+
+/* This is the external query interface for mapmode. */
+int
+getmapmode(void)
+{
+	return (mapmode);
+}
+
+/*
+ * Unpack the pkgmap or the contents file or whatever file is in that format.
+ * Based upon mapmode, environment parameters will be resolved for mode,
+ * owner and group.
+ */
+
+int
+gpkgmap(struct cfent *ept, FILE *fp)
+{
+	int		c;
+	boolean_t	first_char = B_TRUE;
+
+	setErrstr(NULL);
+	ept->volno = 0;
+	ept->ftype = BADFTYPE;
+	(void) strcpy(ept->pkg_class, BADCLASS);
+	ept->pkg_class_idx = -1;
+	ept->path = NULL;
+	ept->ainfo.local = NULL;
+	/* default attributes were supplied, so don't reset */
+	ept->ainfo.mode = d_mode;
+	(void) strcpy(ept->ainfo.owner, d_owner);
+	(void) strcpy(ept->ainfo.group, d_group);
+#ifdef SUNOS41
+	ept->ainfo.xmajor = BADMAJOR;
+	ept->ainfo.xminor = BADMINOR;
+#else
+	ept->ainfo.major = BADMAJOR;
+	ept->ainfo.minor = BADMINOR;
+#endif
+	ept->cinfo.cksum = ept->cinfo.modtime = ept->cinfo.size = (-1L);
+
+	ept->npkgs = 0;
+
+	if (!fp)
+		return (-1);
+readline:
+	c = eatwhite(fp);
+
+	/*
+	 * If the first character is not a digit, we assume that the
+	 * volume number is 1.
+	 */
+	if (first_char && !isdigit(c)) {
+		ept->volno = 1;
+	}
+	first_char = B_FALSE;
+
+	switch (c) {
+	    case EOF:
+		return (0);
+
+	    case '0':
+	    case '1':
+	    case '2':
+	    case '3':
+	    case '4':
+	    case '5':
+	    case '6':
+	    case '7':
+	    case '8':
+	    case '9':
+		if (ept->volno) {
+			setErrstr(pkg_gt(ERR_BAD_VOLUME_NUMBER));
+			goto error;
+		}
+		do {
+			ept->volno = (ept->volno*10)+c-'0';
+			c = getc(fp);
+		} while (isdigit(c));
+		if (ept->volno == 0)
+			ept->volno = 1;
+
+		goto readline;
+
+	    case ':':
+	    case '#':
+		(void) getend(fp);
+		/*FALLTHRU*/
+	    case '\n':
+		/*
+		 * Since we are going to scan the next line,
+		 * we need to reset volume number and first_char.
+		 */
+		ept->volno = 0;
+		first_char = B_TRUE;
+		goto readline;
+
+	    case 'i':
+		ept->ftype = (char)c;
+		c = eatwhite(fp);
+		/*FALLTHRU*/
+	    case '.':
+	    case '/':
+		(void) ungetc(c, fp);
+
+		if (getstr(fp, "=", PATH_MAX, mypath)) {
+			setErrstr(pkg_gt(ERR_CANNOT_READ_PATHNAME_FIELD));
+			goto error;
+		}
+		ept->path = mypath;
+		c = getc(fp);
+		if (c == '=') {
+			if (getstr(fp, NULL, PATH_MAX, mylocal)) {
+				setErrstr(pkg_gt(ERR_CANT_READ_LCLPATH));
+				goto error;
+			}
+			ept->ainfo.local = mylocal;
+		} else
+			(void) ungetc(c, fp);
+
+		if (ept->ftype == 'i') {
+			/* content info might exist */
+			if (!getlnum(fp, 10, (fsblkcnt_t *)&ept->cinfo.size,
+			    BADCONT) &&
+			    (getnum(fp, 10, (long *)&ept->cinfo.cksum,
+			    BADCONT) ||
+			    getnum(fp, 10, (long *)&ept->cinfo.modtime,
+			    BADCONT))) {
+				setErrstr(pkg_gt(ERR_CANNOT_READ_CONTENT_INFO));
+				goto error;
+			}
+		}
+		if (getend(fp)) {
+			setErrstr(pkg_gt(ERR_EXTRA_TOKENS_PRESENT));
+			return (-1);
+		}
+		return (1);
+
+	    case '?':
+	    case 'f':
+	    case 'v':
+	    case 'e':
+	    case 'l':
+	    case 's':
+	    case 'p':
+	    case 'c':
+	    case 'b':
+	    case 'd':
+	    case 'x':
+		ept->ftype = (char)c;
+		if (getstr(fp, NULL, CLSSIZ, ept->pkg_class)) {
+			setErrstr(pkg_gt(ERR_CANNOT_READ_CLASS_TOKEN));
+			goto error;
+		}
+		if (getstr(fp, "=", PATH_MAX, mypath)) {
+			setErrstr(pkg_gt(ERR_CANNOT_READ_PATHNAME_FIELD));
+			goto error;
+		}
+		ept->path = mypath;
+
+		c = getc(fp);
+		if (c == '=') {
+			/* local path */
+			if (getstr(fp, NULL, PATH_MAX, mylocal)) {
+				if (ept->ftype == 's' || ept->ftype == 'l') {
+					setErrstr(pkg_gt(ERR_READLINK));
+				} else {
+					setErrstr(
+						pkg_gt(ERR_CANT_READ_LCLPATH));
+				}
+				goto error;
+			}
+			ept->ainfo.local = mylocal;
+		} else if (strchr("sl", ept->ftype)) {
+			if ((c != EOF) && (c != '\n'))
+				(void) getend(fp);
+			setErrstr(pkg_gt(ERR_BAD_LINK_SPEC));
+			return (-1);
+		} else
+			(void) ungetc(c, fp);
+		break;
+
+	    default:
+		setErrstr(pkg_gt(ERR_UNKNOWN_FTYPE));
+error:
+		(void) getend(fp);
+		return (-1);
+	}
+
+	if (strchr("sl", ept->ftype) && (ept->ainfo.local == NULL)) {
+		setErrstr(pkg_gt(ERR_NO_LINKSOURCE));
+		goto error;
+	}
+
+	if (strchr("cb", ept->ftype)) {
+#ifdef SUNOS41
+		ept->ainfo.xmajor = BADMAJOR;
+		ept->ainfo.xminor = BADMINOR;
+		if (getnum(fp, 10, (long *)&ept->ainfo.xmajor, BADMAJOR) ||
+		    getnum(fp, 10, (long *)&ept->ainfo.xminor, BADMINOR))
+#else
+		ept->ainfo.major = BADMAJOR;
+		ept->ainfo.minor = BADMINOR;
+		if (getnum(fp, 10, (long *)&ept->ainfo.major, BADMAJOR) ||
+		    getnum(fp, 10, (long *)&ept->ainfo.minor, BADMINOR))
+#endif
+		{
+			setErrstr(pkg_gt(ERR_CANNOT_READ_MM_DEVNUMS));
+			goto error;
+		}
+	}
+
+	/*
+	 * Links and information files don't have attributes associated with
+	 * them. The following either resolves potential variables or passes
+	 * them through. Mode is tested for validity to some degree. BAD???
+	 * is returned to indicate that no meaningful mode was provided. A
+	 * higher authority will decide if that's OK or not. CUR??? means that
+	 * the prototype file specifically requires a wildcard ('?') for
+	 * that entry. We issue an error if attributes were entered wrong.
+	 * We just return BAD??? if there was no entry at all.
+	 */
+	if (strchr("cbdxpfve", ept->ftype)) {
+		int retval;
+
+		if ((retval = getvalmode(fp, &(ept->ainfo.mode), CURMODE,
+		    (mapmode != MAPNONE))) == 1)
+			goto end;	/* nothing else on the line */
+		else if (retval == 2)
+			goto error;	/* mode is too no good */
+
+		/* owner & group should be here */
+		if ((retval = getstr(fp, NULL, ATRSIZ,
+		    ept->ainfo.owner)) == 1)
+			goto end;	/* no owner or group - warning */
+		if (retval == -1) {
+			setErrstr(pkg_gt(ERR_OWNTOOLONG));
+			goto error;
+		}
+
+		if ((retval = getstr(fp, NULL, ATRSIZ,
+		    ept->ainfo.group)) == 1)
+			goto end;	/* no group - warning */
+		if (retval == -1) {
+			setErrstr(pkg_gt(ERR_GRPTOOLONG));
+			goto error;
+		}
+
+		/* Resolve the parameters if required. */
+		if (mapmode != MAPNONE) {
+			if (mapvar(mapmode, ept->ainfo.owner)) {
+				(void) snprintf(getErrbufAddr(),
+					getErrbufSize(),
+					pkg_gt(ERR_NOVAR),
+					maptype, ept->ainfo.owner);
+				setErrstr(getErrbufAddr());
+				goto error;
+			}
+			if (mapvar(mapmode, ept->ainfo.group)) {
+				(void) snprintf(getErrbufAddr(),
+					getErrbufSize(), pkg_gt(ERR_NOVAR),
+					maptype, ept->ainfo.group);
+				setErrstr(getErrbufAddr());
+				goto error;
+			}
+		}
+	}
+
+	if (strchr("ifve", ept->ftype)) {
+		/* look for content description */
+		if (!getlnum(fp, 10, (fsblkcnt_t *)&ept->cinfo.size, BADCONT) &&
+		(getnum(fp, 10, (long *)&ept->cinfo.cksum, BADCONT) ||
+		getnum(fp, 10, (long *)&ept->cinfo.modtime, BADCONT))) {
+			setErrstr(pkg_gt(ERR_CANNOT_READ_CONTENT_INFO));
+			goto error;
+		}
+	}
+
+	if (ept->ftype == 'i')
+		goto end;
+
+end:
+	if (getend(fp) && ept->pinfo) {
+		setErrstr(pkg_gt(ERR_EXTRA_TOKENS_PRESENT));
+		return (-1);
+	}
+
+done:
+	return (1);
+}
+
+/*
+ * Get and validate the mode attribute. This returns an error if
+ *	1. the mode string is too long
+ *	2. the mode string includes alpha characters
+ *	3. the mode string is not octal
+ *	4. mode string is an install parameter
+ *	5. mode is an unresolved build parameter and MAPBUILD is
+ *	   in effect.
+ * If the mode is a build parameter, it is
+ *	1. returned as is if MAPNONE is in effect
+ *	2. evaluated if MAPBUILD is in effect
+ *
+ * NOTE : We use "mapmode!=MAPBUILD" to gather that it is install
+ * time. At install time we just fix a mode with bad bits set by
+ * setting it to CURMODE. This should be an error in a few releases
+ * (2.8 maybe) but faulty modes are so common in existing packages
+ * that this is a reasonable exception. -- JST 1994-11-9
+ *
+ * RETURNS
+ *	0 if mode is being returned as a valid value
+ *	1 if no attributes are present on the line
+ *	2 if there was a fundamental error
+ */
+static int
+getvalmode(FILE *fp, mode_t *d, long bad, int map)
+{
+	char tempmode[20];
+	mode_t tempmode_t;
+	int retval;
+
+	if ((retval = getstr(fp, NULL, ATRSIZ, tempmode)) == 1)
+		return (1);
+	else if (retval == -1) {
+		setErrstr(pkg_gt(ERR_MODELONG));
+		return (2);
+	} else {
+		/*
+		 * If it isn't a '?' (meaning go with whatever mode is
+		 * there), validate the mode and convert it to a mode_t. The
+		 * "bad" variable here is a misnomer. It doesn't necessarily
+		 * mean bad.
+		 */
+		if (tempmode[0] == '?') {
+			*d = WILDCARD;
+		} else {
+			/*
+			 * Mode may not be an install parameter or a
+			 * non-build parameter.
+			 */
+			if (tempmode[0] == '$' &&
+			    (isupper(tempmode[1]) || !islower(tempmode[1]))) {
+				setErrstr(pkg_gt(ERR_IMODE));
+				return (2);
+			}
+
+			if ((map) && (mapvar(mapmode, tempmode))) {
+				(void) snprintf(getErrbufAddr(),
+						getErrbufSize(),
+						pkg_gt(ERR_NOVAR),
+						maptype, tempmode);
+				setErrstr(getErrbufAddr());
+				return (2);
+			}
+
+
+			if (tempmode[0] == '$') {
+				*d = BADMODE;	/* may be a problem */
+			} else {
+				/*
+				 * At this point it's supposed to be
+				 * something we can convert to a number.
+				 */
+				int n = 0;
+
+				/*
+				 * We reject it if it contains nonnumbers or
+				 * it's not octal.
+				 */
+				while (tempmode[n] && !isspace(tempmode[n])) {
+					if (!isdigit(tempmode[n])) {
+						setErrstr(
+							pkg_gt(ERR_MODEALPHA));
+						return (2);
+					}
+
+					if (strchr("89abcdefABCDEF",
+					    tempmode[n])) {
+						setErrstr(
+							pkg_gt(ERR_BASEINVAL));
+						return (2);
+					}
+					n++;
+				}
+
+				tempmode_t = strtol(tempmode, NULL, 8);
+
+				/*
+				 * We reject it if it contains inappropriate
+				 * bits.
+				 */
+				if (tempmode_t & ~(S_IAMB |
+				    S_ISUID | S_ISGID | S_ISVTX)) {
+					if (mapmode != MAPBUILD) {
+						tempmode_t = bad;
+					} else {
+						setErrstr(pkg_gt(ERR_MODEBITS));
+						return (2);
+					}
+				}
+				*d = tempmode_t;
+			}
+		}
+		return (0);
+	}
+}
+
+static int
+getnum(FILE *fp, int base, long *d, long bad)
+{
+	int c, b;
+
+	/* leading white space ignored */
+	c = eatwhite(fp);
+	if (c == '?') {
+		*d = bad;
+		return (0);
+	}
+
+	if ((c == EOF) || (c == '\n') || !isdigit(c)) {
+		(void) ungetc(c, fp);
+		return (1);
+	}
+
+	*d = 0;
+	while (isdigit(c)) {
+		b = (c & 017);
+		if (b >= base)
+			return (2);
+		*d = (*d * base) + b;
+		c = getc(fp);
+	}
+	(void) ungetc(c, fp);
+	return (0);
+}
+
+static int
+getlnum(FILE *fp, int base, fsblkcnt_t *d, long bad)
+{
+	int c, b;
+
+	/* leading white space ignored */
+	c = eatwhite(fp);
+	if (c == '?') {
+		*d = bad;
+		return (0);
+	}
+
+	if ((c == EOF) || (c == '\n') || !isdigit(c)) {
+		(void) ungetc(c, fp);
+		return (1);
+	}
+
+	*d = 0;
+	while (isdigit(c)) {
+		b = (c & 017);
+		if (b >= base)
+			return (2);
+		*d = (*d * base) + b;
+		c = getc(fp);
+	}
+	(void) ungetc(c, fp);
+	return (0);
+}
+
+/*
+ *  Get a string from the file. Returns
+ *	0 if all OK
+ *	1 if nothing there
+ *	-1 if string is too long
+ */
+static int
+getstr(FILE *fp, char *sep, int n, char *str)
+{
+	int c;
+
+	/* leading white space ignored */
+	c = eatwhite(fp);
+	if ((c == EOF) || (c == '\n')) {
+		(void) ungetc(c, fp);
+		return (1); /* nothing there */
+	}
+
+	/* fill up string until space, tab, or separator */
+	while (!strchr(" \t", c) && (!sep || !strchr(sep, c))) {
+		if (n-- < 1) {
+			*str = '\0';
+			return (-1); /* too long */
+		}
+		*str++ = (char)c;
+		c = getc(fp);
+		if ((c == EOF) || (c == '\n'))
+			break; /* no more on this line */
+	}
+	*str = '\0';
+	(void) ungetc(c, fp);
+
+	return (0);
+}
+
+static int
+getend(FILE *fp)
+{
+	int c;
+	int n;
+
+	n = 0;
+	do {
+		if ((c = getc(fp)) == EOF)
+			return (n);
+		if (!isspace(c))
+			n++;
+	} while (c != '\n');
+	return (n);
+}
+
+static int
+eatwhite(FILE *fp)
+{
+	int c;
+
+	/* this test works around a side effect of getc() */
+	if (feof(fp))
+		return (EOF);
+	do
+		c = getc(fp);
+	while ((c == ' ') || (c == '\t'));
+	return (c);
+}
+
+int
+gpkgmapvfp(struct cfent *ept, VFP_T *vfp)
+{
+	int		c;
+	boolean_t	first_char = B_TRUE;
+	(void) strlcpy(ept->pkg_class, BADCLASS, sizeof (ept->pkg_class));
+	(void) strlcpy(ept->ainfo.owner, d_owner, sizeof (ept->ainfo.owner));
+	(void) strlcpy(ept->ainfo.group, d_group, sizeof (ept->ainfo.group));
+
+	setErrstr(NULL);
+	ept->volno = 0;
+	ept->ftype = BADFTYPE;
+	ept->pkg_class_idx = -1;
+	ept->path = NULL;
+	ept->ainfo.local = NULL;
+	ept->ainfo.mode = d_mode;
+	ept->ainfo.major = BADMAJOR;
+	ept->ainfo.minor = BADMINOR;
+	ept->cinfo.cksum = (-1L);
+	ept->cinfo.modtime = (-1L);
+	ept->cinfo.size = (-1L);
+
+	ept->npkgs = 0;
+
+	/* return error if no vfp specified */
+
+	if (vfp == (VFP_T *)NULL) {
+		return (-1);
+	}
+
+readline:
+	while (((c = vfpGetcNoInc(vfp)) != '\0') && (isspace(vfpGetc(vfp))))
+		;
+
+	/*
+	 * If the first character is not a digit, we assume that the
+	 * volume number is 1.
+	 */
+	if (first_char && !isdigit(c)) {
+		ept->volno = 1;
+	}
+	first_char = B_FALSE;
+
+	/*
+	 * In case of hsfs the zero-padding of partial pages
+	 * returned by mmap is not done properly. A separate bug has been filed
+	 * on this.
+	 */
+
+	if (vfp->_vfpCurr && (vfp->_vfpCurr > vfp->_vfpEnd)) {
+		return (0);
+	}
+
+	switch (c) {
+	    case '\0':
+		return (0);
+
+	    case '0':
+	    case '1':
+	    case '2':
+	    case '3':
+	    case '4':
+	    case '5':
+	    case '6':
+	    case '7':
+	    case '8':
+	    case '9':
+		if (ept->volno) {
+			setErrstr(pkg_gt(ERR_BAD_VOLUME_NUMBER));
+			goto error;
+		}
+		do {
+			ept->volno = (ept->volno*10)+c-'0';
+			c = vfpGetc(vfp);
+		} while (isdigit(c));
+		if (ept->volno == 0) {
+			ept->volno = 1;
+		}
+
+		goto readline;
+
+	    case ':':
+	    case '#':
+		(void) findendvfp(&vfpGetCurrCharPtr(vfp));
+		/*FALLTHRU*/
+	    case '\n':
+		/*
+		 * Since we are going to scan the next line,
+		 * we need to reset volume number and first_char.
+		 */
+		ept->volno = 0;
+		first_char = B_TRUE;
+		goto readline;
+
+	    case 'i':
+		ept->ftype = (char)c;
+		while (((c = vfpGetcNoInc(vfp)) != '\0') &&
+						(isspace(vfpGetc(vfp))))
+			;
+		/*FALLTHRU*/
+	    case '.':
+	    case '/':
+		vfpDecCurrPtr(vfp);
+
+		if (getstrvfp(&vfpGetCurrCharPtr(vfp), "=", PATH_MAX, mypath)) {
+			setErrstr(pkg_gt(ERR_CANNOT_READ_PATHNAME_FIELD));
+			goto error;
+		}
+		ept->path = mypath;
+		c = vfpGetc(vfp);
+		if (c == '=') {
+			if (getstrvfp(&vfpGetCurrCharPtr(vfp), NULL, PATH_MAX,
+							mylocal)) {
+				setErrstr(pkg_gt(ERR_CANT_READ_LCLPATH));
+				goto error;
+			}
+			ept->ainfo.local = mylocal;
+		} else {
+			vfpDecCurrPtr(vfp);
+		}
+
+		if (ept->ftype == 'i') {
+			/* content info might exist */
+			if (!getlnumvfp(&vfpGetCurrCharPtr(vfp), 10,
+				(fsblkcnt_t *)&ept->cinfo.size, BADCONT) &&
+			    (getnumvfp(&vfpGetCurrCharPtr(vfp), 10,
+				(long *)&ept->cinfo.cksum, BADCONT) ||
+			    getnumvfp(&vfpGetCurrCharPtr(vfp), 10,
+				(long *)&ept->cinfo.modtime, BADCONT))) {
+				setErrstr(pkg_gt(ERR_CANNOT_READ_CONTENT_INFO));
+				goto error;
+			}
+		}
+
+		if (getendvfp(&vfpGetCurrCharPtr(vfp))) {
+			setErrstr(pkg_gt(ERR_EXTRA_TOKENS_PRESENT));
+			return (-1);
+		}
+		return (1);
+
+	    case '?':
+	    case 'f':
+	    case 'v':
+	    case 'e':
+	    case 'l':
+	    case 's':
+	    case 'p':
+	    case 'c':
+	    case 'b':
+	    case 'd':
+	    case 'x':
+		ept->ftype = (char)c;
+		if (getstrvfp(&vfpGetCurrCharPtr(vfp), NULL,
+						CLSSIZ, ept->pkg_class)) {
+			setErrstr(pkg_gt(ERR_CANNOT_READ_CLASS_TOKEN));
+			goto error;
+		}
+		if (getstrvfp(&vfpGetCurrCharPtr(vfp), "=", PATH_MAX, mypath)) {
+			setErrstr(pkg_gt(ERR_CANNOT_READ_PATHNAME_FIELD));
+			goto error;
+		}
+		ept->path = mypath;
+
+		c = vfpGetc(vfp);
+		if (c == '=') {
+			/* local path */
+			if (getstrvfp(&vfpGetCurrCharPtr(vfp), NULL,
+							PATH_MAX, mylocal)) {
+				if (ept->ftype == 's' || ept->ftype == 'l') {
+					setErrstr(pkg_gt(ERR_READLINK));
+				} else {
+					setErrstr(
+						pkg_gt(ERR_CANT_READ_LCLPATH));
+				}
+				goto error;
+			}
+			ept->ainfo.local = mylocal;
+		} else if ((ept->ftype == 's') || (ept->ftype == 'l')) {
+			if ((c != '\0') && (c != '\n'))
+				(void) findendvfp(&vfpGetCurrCharPtr(vfp));
+			setErrstr(pkg_gt(ERR_BAD_LINK_SPEC));
+			return (-1);
+		} else {
+			vfpDecCurrPtr(vfp);
+		}
+		break;
+
+	    default:
+		setErrstr(pkg_gt(ERR_UNKNOWN_FTYPE));
+error:
+		(void) findendvfp(&vfpGetCurrCharPtr(vfp));
+		return (-1);
+	}
+
+	if (((ept->ftype == 's') || (ept->ftype == 'l')) &&
+					(ept->ainfo.local == NULL)) {
+		setErrstr(pkg_gt(ERR_NO_LINKSOURCE));
+		goto error;
+	}
+
+	if (((ept->ftype == 'c') || (ept->ftype == 'b'))) {
+		ept->ainfo.major = BADMAJOR;
+		ept->ainfo.minor = BADMINOR;
+
+		if (getnumvfp(&vfpGetCurrCharPtr(vfp), 10,
+				(long *)&ept->ainfo.major, BADMAJOR) ||
+		    getnumvfp(&vfpGetCurrCharPtr(vfp), 10,
+				(long *)&ept->ainfo.minor, BADMINOR)) {
+			setErrstr(pkg_gt(ERR_CANNOT_READ_MM_DEVNUMS));
+			goto error;
+		}
+	}
+
+	/*
+	 * Links and information files don't have attributes associated with
+	 * them. The following either resolves potential variables or passes
+	 * them through. Mode is tested for validity to some degree. BAD???
+	 * is returned to indicate that no meaningful mode was provided. A
+	 * higher authority will decide if that's OK or not. CUR??? means that
+	 * the prototype file specifically requires a wildcard ('?') for
+	 * that entry. We issue an error if attributes were entered wrong.
+	 * We just return BAD??? if there was no entry at all.
+	 */
+	if ((ept->ftype == 'd') || (ept->ftype == 'x') || (ept->ftype == 'c') ||
+		(ept->ftype == 'b') || (ept->ftype == 'p') ||
+		(ept->ftype == 'f') || (ept->ftype == 'v') ||
+		(ept->ftype == 'e')) {
+		int retval;
+
+		retval = getvalmodevfp(&vfpGetCurrCharPtr(vfp),
+				&(ept->ainfo.mode),
+				CURMODE, (mapmode != MAPNONE));
+
+		if (retval == 1) {
+			goto end;	/* nothing else on the line */
+		} else if (retval == 2) {
+			goto error;	/* mode is too no good */
+		}
+
+		/* owner & group should be here */
+		if ((retval = getstrvfp(&vfpGetCurrCharPtr(vfp), NULL, ATRSIZ,
+		    ept->ainfo.owner)) == 1)
+			goto end;	/* no owner or group - warning */
+		if (retval == -1) {
+			setErrstr(pkg_gt(ERR_OWNTOOLONG));
+			goto error;
+		}
+
+		if ((retval = getstrvfp(&vfpGetCurrCharPtr(vfp), NULL, ATRSIZ,
+		    ept->ainfo.group)) == 1)
+			goto end;	/* no group - warning */
+		if (retval == -1) {
+			setErrstr(pkg_gt(ERR_GRPTOOLONG));
+			goto error;
+		}
+
+		/* Resolve the parameters if required. */
+		if (mapmode != MAPNONE) {
+			if (mapvar(mapmode, ept->ainfo.owner)) {
+				(void) snprintf(getErrbufAddr(),
+					getErrbufSize(), pkg_gt(ERR_NOVAR),
+					maptype, ept->ainfo.owner);
+				setErrstr(getErrbufAddr());
+				goto error;
+			}
+			if (mapvar(mapmode, ept->ainfo.group)) {
+				(void) snprintf(getErrbufAddr(),
+					getErrbufSize(), pkg_gt(ERR_NOVAR),
+					maptype, ept->ainfo.group);
+				setErrstr(getErrbufAddr());
+				goto error;
+			}
+		}
+	}
+
+	if ((ept->ftype == 'i') || (ept->ftype == 'f') ||
+			(ept->ftype == 'v') || (ept->ftype == 'e')) {
+		/* look for content description */
+		if (!getlnumvfp(&vfpGetCurrCharPtr(vfp), 10,
+				(fsblkcnt_t *)&ept->cinfo.size, BADCONT) &&
+		(getnumvfp(&vfpGetCurrCharPtr(vfp), 10,
+				(long *)&ept->cinfo.cksum, BADCONT) ||
+		getnumvfp(&vfpGetCurrCharPtr(vfp), 10,
+				(long *)&ept->cinfo.modtime, BADCONT))) {
+			setErrstr(pkg_gt(ERR_CANNOT_READ_CONTENT_INFO));
+			goto error;
+		}
+	}
+
+	if (ept->ftype == 'i')
+		goto end;
+
+end:
+	if (getendvfp(&vfpGetCurrCharPtr(vfp)) && ept->pinfo) {
+		setErrstr(pkg_gt(ERR_EXTRA_TOKENS_PRESENT));
+		return (-1);
+	}
+
+done:
+	return (1);
+}
+
+/*
+ * Get and validate the mode attribute. This returns an error if
+ *	1. the mode string is too long
+ *	2. the mode string includes alpha characters
+ *	3. the mode string is not octal
+ *	4. mode string is an install parameter
+ *	5. mode is an unresolved build parameter and MAPBUILD is
+ *	   in effect.
+ * If the mode is a build parameter, it is
+ *	1. returned as is if MAPNONE is in effect
+ *	2. evaluated if MAPBUILD is in effect
+ *
+ * NOTE : We use "mapmode!=MAPBUILD" to gather that it is install
+ * time. At install time we just fix a mode with bad bits set by
+ * setting it to CURMODE. This should be an error in a few releases
+ * (2.8 maybe) but faulty modes are so common in existing packages
+ * that this is a reasonable exception. -- JST 1994-11-9
+ *
+ * RETURNS
+ *	0 if mode is being returned as a valid value
+ *	1 if no attributes are present on the line
+ *	2 if there was a fundamental error
+ */
+static int
+getvalmodevfp(char **cp, mode_t *d, long bad, int map)
+{
+	char	tempmode[ATRSIZ+1];
+	mode_t	tempmode_t;
+	int	retval;
+	int	n;
+
+	if ((retval = getstrvfp(cp, NULL, sizeof (tempmode), tempmode)) == 1) {
+		return (1);
+	} else if (retval == -1) {
+		setErrstr(pkg_gt(ERR_MODELONG));
+		return (2);
+	}
+
+	/*
+	 * If it isn't a '?' (meaning go with whatever mode is
+	 * there), validate the mode and convert it to a mode_t. The
+	 * "bad" variable here is a misnomer. It doesn't necessarily
+	 * mean bad.
+	 */
+	if (tempmode[0] == '?') {
+		*d = WILDCARD;
+		return (0);
+	}
+
+	/*
+	 * Mode may not be an install parameter or a
+	 * non-build parameter.
+	 */
+
+	if (tempmode[0] == '$' &&
+	    (isupper(tempmode[1]) || !islower(tempmode[1]))) {
+		setErrstr(pkg_gt(ERR_IMODE));
+		return (2);
+	}
+
+	if ((map) && (mapvar(mapmode, tempmode))) {
+		(void) snprintf(getErrbufAddr(), getErrbufSize(),
+				pkg_gt(ERR_NOVAR), maptype, tempmode);
+		setErrstr(getErrbufAddr());
+		return (2);
+	}
+
+	if (tempmode[0] == '$') {
+		*d = BADMODE;	/* may be a problem */
+		return (0);
+	}
+
+	/* it's supposed to be something we can convert to a number */
+
+	n = 0;
+
+	/* reject it if it contains nonnumbers or it's not octal */
+
+	while (tempmode[n] && !isspace(tempmode[n])) {
+		if (!isdigit(tempmode[n])) {
+			setErrstr(pkg_gt(ERR_MODEALPHA));
+			return (2);
+		}
+
+		if (strchr("89abcdefABCDEF", tempmode[n])) {
+			setErrstr(pkg_gt(ERR_BASEINVAL));
+			return (2);
+		}
+		n++;
+	}
+
+	tempmode_t = strtol(tempmode, NULL, 8);
+
+	/*
+	 * We reject it if it contains inappropriate
+	 * bits.
+	 */
+	if (tempmode_t & (~(S_IAMB | S_ISUID | S_ISGID | S_ISVTX))) {
+		if (mapmode == MAPBUILD) {
+			setErrstr(pkg_gt(ERR_MODEBITS));
+			return (2);
+		}
+		tempmode_t = bad;
+	}
+
+	*d = tempmode_t;
+
+	return (0);
+}
+
+int
+getnumvfp(char **cp, int base, long *d, long bad)
+{
+	int c;
+	char	*p = *cp;
+
+	if (*p == '\0') {
+		return (0);
+	}
+
+	/* leading white space ignored */
+	while (((c = *p) != '\0') && (isspace(*p++)))
+		;
+	if (c == '?') {
+		*d = bad;
+		*cp = p;
+		return (0);
+	}
+
+	if ((c == '\0') || (c == '\n') || !isdigit(c)) {
+		p--;
+		*cp = p;
+		return (1);
+	}
+
+	*d = 0;
+	while (isdigit(c)) {
+		*d = (*d * base) + (c & 017);
+		c = *p++;
+	}
+	p--;
+	*cp = p;
+	return (0);
+}
+
+int
+getlnumvfp(char **cp, int base, fsblkcnt_t *d, long bad)
+{
+	int c;
+	char	*p = *cp;
+
+	if (*p == '\0') {
+		return (0);
+	}
+
+	/* leading white space ignored */
+	while (((c = *p) != '\0') && (isspace(*p++)))
+		;
+	if (c == '?') {
+		*d = bad;
+		*cp = p;
+		return (0);
+	}
+
+	if ((c == '\0') || (c == '\n') || !isdigit(c)) {
+		p--;
+		*cp = p;
+		return (1);
+	}
+
+	*d = 0;
+	while (isdigit(c)) {
+		*d = (*d * base) + (c & 017);
+		c = *p++;
+	}
+	p--;
+	*cp = p;
+	return (0);
+}
+
+static int
+getstrvfp(char **cp, char *sep, int n, char *str)
+{
+	char	delims[256];
+	int	c;
+	char	*p = *cp;
+	char	*p1;
+	size_t	len;
+
+	if (*p == '\0') {
+		return (1);
+	}
+
+	/* leading white space ignored */
+
+	while (((c = *p) != '\0') && (isspace(*p++)))
+		;
+	if ((c == '\0') || (c == '\n')) {
+		p--;
+		*cp = p;
+		return (1); /* nothing there */
+	}
+
+	p--;
+
+	/* generate complete list of delimiters to scan for */
+
+	(void) strlcpy(delims, " \t\n", sizeof (delims));
+	if ((sep != (char *)NULL) && (*sep != '\0')) {
+		(void) strlcat(delims, sep, sizeof (delims));
+	}
+
+	/* compute length based on delimiter found or not */
+
+	p1 = strpbrk(p, delims);
+	if (p1 == (char *)NULL) {
+		len = strlen(p);
+	} else {
+		len = (ptrdiff_t)p1 - (ptrdiff_t)p;
+	}
+
+	/* if string will fit in result buffer copy string and return success */
+
+	if (len < n) {
+		(void) memcpy(str, p, len);
+		str[len] = '\0';
+		p += len;
+		*cp = p;
+		return (0);
+	}
+
+	/* result buffer too small; copy partial string, return error */
+	(void) memcpy(str, p, n-1);
+	str[n-1] = '\0';
+	p += n;
+	*cp = p;
+	return (-1);
+}
+
+/*
+ * Name:	getendvfp
+ * Description:	Locate the end of the current line given a pointer into a buffer
+ *		containing characters that is null terminated.
+ * Arguments:	char **cp - pointer to pointer to null-terminated string buffer
+ * Returns:	int == 0 -- no non-space characters preceeded the newline
+ *		    != 0 -- one or more non-space characters preceeded newline
+ * Effects:	cp is updated to point to the first character PAST the first new
+ *		line character found. If no newline character is found, cp is
+ *		updated to point to the '\0' at the end of the buffer.
+ */
+
+static int
+getendvfp(char **cp)
+{
+	int	n;
+	char	*p = *cp;
+
+	n = 0;
+
+	/* if at end of buffer return no more characters left */
+
+	if (*p == '\0') {
+		return (0);
+	}
+
+	/* find the first null or end of line character */
+
+	while ((*p != '\0') && (*p != '\n')) {
+		if (n == 0) {
+			if (!isspace(*p)) {
+				n++;
+			}
+		}
+		p++;
+	}
+
+	/* if at newline, increment pointer to first character past newline */
+
+	if (*p == '\n') {
+		p++;
+	}
+
+	/* set return pointer to null or first character past newline */
+
+	*cp = p;
+
+	/* return space/nospace indicator */
+
+	return (n);
+}
+
+/*
+ * Name:	findendvfp
+ * Description:	Locate the end of the current line given a pointer into a buffer
+ *		containing characters that is null terminated.
+ * Arguments:	char **cp - pointer to pointer to null-terminated string buffer
+ * Returns:	none
+ * Effects:	cp is updated to point to the first character PAST the first new
+ *		line character found. If no newline character is found, cp is
+ *		updated to point to the '\0' at the end of the buffer.
+ */
+
+static void
+findendvfp(char **cp)
+{
+	char	*p1;
+	char	*p = *cp;
+
+	/* if at end of buffer return no more characters left */
+
+	if (*p == '\0') {
+		return;
+	}
+
+	/* find the end of the line */
+
+	p1 = strchr(p, '\n');
+	if (p1 != (char *)NULL) {
+		*cp = ++p1;
+		return;
+	}
+
+	/* no newline found - point to null terminator */
+
+	*cp = strchr(p, '\0');
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/usr/src/lib/libpkg/common/handlelocalfs.c	Fri Jun 05 10:28:40 2009 -0400
@@ -0,0 +1,150 @@
+/*
+ *
+ * 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
+ * or http://www.opensolaris.org/os/licensing.
+ * 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 2009 Sun Microsystems, Inc.  All rights reserved.
+ * Use is subject to license terms.
+ *
+*/
+
+
+#include <stdlib.h>
+#include <unistd.h>
+#include <strings.h>
+#include <libscf.h>
+
+#define	MAX_TRY	15
+static boolean_t fs_temporarily_enabled = B_FALSE;
+char svm_core_svcs[] = "system/filesystem/local:default";
+
+/*
+ * Name:	enable_local_fs
+ * Description: If the SMF service system/filesystem/local:default is not
+ *		enabled, then this function enables the service, so that,
+ *		all the local filesystems are mounted.
+ * Arguments:	None
+ * Returns:	B_TRUE on success; B_FALSE on error.
+ */
+boolean_t
+enable_local_fs(void)
+{
+	char *cur_smf_state;
+	int i;
+	boolean_t fs_enabled_here = B_FALSE;
+
+	if (fs_temporarily_enabled)  {
+		return (B_TRUE);
+	}
+
+	if ((cur_smf_state = smf_get_state(svm_core_svcs)) != NULL) {
+		if (strcmp(cur_smf_state, SCF_STATE_STRING_DISABLED) == 0) {
+			if (smf_enable_instance(svm_core_svcs, SMF_TEMPORARY)
+				!= 0) {
+				free(cur_smf_state);
+				return (B_FALSE);
+			}
+
+			fs_enabled_here = B_TRUE;
+
+		} else if (strcmp(cur_smf_state, SCF_STATE_STRING_ONLINE)
+				== 0) {
+			free(cur_smf_state);
+			return (B_TRUE);
+		} else if (strcmp(cur_smf_state, SCF_STATE_STRING_OFFLINE)
+				!= 0) {
+			free(cur_smf_state);
+			return (B_FALSE);
+		}
+
+		free(cur_smf_state);
+
+	} else {
+		return (B_FALSE);
+	}
+
+	for (i = 0; i < MAX_TRY; i++) {
+		if ((cur_smf_state = smf_get_state(svm_core_svcs)) != NULL) {
+			if (strcmp(cur_smf_state, SCF_STATE_STRING_ONLINE)
+				== 0) {
+				free(cur_smf_state);
+				if (fs_enabled_here) {
+					fs_temporarily_enabled = B_TRUE;
+				}
+				return (B_TRUE);
+			} else if ((strcmp(cur_smf_state,
+				SCF_STATE_STRING_OFFLINE) == 0) ||
+		(strcmp(cur_smf_state, SCF_STATE_STRING_DISABLED) == 0)) {
+				(void) sleep(1);
+				free(cur_smf_state);
+			} else {
+				free(cur_smf_state);
+				return (B_FALSE);
+			}
+		} else {
+			return (B_FALSE);
+		}
+	}
+
+	return (B_FALSE);
+}
+
+/*
+ * Name:	restore_local_fs
+ * Description: If the SMF service system/filesystem/local:default was
+ *		enabled using enable_local_fs(), then this function disables
+ *		the service.
+ * Arguments:	None
+ * Returns:	B_TRUE on success; B_FALSE on error.
+ */
+boolean_t
+restore_local_fs(void)
+{
+	int i;
+	char *cur_smf_state;
+
+	if (!fs_temporarily_enabled) {
+		return (B_TRUE);
+	}
+
+	if (smf_disable_instance(svm_core_svcs, SMF_TEMPORARY) != 0) {
+		return (B_FALSE);
+	}
+
+	for (i = 0; i < MAX_TRY; i++) {
+		if ((cur_smf_state = smf_get_state(svm_core_svcs)) != NULL) {
+			if (strcmp(cur_smf_state, SCF_STATE_STRING_DISABLED)
+				== 0) {
+				fs_temporarily_enabled = B_FALSE;
+				free(cur_smf_state);
+				break;
+			}
+			(void) sleep(1);
+
+			free(cur_smf_state);
+		} else {
+			return (B_FALSE);
+		}
+	}
+
+	return (!fs_temporarily_enabled);
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/usr/src/lib/libpkg/common/isdir.c	Fri Jun 05 10:28:40 2009 -0400
@@ -0,0 +1,391 @@
+/*
+ * 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
+ * or http://www.opensolaris.org/os/licensing.
+ * 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 2004 Sun Microsystems, Inc.  All rights reserved.
+ * Use is subject to license terms.
+ */
+
+/* Copyright (c) 1984, 1986, 1987, 1988, 1989 AT&T */
+/* All Rights Reserved */
+
+
+#include <stdio.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <archives.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <limits.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <string.h>
+#include "pkglocale.h"
+#include "pkglibmsgs.h"
+
+/*
+ * Defines for cpio/compression checks.
+ */
+#define	BIT_MASK		0x1f
+#define	BLOCK_MASK		0x80
+
+#define	MASK_CK(x, y)	(((x) & (y)) == (y))
+#define	ISCOMPCPIO	((unsigned char) cm.c_mag[0] == m_h[0] && \
+			(unsigned char) cm.c_mag[1] == m_h[1] && \
+			(MASK_CK((unsigned char) cm.c_mag[2], BLOCK_MASK) || \
+			MASK_CK((unsigned char) cm.c_mag[2], BIT_MASK)))
+
+#define	ISCPIO		(cm.b_mag != CMN_BIN && \
+			(strcmp(cm.c_mag, CMS_ASC) == 0) && \
+			(strcmp(cm.c_mag, CMS_CHR) == 0) && \
+			(strcmp(cm.c_mag, CMS_CRC) == 0))
+
+/* location of distributed file system types database */
+
+#define	REMOTE_FS_DBFILE	"/etc/dfs/fstypes"
+
+/* character array used to hold dfs types database contents */
+
+static long		numRemoteFstypes = -1;
+static char		**remoteFstypes = (char **)NULL;
+
+/* forward declarations */
+
+static void _InitRemoteFstypes(void);
+
+int isFdRemote(int a_fd);
+int isPathRemote(char *a_path);
+int isFstypeRemote(char *a_fstype);
+int isdir(char *path);
+int isfile(char *dir, char *file);
+int iscpio(char *path, int *iscomp);
+
+/*
+ * Name:	isdir
+ * Description:	determine if specified path exists and is a directory
+ * Arguments:	path - pointer to string representing the path to verify
+ * returns: 0 - directory exists
+ *	    1 - directory does not exist or is not a directory
+ * NOTE:	errno is set appropriately
+ */
+
+int
+isdir(char *path)
+{
+	struct stat statbuf;
+
+	/* return error if path does not exist */
+
+	if (stat(path, &statbuf) != 0) {
+		return (1);
+	}
+
+	/* return error if path is not a directory */
+
+	if ((statbuf.st_mode & S_IFMT) != S_IFDIR) {
+		errno = ENOTDIR;
+		return (1);
+	}
+
+	return (0);
+}
+
+/*
+ * Name:	isfile
+ * Description:	determine if specified path exists and is a directory
+ * Arguments:	dir - pointer to string representing the directory where
+ *			the file is located
+ *			== NULL - use "file" argument only
+ *		file - pointer to string representing the file to verify
+ * Returns:	0 - success - file exists
+ *		1 - failure - file does not exist OR is not a file
+ * NOTE:	errno is set appropriately
+ */
+
+int
+isfile(char *dir, char *file)
+{
+	struct stat statbuf;
+	char	path[PATH_MAX];
+
+	/* construct full path if directory specified */
+
+	if (dir) {
+		(void) snprintf(path, sizeof (path), "%s/%s", dir, file);
+		file = path;
+	}
+
+	/* return error if path does not exist */
+
+	if (stat(file, &statbuf) != 0) {
+		return (1);
+	}
+
+	/* return error if path is a directory */
+
+	if ((statbuf.st_mode & S_IFMT) == S_IFDIR) {
+		errno = EISDIR;
+		return (1);
+	}
+
+	/* return error if path is not a file */
+
+	if ((statbuf.st_mode & S_IFMT) != S_IFREG) {
+		errno = EINVAL;
+		return (1);
+	}
+
+	return (0);
+}
+
+int
+iscpio(char *path, int *iscomp)
+{
+	/*
+	 * Compressed File Header.
+	 */
+	unsigned char m_h[] = { "\037\235" };		/* 1F 9D */
+
+	static union {
+		short int	b_mag;
+		char		c_mag[CMS_LEN];
+	}	cm;
+
+	struct stat	statb;
+	int		fd;
+
+
+	*iscomp = 0;
+
+	if ((fd = open(path, O_RDONLY, 0)) == -1) {
+		if (errno != ENOENT) {
+			perror("");
+			(void) fprintf(stderr, pkg_gt(ERR_ISCPIO_OPEN), path);
+		}
+		return (0);
+	} else {
+		if (fstat(fd, &statb) == -1) {
+			perror("");
+			(void) fprintf(stderr, pkg_gt(ERR_ISCPIO_FSTAT), path);
+			(void) close(fd);
+			return (0);
+		} else {
+			if (S_ISREG(statb.st_mode)) {	/* Must be a file */
+				if (read(fd, cm.c_mag, sizeof (cm.c_mag)) !=
+				    sizeof (cm.c_mag)) {
+					perror("");
+					(void) fprintf(stderr,
+					    pkg_gt(ERR_ISCPIO_READ), path);
+					(void) close(fd);
+					return (0);
+				}
+				/*
+				 * Try to determine if the file is a compressed
+				 * file, if that fails, try to determine if it
+				 * is a cpio archive, if that fails, then we
+				 * fail!
+				 */
+				if (ISCOMPCPIO) {
+					*iscomp = 1;
+					(void) close(fd);
+					return (1);
+				} else if (ISCPIO) {
+					(void) fprintf(stderr,
+					    pkg_gt(ERR_ISCPIO_NOCPIO),
+					    path);
+					(void) close(fd);
+					return (0);
+				}
+				(void) close(fd);
+				return (1);
+			} else {
+				(void) close(fd);
+				return (0);
+			}
+		}
+	}
+}
+
+/*
+ * Name:	isPathRemote
+ * Description:	determine if a path object is local or remote
+ * Arguments:	a_path - [RO, *RO] - (char *)
+ *			Pointer to string representing the path to check
+ * Returns:	int
+ *			1 - the path is remote
+ *			0 - the path is local to this system
+ *			-1 - cannot determine if path is remote or local
+ */
+
+int
+isPathRemote(char *a_path)
+{
+	int		r;
+	struct stat	statbuf;
+
+	r = lstat(a_path, &statbuf);
+	if (r < 0) {
+		return (-1);
+	}
+
+	return (isFstypeRemote(statbuf.st_fstype));
+}
+
+/*
+ * Name:	isFdRemote
+ * Description:	determine if an open file is local or remote
+ * Arguments:	a_fd - [RO, *RO] - (int)
+ *			Integer representing open file to check
+ * Returns:	int
+ *			1 - the path is remote
+ *			0 - the path is local to this system
+ *			-1 - cannot determine if path is remote or local
+ */
+
+int
+isFdRemote(int a_fd)
+{
+	int		r;
+	struct stat	statbuf;
+
+	r = fstat(a_fd, &statbuf);
+	if (r < 0) {
+		return (-1);
+	}
+
+	return (isFstypeRemote(statbuf.st_fstype));
+}
+
+/*
+ * Name:	isFstypeRemote
+ * Description:	determine if a file system type is remote (distributed)
+ * Arguments:	a_fstype - [RO, *RO] - (char *)
+ *			Pointer to string representing the file system type
+ *			to check
+ * Returns:	int
+ *			1 - the file system type is remote
+ *			0 - the file system type is local to this system
+ */
+
+int
+isFstypeRemote(char *a_fstype)
+{
+	int	i;
+
+	/* initialize the list if it is not yet initialized */
+
+	_InitRemoteFstypes();
+
+	/* scan the list looking for the specified type */
+
+	for (i = 0; i < numRemoteFstypes; i++) {
+		if (strcmp(remoteFstypes[i], a_fstype) == 0) {
+			return (1);
+		}
+	}
+
+	/* type not found in remote file system type list - is not remote */
+
+	return (0);
+}
+
+/*
+ * Name:	_InitRemoteFstypes
+ * Description:	initialize table of remote file system type names
+ * Arguments:	none
+ * Returns:	none
+ * Side Effects:
+ *	- The global array "(char **)remoteFstypes" is set to the
+ *	  address of an array of string pointers, each of which represents
+ *	  a single remote file system type
+ *	- The global variable "(long) numRemoteFstypes" is set to the total
+ *	  number of remote file system type strings (names) that are
+ *	  contained in the "remoteFstypes" global array.
+ *	- numRemoteFstypes is initialized to "-1" before any attempt has been
+ *	  made to read the remote file system type name database.
+ */
+static void
+_InitRemoteFstypes(void)
+{
+	FILE    *fp;
+	char    line_buf[LINE_MAX];
+
+	/* return if already initialized */
+
+	if (numRemoteFstypes > 0) {
+		return;
+	}
+
+	/* if list is uninitialized, start with zero */
+
+	if (numRemoteFstypes == -1) {
+		numRemoteFstypes = 0;
+	}
+
+	/* open the remote file system type database file */
+
+	if ((fp = fopen(REMOTE_FS_DBFILE, "r")) == NULL) {
+		/* no remote type database: use predefined remote types */
+		remoteFstypes = (char **)realloc(remoteFstypes,
+					sizeof (char *) * (numRemoteFstypes+3));
+		remoteFstypes[numRemoteFstypes++] = "nfs";	/* +1 */
+		remoteFstypes[numRemoteFstypes++] = "autofs";	/* +2 */
+		remoteFstypes[numRemoteFstypes++] = "cachefs";	/* +3 */
+		return;
+	}
+
+	/*
+	 * Read the remote file system type database; from fstypes(4):
+	 *
+	 * fstypes resides in directory /etc/dfs and lists distributed file
+	 * system utilities packages installed on the system. For each installed
+	 * distributed file system type, there is a line that begins with the
+	 * file system type name (for example, ``nfs''), followed by white space
+	 * and descriptive text.
+	 *
+	 * Lines will look at lot like this:
+	 *
+	 *	nfs NFS Utilities
+	 *	autofs AUTOFS Utilities
+	 *	cachefs CACHEFS Utilities
+	 */
+
+	while (fgets(line_buf, sizeof (line_buf), fp) != NULL) {
+		char		buf[LINE_MAX];
+		static char	format[128] = {'\0'};
+
+		if (format[0] == '\0') {
+			/* create bounded format: %ns */
+			(void) snprintf(format, sizeof (format),
+				"%%%ds", sizeof (buf)-1);
+		}
+
+		(void) sscanf(line_buf, format, buf);
+
+		remoteFstypes = realloc(remoteFstypes,
+					sizeof (char *) * (numRemoteFstypes+1));
+		remoteFstypes[numRemoteFstypes++] = strdup(buf);
+	}
+
+	/* close database file and return */
+
+	(void) fclose(fp);
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/usr/src/lib/libpkg/common/keystore.c	Fri Jun 05 10:28:40 2009 -0400
@@ -0,0 +1,2474 @@
+/*
+ * 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
+ * or http://www.opensolaris.org/os/licensing.
+ * 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 2006 Sun Microsystems, Inc.  All rights reserved.
+ * Use is subject to license terms.
+ */
+
+/*
+ * Module:	keystore.c
+ * Description:	This module contains the structure definitions for processing
+ *		package keystore files.
+ */
+
+#include <errno.h>
+#include <fcntl.h>
+#include <unistd.h>
+#include <strings.h>
+#include <libintl.h>
+#include <time.h>
+#include <ctype.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <openssl/evp.h>
+#include <openssl/x509.h>
+#include <openssl/pkcs12.h>
+#include <openssl/asn1.h>
+#include <openssl/pem.h>
+#include <openssl/err.h>
+#include <openssl/safestack.h>
+#include <openssl/stack.h>
+#include "p12lib.h"
+#include "pkgerr.h"
+#include "keystore.h"
+#include "pkglib.h"
+#include "pkglibmsgs.h"
+
+typedef struct keystore_t {
+	boolean_t		dirty;
+	boolean_t		new;
+	char			*path;
+	char			*passphrase;
+	/* truststore handles */
+	int			cafd;
+	STACK_OF(X509)		*cacerts;
+	char			*capath;
+
+	/* user certificate handles */
+	STACK_OF(X509)		*clcerts;
+	char			*clpath;
+
+	/* private key handles */
+	STACK_OF(EVP_PKEY)	*pkeys;
+	char			*keypath;
+} keystore_t;
+
+/* local routines */
+static keystore_t	*new_keystore(void);
+static void		free_keystore(keystore_t *);
+static boolean_t	verify_keystore_integrity(PKG_ERR *, keystore_t *);
+static boolean_t	check_password(PKCS12 *, char *);
+static boolean_t	resolve_paths(PKG_ERR *, char *, char *,
+    long, keystore_t *);
+static boolean_t	lock_keystore(PKG_ERR *, long, keystore_t *);
+
+static boolean_t	unlock_keystore(PKG_ERR *, keystore_t *);
+static boolean_t	read_keystore(PKG_ERR *, keystore_t *,
+    keystore_passphrase_cb);
+static boolean_t	write_keystore(PKG_ERR *, keystore_t *,
+    keystore_passphrase_cb);
+static boolean_t	write_keystore_file(PKG_ERR *, char *, PKCS12 *);
+static boolean_t	clear_keystore_file(PKG_ERR *, char *);
+static PKCS12		*read_keystore_file(PKG_ERR *, char *);
+static char		*get_time_string(ASN1_TIME *);
+
+/* locking routines */
+static boolean_t	restore_keystore_file(PKG_ERR *, char *);
+static int		file_lock(int, int, int);
+static int		file_unlock(int);
+static boolean_t	file_lock_test(int, int);
+static boolean_t	file_empty(char *);
+static boolean_t	get_keystore_passwd(PKG_ERR *err, PKCS12 *p12,
+    keystore_passphrase_cb cb, keystore_t *keystore);
+static boolean_t	wait_restore(int, char *, char *, char *);
+
+#define	KEYSTORE_PERMS	(S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH)
+
+/* wait on other keystore access for 1 minute before giving up */
+#define	LOCK_TIMEOUT	60
+
+/*
+ * print_certs  - prints certificates out of a keystore, to a file.
+ *
+ * Arguments:
+ * err - Error object to append errors to
+ * keystore - Keystore on which to operate
+ * alias - Name of certificate to print, NULL means print all
+ * format - Format in which to print certificates
+ * outfile - Where to print certificates
+ *
+ * Returns:
+ *   0 - Success
+ *   non-zero - Failure, errors added to err
+ */
+int
+print_certs(PKG_ERR *err, keystore_handle_t keystore_h, char *alias,
+    keystore_encoding_format_t format, FILE *outfile)
+{
+	int		i;
+	X509		*cert;
+	char		*fname = NULL;
+	boolean_t	found = B_FALSE;
+	keystore_t	*keystore = keystore_h;
+
+	if (keystore->clcerts != NULL) {
+		/* print out each client cert */
+		for (i = 0; i < sk_X509_num(keystore->clcerts); i++) {
+			cert = sk_X509_value(keystore->clcerts, i);
+			(void) sunw_get_cert_fname(GETDO_COPY, cert,
+			    &fname);
+
+			if (fname == NULL) {
+				/* no name recorded, keystore is corrupt */
+				pkgerr_add(err, PKGERR_CORRUPT,
+				    gettext(ERR_KEYSTORE_NO_ALIAS),
+				    get_subject_display_name(cert));
+				return (1);
+			}
+
+			if ((alias != NULL) && (!streq(alias, fname))) {
+				/* name does not match, skip it */
+				(void) OPENSSL_free(fname);
+				fname = NULL;
+				continue;
+			} else {
+				found = B_TRUE;
+				(void) print_cert(err, cert, format,
+				    fname, B_FALSE, outfile);
+				(void) OPENSSL_free(fname);
+				fname = NULL;
+			}
+		}
+	}
+
+	if (fname != NULL) {
+	    (void) OPENSSL_free(fname);
+	    fname = NULL;
+	}
+
+	if (keystore->cacerts != NULL) {
+		/* print out each trusted cert */
+		for (i = 0; i < sk_X509_num(keystore->cacerts); i++) {
+			cert = sk_X509_value(keystore->cacerts, i);
+			(void) sunw_get_cert_fname(GETDO_COPY,
+			    cert, &fname);
+
+			if (fname == NULL) {
+				/* no name recorded, keystore is corrupt */
+				pkgerr_add(err, PKGERR_CORRUPT,
+				    gettext(ERR_KEYSTORE_NO_ALIAS),
+				    get_subject_display_name(cert));
+				return (1);
+			}
+
+			if ((alias != NULL) && (!streq(alias, fname))) {
+				/* name does not match, skip it */
+				(void) OPENSSL_free(fname);
+				fname = NULL;
+				continue;
+			} else {
+				found = B_TRUE;
+				(void) print_cert(err, cert, format,
+				    fname, B_TRUE, outfile);
+				(void) OPENSSL_free(fname);
+				fname = NULL;
+			}
+		}
+	}
+
+	if (fname != NULL) {
+	    (void) OPENSSL_free(fname);
+	    fname = NULL;
+	}
+
+	if (found) {
+		return (0);
+	} else {
+		/* no certs printed */
+		if (alias != NULL) {
+			pkgerr_add(err, PKGERR_NOALIASMATCH,
+			    gettext(ERR_KEYSTORE_NOCERT),
+			    alias, keystore->path);
+		} else {
+			pkgerr_add(err, PKGERR_NOPUBKEY,
+			    gettext(ERR_KEYSTORE_NOPUBCERTS),
+			    keystore->path);
+			pkgerr_add(err, PKGERR_NOCACERT,
+			    gettext(ERR_KEYSTORE_NOCACERTS),
+			    keystore->path);
+		}
+		return (1);
+	}
+}
+
+/*
+ * print_cert  - prints a single certificate, to a file
+ *
+ * Arguments:
+ * err - Error object to append errors to
+ * x - The certificate to print
+ * alias - Name of certificate to print
+ * format - Format in which to print certificate
+ * outfile - Where to print certificate
+ *
+ * Returns:
+ *   0 - Success
+ *   non-zero - Failure, errors added to err
+ */
+int print_cert(PKG_ERR *err, X509 *x,
+    keystore_encoding_format_t format, char *alias, boolean_t is_trusted,
+    FILE *outfile)
+{
+
+	char *vdb_str;
+	char *vda_str;
+	char vd_str[ATTR_MAX];
+	int ret = 0;
+	char *cn_str, *icn_str, *typ_str;
+	char *tmp;
+	char *md5_fp;
+	char *sha1_fp;
+	int len;
+
+	/* need to localize the word "Fingerprint", hence these pointers */
+	char md5_label[ATTR_MAX];
+	char sha1_label[ATTR_MAX];
+
+	if (is_trusted) {
+		typ_str = gettext(MSG_KEYSTORE_TRUSTED);
+	} else {
+		typ_str = gettext(MSG_KEYSTORE_UNTRUSTED);
+	}
+
+	if ((cn_str = get_subject_display_name(x)) == NULL) {
+		cn_str = gettext(MSG_KEYSTORE_UNKNOWN);
+	}
+
+	if ((icn_str = get_issuer_display_name(x)) == NULL) {
+		icn_str = gettext(MSG_KEYSTORE_UNKNOWN);
+	}
+
+	vdb_str = xstrdup(get_time_string(X509_get_notBefore(x)));
+	vda_str = xstrdup(get_time_string(X509_get_notAfter(x)));
+	if (((len = snprintf(vd_str, ATTR_MAX, "<%s> - <%s>",
+	    vdb_str, vda_str)) < 0) || (len >= ATTR_MAX)) {
+		pkgerr_add(err, PKGERR_WEB, gettext(ERR_LEN), vdb_str);
+		ret = 1;
+		goto cleanup;
+	}
+
+	if ((tmp = get_fingerprint(x, EVP_md5())) == NULL) {
+		md5_fp = gettext(MSG_KEYSTORE_UNKNOWN);
+	} else {
+		/*
+		 * make a copy, otherwise the next call to get_fingerprint
+		 * will overwrite this one
+		 */
+		md5_fp = xstrdup(tmp);
+	}
+
+	if ((tmp = get_fingerprint(x, EVP_sha1())) == NULL) {
+		sha1_fp = gettext(MSG_KEYSTORE_UNKNOWN);
+	} else {
+		sha1_fp = xstrdup(tmp);
+	}
+
+	(void) snprintf(md5_label, ATTR_MAX, "%s %s",
+	    OBJ_nid2sn(EVP_MD_type(EVP_md5())),
+	    /* i18n: 14 characters max */
+	    gettext(MSG_KEYSTORE_FP));
+
+	(void) snprintf(sha1_label, ATTR_MAX, "%s %s",
+	    OBJ_nid2sn(EVP_MD_type(EVP_sha1())),
+	    /* i18n: 14 characters max */
+	    gettext(MSG_KEYSTORE_FP));
+
+	switch (format) {
+	case KEYSTORE_FORMAT_PEM:
+		(void) PEM_write_X509(outfile, x);
+		break;
+	case KEYSTORE_FORMAT_DER:
+		(void) i2d_X509_fp(outfile, x);
+		break;
+	case KEYSTORE_FORMAT_TEXT:
+		(void) fprintf(outfile, "%18s: %s\n",
+		    /* i18n: 18 characters max */
+		    gettext(MSG_KEYSTORE_AL), alias);
+		(void) fprintf(outfile, "%18s: %s\n",
+		    /* i18n: 18 characters max */
+		    gettext(MSG_KEYSTORE_CN), cn_str);
+		(void) fprintf(outfile, "%18s: %s\n",
+		    /* i18n: 18 characters max */
+		    gettext(MSG_KEYSTORE_TY), typ_str);
+		(void) fprintf(outfile, "%18s: %s\n",
+		    /* i18n: 18 characters max */
+		    gettext(MSG_KEYSTORE_IN), icn_str);
+		(void) fprintf(outfile, "%18s: %s\n",
+		    /* i18n: 18 characters max */
+		    gettext(MSG_KEYSTORE_VD), vd_str);
+		(void) fprintf(outfile, "%18s: %s\n", md5_label, md5_fp);
+		(void) fprintf(outfile, "%18s: %s\n", sha1_label, sha1_fp);
+		(void) fprintf(outfile, "\n");
+		break;
+	default:
+		pkgerr_add(err, PKGERR_INTERNAL,
+		    gettext(ERR_KEYSTORE_INTERNAL),
+		    __FILE__, __LINE__);
+		ret = 1;
+		goto cleanup;
+	}
+
+cleanup:
+	if (md5_fp != NULL)
+		free(md5_fp);
+	if (sha1_fp != NULL)
+		free(sha1_fp);
+	if (vda_str != NULL)
+		free(vda_str);
+	if (vdb_str != NULL)
+		free(vdb_str);
+	return (ret);
+}
+
+/*
+ * open_keystore - Initialize new keystore object for
+ * impending access.
+ *
+ * Arguments:
+ * err - Error object to append errors to
+ * keystore_file - Base filename or directory of keystore
+ * app - Application making request
+ * passwd - Password used to decrypt keystore
+ * flags - Control flags used to control access mode and behavior
+ * result - Resulting keystore object stored here on success
+ *
+ * Returns:
+ *   0 - Success - result contains a pointer to the opened keystore
+ *   non-zero - Failure, errors added to err
+ */
+int
+open_keystore(PKG_ERR *err, char *keystore_file, char *app,
+    keystore_passphrase_cb cb, long flags, keystore_handle_t *result)
+{
+	int ret = 0;
+	keystore_t	*tmpstore;
+
+	tmpstore = new_keystore();
+
+	tmpstore->dirty = B_FALSE;
+	tmpstore->new = B_FALSE;
+	tmpstore->path = xstrdup(keystore_file);
+
+	if (!resolve_paths(err, keystore_file, app, flags, tmpstore)) {
+		/* unable to determine keystore paths */
+		pkgerr_add(err, PKGERR_CORRUPT, gettext(ERR_KEYSTORE_REPAIR),
+		    keystore_file);
+		ret = 1;
+		goto cleanup;
+	}
+
+	if (!verify_keystore_integrity(err, tmpstore)) {
+		/* unable to repair keystore */
+		pkgerr_add(err, PKGERR_CORRUPT, gettext(ERR_KEYSTORE_REPAIR),
+		    keystore_file);
+		ret = 1;
+		goto cleanup;
+	}
+
+	if (!lock_keystore(err, flags, tmpstore)) {
+		pkgerr_add(err, PKGERR_LOCKED, gettext(ERR_KEYSTORE_LOCKED),
+		    keystore_file);
+		ret = 1;
+		goto cleanup;
+	}
+
+	/* now that we have locked the keystore, go ahead and read it */
+	if (!read_keystore(err, tmpstore, cb)) {
+		pkgerr_add(err, PKGERR_READ, gettext(ERR_PARSE),
+		    keystore_file);
+		ret = 1;
+		goto cleanup;
+	}
+
+	*result = tmpstore;
+	tmpstore = NULL;
+
+cleanup:
+	if (tmpstore != NULL)
+		free_keystore(tmpstore);
+	return (ret);
+}
+
+/*
+ * new_keystore - Allocates and initializes a Keystore object
+ *
+ * Arguments:
+ * NONE
+ *
+ * Returns:
+ *   NULL - out of memory
+ *   otherwise, returns a pointer to the newly allocated object,
+ *   which should be freed with free_keystore() when no longer
+ *   needed.
+ */
+static keystore_t
+*new_keystore(void)
+{
+	keystore_t *tmpstore;
+
+	if ((tmpstore = (keystore_t *)malloc(sizeof (keystore_t))) == NULL) {
+		return (NULL);
+	}
+	tmpstore->dirty = B_FALSE;
+	tmpstore->new = B_FALSE;
+	tmpstore->path = NULL;
+	tmpstore->passphrase = NULL;
+	tmpstore->cafd = -1;
+	tmpstore->cacerts = NULL;
+	tmpstore->capath = NULL;
+	tmpstore->clcerts = NULL;
+	tmpstore->clpath = NULL;
+	tmpstore->pkeys = NULL;
+	tmpstore->keypath = NULL;
+
+	return (tmpstore);
+}
+
+/*
+ * free_keystore - Deallocates a Keystore object
+ *
+ * Arguments:
+ * keystore - The keystore to deallocate
+ *
+ * Returns:
+ *   NONE
+ */
+static void
+free_keystore(keystore_t *keystore)
+{
+	if (keystore->path != NULL)
+		free(keystore->path);
+	if (keystore->capath != NULL)
+		free(keystore->capath);
+	if (keystore->passphrase != NULL)
+		free(keystore->passphrase);
+	if (keystore->clpath != NULL)
+		free(keystore->clpath);
+	if (keystore->keypath != NULL)
+		free(keystore->keypath);
+
+	if (keystore->pkeys != NULL) {
+		sk_EVP_PKEY_pop_free(keystore->pkeys,
+		    sunw_evp_pkey_free);
+	}
+	if (keystore->clcerts != NULL)
+		sk_X509_free(keystore->clcerts);
+	if (keystore->cacerts != NULL)
+		sk_X509_free(keystore->cacerts);
+	free(keystore);
+}
+
+/*
+ * close_keystore - Writes keystore to disk if needed, then
+ * unlocks and closes keystore.
+ *
+ * Arguments:
+ * err - Error object to append errors to
+ * keystore - Keystore which should be closed
+ * passwd - Password used to encrypt keystore
+ *
+ * Returns:
+ *   0 - Success - keystore is committed to disk, and unlocked
+ *   non-zero - Failure, errors added to err
+ */
+int
+close_keystore(PKG_ERR *err, keystore_handle_t keystore_h,
+    keystore_passphrase_cb cb)
+{
+	int ret = 0;
+	keystore_t *keystore = keystore_h;
+
+	if (keystore->dirty) {
+		/* write out the keystore first */
+		if (!write_keystore(err, keystore, cb)) {
+			pkgerr_add(err, PKGERR_WRITE,
+			    gettext(ERR_KEYSTORE_WRITE),
+			    keystore->path);
+			ret = 1;
+			goto cleanup;
+		}
+	}
+
+	if (!unlock_keystore(err, keystore)) {
+		pkgerr_add(err, PKGERR_UNLOCK, gettext(ERR_KEYSTORE_UNLOCK),
+		    keystore->path);
+		ret = 1;
+		goto cleanup;
+	}
+
+	free_keystore(keystore);
+cleanup:
+	return (ret);
+}
+
+/*
+ * merge_ca_cert - Adds a trusted certificate (trust anchor) to a keystore.
+ * certificate checked for validity dates and non-duplicity.
+ *
+ * Arguments:
+ * err - Error object to add errors to
+ * cacert - Certificate which to merge into keystore
+ * keystore - The keystore into which the certificate is merged
+ *
+ * Returns:
+ *   0 - Success - Certificate passes validity, and
+ *		is merged into keystore
+ * non-zero - Failure, errors recorded in err
+ */
+int
+merge_ca_cert(PKG_ERR *err, X509 *cacert, keystore_handle_t keystore_h)
+{
+
+	int		ret = 0;
+	X509		*existing = NULL;
+	char		*fname;
+	keystore_t	*keystore = keystore_h;
+
+	/* check validity dates */
+	if (check_cert(err, cacert) != 0) {
+		ret = 1;
+		goto cleanup;
+	}
+
+	/* create the certificate's friendlyName */
+	fname = get_subject_display_name(cacert);
+
+	if (sunw_set_fname(fname, NULL, cacert) != 0) {
+		pkgerr_add(err, PKGERR_NOMEM, gettext(ERR_MEM));
+		ret = 1;
+		goto cleanup;
+	}
+
+	/* merge certificate into the keystore */
+	if (keystore->cacerts == NULL) {
+		/* no existing truststore, so make a new one */
+		if ((keystore->cacerts = sk_X509_new_null()) == NULL) {
+			pkgerr_add(err, PKGERR_NOMEM, gettext(ERR_MEM));
+			ret = 1;
+			goto cleanup;
+		}
+	} else {
+		/* existing truststore, make sure there's no duplicate */
+		if (sunw_find_fname(fname, NULL, keystore->cacerts,
+		    NULL, &existing) < 0) {
+			pkgerr_add(err, PKGERR_INTERNAL,
+			    gettext(ERR_KEYSTORE_INTERNAL),
+			    __FILE__, __LINE__);
+			ERR_print_errors_fp(stderr);
+			ret = 1;
+			goto cleanup;
+			/* could not search properly! */
+		}
+		if (existing != NULL) {
+			/* whoops, found one already */
+			pkgerr_add(err, PKGERR_DUPLICATE,
+			    gettext(ERR_KEYSTORE_DUPLICATECERT), fname);
+			ret = 1;
+			goto cleanup;
+		}
+	}
+
+	(void) sk_X509_push(keystore->cacerts, cacert);
+	keystore->dirty = B_TRUE;
+cleanup:
+	if (existing != NULL)
+		X509_free(existing);
+	return (ret);
+}
+
+/*
+ * find_key_cert_pair - Searches a keystore for a matching
+ * public key certificate and private key, given an alias.
+ *
+ * Arguments:
+ * err - Error object to add errors to
+ * ks - Keystore to search
+ * alias - Name to used to match certificate's alias
+ * key - Resulting key is placed here
+ * cert - Resulting cert is placed here
+ *
+ * Returns:
+ *   0 - Success - Matching cert/key pair placed in key and cert.
+ * non-zero - Failure, errors recorded in err
+ */
+int
+find_key_cert_pair(PKG_ERR *err, keystore_handle_t ks_h, char *alias,
+    EVP_PKEY **key, X509 **cert)
+{
+	X509		*tmpcert = NULL;
+	EVP_PKEY	*tmpkey = NULL;
+	int		ret = 0;
+	int		items_found;
+	keystore_t	*ks = ks_h;
+
+	if (key == NULL || cert == NULL) {
+		pkgerr_add(err, PKGERR_NOPUBKEY,
+		    gettext(ERR_KEYSTORE_NOPUBCERTS), ks->path);
+		ret = 1;
+		goto cleanup;
+	}
+
+	if (ks->clcerts == NULL) {
+		/* no public certs */
+		pkgerr_add(err, PKGERR_NOPUBKEY,
+		    gettext(ERR_KEYSTORE_NOCERTS), ks->path);
+		ret = 1;
+		goto cleanup;
+	}
+	if (ks->pkeys == NULL) {
+		/* no private keys */
+		pkgerr_add(err, PKGERR_NOPRIVKEY,
+		    gettext(ERR_KEYSTORE_NOKEYS), ks->path);
+		ret = 1;
+		goto cleanup;
+	}
+
+	/* try the easy case first */
+	if ((sk_EVP_PKEY_num(ks->pkeys) == 1) &&
+	    (sk_X509_num(ks->clcerts) == 1)) {
+		tmpkey = sk_EVP_PKEY_value(ks->pkeys, 0);
+		tmpcert = sk_X509_value(ks->clcerts, 0);
+		if (sunw_check_keys(tmpcert, tmpkey)) {
+			/*
+			 * only one private key and public key cert, and they
+			 * match, so use them
+			 */
+			*key = tmpkey;
+			tmpkey = NULL;
+			*cert = tmpcert;
+			tmpcert = NULL;
+			goto cleanup;
+		}
+	}
+
+	/* Attempt to find the right pair given the alias */
+	items_found = sunw_find_fname(alias, ks->pkeys, ks->clcerts,
+	    &tmpkey, &tmpcert);
+
+	if ((items_found < 0) ||
+	    (items_found & (FOUND_PKEY | FOUND_CERT)) == 0) {
+		/* no key/cert pair found. bail. */
+		pkgerr_add(err, PKGERR_BADALIAS,
+		    gettext(ERR_KEYSTORE_NOMATCH), alias);
+		ret = 1;
+		goto cleanup;
+	}
+
+	/* success */
+	*key = tmpkey;
+	tmpkey = NULL;
+	*cert = tmpcert;
+	tmpcert = NULL;
+
+cleanup:
+
+	if (tmpcert != NULL)
+		(void) X509_free(tmpcert);
+
+	if (tmpkey != NULL)
+		sunw_evp_pkey_free(tmpkey);
+
+	return (ret);
+}
+
+/*
+ * find_ca_certs - Searches a keystore for trusted certificates
+ *
+ * Arguments:
+ * err - Error object to add errors to
+ * ks - Keystore to search
+ * cacerts - resulting set of trusted certs are placed here
+ *
+ * Returns:
+ *   0 - Success - trusted cert list returned in cacerts
+ * non-zero - Failure, errors recorded in err
+ */
+int
+find_ca_certs(PKG_ERR *err, keystore_handle_t ks_h, STACK_OF(X509) **cacerts)
+{
+
+	keystore_t	*ks = ks_h;
+
+	/* easy */
+	if (cacerts == NULL) {
+		pkgerr_add(err, PKGERR_INTERNAL,
+		    gettext(ERR_KEYSTORE_INTERNAL), __FILE__, __LINE__);
+		return (1);
+	}
+
+	*cacerts = ks->cacerts;
+	return (0);
+}
+
+/*
+ * find_cl_certs - Searches a keystore for user certificates
+ *
+ * Arguments:
+ * err - Error object to add errors to
+ * ks - Keystore to search
+ * cacerts - resulting set of user certs are placed here
+ *
+ * No matching of any kind is performed.
+ * Returns:
+ *   0 - Success - trusted cert list returned in cacerts
+ * non-zero - Failure, errors recorded in err
+ */
+/* ARGSUSED */
+int
+find_cl_certs(PKG_ERR *err, keystore_handle_t ks_h, STACK_OF(X509) **clcerts)
+{
+	keystore_t	*ks = ks_h;
+
+	/* easy */
+	*clcerts = ks->clcerts;
+	return (0);
+}
+
+
+/*
+ * merge_cert_and_key - Adds a user certificate and matching
+ * private key to a keystore.
+ * certificate checked for validity dates and non-duplicity.
+ *
+ * Arguments:
+ * err - Error object to add errors to
+ * cert - Certificate which to merge into keystore
+ * key - matching private key to 'cert'
+ * alias - Name which to store the cert and key under
+ * keystore - The keystore into which the certificate is merged
+ *
+ * Returns:
+ *   0 - Success - Certificate passes validity, and
+ *		is merged into keystore, along with key
+ * non-zero - Failure, errors recorded in err
+ */
+int
+merge_cert_and_key(PKG_ERR *err, X509 *cert, EVP_PKEY *key, char *alias,
+    keystore_handle_t keystore_h)
+{
+	X509		*existingcert = NULL;
+	EVP_PKEY	*existingkey = NULL;
+	int		ret = 0;
+	keystore_t	*keystore = keystore_h;
+
+	/* check validity dates */
+	if (check_cert(err, cert) != 0) {
+		ret = 1;
+		goto cleanup;
+	}
+
+	/* set the friendlyName of the key and cert to the supplied alias */
+	if (sunw_set_fname(alias, key, cert) != 0) {
+		pkgerr_add(err, PKGERR_NOMEM, gettext(ERR_MEM));
+		ret = 1;
+		goto cleanup;
+	}
+
+	/* merge certificate and key into the keystore */
+	if (keystore->clcerts == NULL) {
+		/* no existing truststore, so make a new one */
+		if ((keystore->clcerts = sk_X509_new_null()) == NULL) {
+			pkgerr_add(err, PKGERR_NOMEM, gettext(ERR_MEM));
+			ret = 1;
+			goto cleanup;
+		}
+	} else {
+		/* existing certstore, make sure there's no duplicate */
+		if (sunw_find_fname(alias, NULL, keystore->clcerts,
+		    NULL, &existingcert) < 0) {
+			pkgerr_add(err, PKGERR_INTERNAL,
+			    gettext(ERR_KEYSTORE_INTERNAL),
+			    __FILE__, __LINE__);
+			ERR_print_errors_fp(stderr);
+			ret = 1;
+			goto cleanup;
+			/* could not search properly! */
+		}
+		if (existingcert != NULL) {
+			/* whoops, found one already */
+			pkgerr_add(err, PKGERR_DUPLICATE,
+			    gettext(ERR_KEYSTORE_DUPLICATECERT), alias);
+			ret = 1;
+			goto cleanup;
+		}
+	}
+
+	if (keystore->pkeys == NULL) {
+		/* no existing keystore, so make a new one */
+		if ((keystore->pkeys = sk_EVP_PKEY_new_null()) == NULL) {
+			pkgerr_add(err, PKGERR_NOMEM, gettext(ERR_MEM));
+			ret = 1;
+			goto cleanup;
+		}
+	} else {
+		/* existing keystore, so make sure there's no duplicate entry */
+		if (sunw_find_fname(alias, keystore->pkeys, NULL,
+		    &existingkey, NULL) < 0) {
+			pkgerr_add(err, PKGERR_INTERNAL,
+			    gettext(ERR_KEYSTORE_INTERNAL),
+			    __FILE__, __LINE__);
+			ERR_print_errors_fp(stderr);
+			ret = 1;
+			goto cleanup;
+			/* could not search properly! */
+		}
+		if (existingkey != NULL) {
+			/* whoops, found one already */
+			pkgerr_add(err, PKGERR_DUPLICATE,
+			    gettext(ERR_KEYSTORE_DUPLICATEKEY), alias);
+			ret = 1;
+			goto cleanup;
+		}
+	}
+
+	(void) sk_X509_push(keystore->clcerts, cert);
+	(void) sk_EVP_PKEY_push(keystore->pkeys, key);
+	keystore->dirty = B_TRUE;
+cleanup:
+	if (existingcert != NULL)
+		(void) X509_free(existingcert);
+	if (existingkey != NULL)
+		(void) sunw_evp_pkey_free(existingkey);
+	return (ret);
+}
+
+/*
+ * delete_cert_and_keys - Deletes one or more certificates
+ *  and matching private keys from a keystore.
+ *
+ * Arguments:
+ * err - Error object to add errors to
+ * ks - The keystore from which certs and keys are deleted
+ * alias - Name which to search for certificates and keys
+ *	to delete
+ *
+ * Returns:
+ *   0 - Success - All trusted certs which match 'alias'
+ *		are deleted.  All user certificates
+ *		which match 'alias' are deleted, along
+ *		with the matching private key.
+ * non-zero - Failure, errors recorded in err
+ */
+int
+delete_cert_and_keys(PKG_ERR *err, keystore_handle_t ks_h, char *alias)
+{
+	X509		*existingcert;
+	EVP_PKEY	*existingkey;
+	int		i;
+	char		*fname = NULL;
+	boolean_t	found = B_FALSE;
+	keystore_t	*ks = ks_h;
+
+	/* delete any and all client certs with the supplied name */
+	if (ks->clcerts != NULL) {
+		for (i = 0; i < sk_X509_num(ks->clcerts); i++) {
+			existingcert = sk_X509_value(ks->clcerts, i);
+			if (sunw_get_cert_fname(GETDO_COPY,
+			    existingcert, &fname) >= 0) {
+				if (streq(fname, alias)) {
+					/* match, so nuke it */
+					existingcert =
+					    sk_X509_delete(ks->clcerts, i);
+					X509_free(existingcert);
+					existingcert = NULL;
+					found = B_TRUE;
+				}
+				(void) OPENSSL_free(fname);
+				fname = NULL;
+			}
+		}
+		if (sk_X509_num(ks->clcerts) <= 0) {
+			/* we deleted all the client certs */
+			sk_X509_free(ks->clcerts);
+			ks->clcerts = NULL;
+		}
+	}
+
+	/* and now the private keys */
+	if (ks->pkeys != NULL) {
+		for (i = 0; i < sk_EVP_PKEY_num(ks->pkeys); i++) {
+			existingkey = sk_EVP_PKEY_value(ks->pkeys, i);
+			if (sunw_get_pkey_fname(GETDO_COPY,
+			    existingkey, &fname) >= 0) {
+				if (streq(fname, alias)) {
+					/* match, so nuke it */
+					existingkey =
+					    sk_EVP_PKEY_delete(ks->pkeys, i);
+					sunw_evp_pkey_free(existingkey);
+					existingkey = NULL;
+					found = B_TRUE;
+				}
+				(void) OPENSSL_free(fname);
+				fname = NULL;
+			}
+		}
+		if (sk_EVP_PKEY_num(ks->pkeys) <= 0) {
+			/* we deleted all the private keys */
+			sk_EVP_PKEY_free(ks->pkeys);
+			ks->pkeys = NULL;
+		}
+	}
+
+	/* finally, remove any trust anchors that match */
+
+	if (ks->cacerts != NULL) {
+		for (i = 0; i < sk_X509_num(ks->cacerts); i++) {
+			existingcert = sk_X509_value(ks->cacerts, i);
+			if (sunw_get_cert_fname(GETDO_COPY,
+			    existingcert, &fname) >= 0) {
+				if (streq(fname, alias)) {
+					/* match, so nuke it */
+					existingcert =
+					    sk_X509_delete(ks->cacerts, i);
+					X509_free(existingcert);
+					existingcert = NULL;
+					found = B_TRUE;
+				}
+				(void) OPENSSL_free(fname);
+				fname = NULL;
+			}
+		}
+		if (sk_X509_num(ks->cacerts) <= 0) {
+			/* we deleted all the CA certs */
+			sk_X509_free(ks->cacerts);
+			ks->cacerts = NULL;
+		}
+	}
+
+	if (found) {
+		ks->dirty = B_TRUE;
+		return (0);
+	} else {
+		/* no certs or keys deleted */
+		pkgerr_add(err, PKGERR_NOALIASMATCH,
+		    gettext(ERR_KEYSTORE_NOCERTKEY),
+		    alias, ks->path);
+		return (1);
+	}
+}
+
+/*
+ * check_cert - Checks certificate validity.  This routine
+ * checks that the current time falls within the period
+ * of validity for the cert.
+ *
+ * Arguments:
+ * err - Error object to add errors to
+ * cert - The certificate to check
+ *
+ * Returns:
+ *   0 - Success - Certificate checks out
+ * non-zero - Failure, errors and reasons recorded in err
+ */
+int
+check_cert(PKG_ERR *err, X509 *cert)
+{
+	char			currtimestr[ATTR_MAX];
+	time_t			currtime;
+	char			*r, *before_str, *after_str;
+	/* get current time */
+	if ((currtime = time(NULL)) == (time_t)-1) {
+		pkgerr_add(err, PKGERR_TIME, gettext(ERR_CURR_TIME));
+		return (1);
+	}
+
+	(void) strlcpy(currtimestr, ctime(&currtime), ATTR_MAX);
+
+	/* trim whitespace from end of time string */
+	for (r = (currtimestr + strlen(currtimestr) - 1); isspace(*r); r--) {
+		*r = '\0';
+	}
+	/* check  validity of cert */
+	switch (sunw_check_cert_times(CHK_BOTH, cert)) {
+	case CHKERR_TIME_OK:
+		/* Current time meets requested checks */
+		break;
+	case CHKERR_TIME_BEFORE_BAD:
+		/* 'not before' field is invalid */
+	case CHKERR_TIME_AFTER_BAD:
+		/* 'not after' field is invalid */
+		pkgerr_add(err, PKGERR_TIME, gettext(ERR_CERT_TIME_BAD));
+		return (1);
+	case CHKERR_TIME_IS_BEFORE:
+		/* Current time is before 'not before' */
+	case CHKERR_TIME_HAS_EXPIRED:
+		/*
+		 * Ignore expiration time since the trust cert used to
+		 * verify the certs used to sign Sun patches is already
+		 * expired. Once the patches get resigned with the new
+		 * cert we will check expiration against the time the
+		 * patch was signed and not the time it is installed.
+		 */
+		return (0);
+	default:
+		pkgerr_add(err, PKGERR_INTERNAL,
+		    gettext(ERR_KEYSTORE_INTERNAL),
+		    __FILE__, __LINE__);
+		return (1);
+	}
+
+	/* all checks ok */
+	return (0);
+}
+
+/*
+ * check_cert - Checks certificate validity.  This routine
+ * checks everything that check_cert checks, and additionally
+ * verifies that the private key and corresponding public
+ * key are indeed a pair.
+ *
+ * Arguments:
+ * err - Error object to add errors to
+ * cert - The certificate to check
+ * key - the key to check
+ * Returns:
+ *   0 - Success - Certificate checks out
+ * non-zero - Failure, errors and reasons recorded in err
+ */
+int
+check_cert_and_key(PKG_ERR *err, X509 *cert, EVP_PKEY *key)
+{
+
+	/* check validity dates */
+	if (check_cert(err, cert) != 0) {
+		return (1);
+	}
+
+	/* check key pair match */
+	if (sunw_check_keys(cert, key) == 0) {
+		pkgerr_add(err, PKGERR_VERIFY, gettext(ERR_MISMATCHED_KEYS),
+		    get_subject_display_name(cert));
+		return (1);
+	}
+
+	/* all checks OK */
+	return (0);
+}
+
+/* ------------------ private functions ---------------------- */
+
+/*
+ * verify_keystore_integrity - Searches for the remnants
+ * of a failed or aborted keystore modification, and
+ * cleans up the files, retstores the keystore to a known
+ * state.
+ *
+ * Arguments:
+ * err - Error object to add errors to
+ * keystore_file - Base directory or filename of keystore
+ * app - Application making request
+ *
+ * Returns:
+ *   0 - Success - Keystore is restored, or untouched in the
+ *		case that cleanup was unnecessary
+ * non-zero - Failure, errors and reasons recorded in err
+ */
+static boolean_t
+verify_keystore_integrity(PKG_ERR *err, keystore_t *keystore)
+{
+	if (keystore->capath != NULL) {
+		if (!restore_keystore_file(err, keystore->capath)) {
+			return (B_FALSE);
+		}
+	}
+	if (keystore->clpath != NULL) {
+		if (!restore_keystore_file(err, keystore->clpath)) {
+			return (B_FALSE);
+		}
+	}
+	if (keystore->keypath != NULL) {
+		if (!restore_keystore_file(err, keystore->keypath)) {
+			return (B_FALSE);
+		}
+	}
+	return (B_TRUE);
+}
+
+/*
+ * restore_keystore_file - restores a keystore file to
+ * a known state.
+ *
+ * Keystore files can possibly be corrupted by a variety
+ * of error conditions during reading/writing.  This
+ * routine, along with write_keystore_file, tries to
+ * maintain keystore integrity by writing the files
+ * out in a particular order, minimizing the time period
+ * that the keystore is in an indeterminate state.
+ *
+ * With the current implementation, there are some failures
+ * that are wholly unrecoverable, such as disk corruption.
+ * These routines attempt to minimize the risk, but not
+ * eliminate it.  When better, atomic operations are available
+ * (such as a trued atabase with commit, rollback, and
+ * guaranteed atomicity), this implementation should use that.
+ *
+ * Arguments:
+ * err - Error object to add errors to
+ * keystore_file - keystore file path to restore.
+ *
+ * Returns:
+ *   0 - Success - Keystore file is restored, or untouched in the
+ *		case that cleanup was unnecessary
+ * non-zero - Failure, errors and reasons recorded in err
+ */
+/* ARGSUSED */
+static boolean_t
+restore_keystore_file(PKG_ERR *err, char *keystore_file)
+{
+	char	newpath[MAXPATHLEN];
+	char	backuppath[MAXPATHLEN];
+	int	newfd;
+	struct stat buf;
+	int len;
+
+	if (((len = snprintf(newpath, MAXPATHLEN, "%s.new",
+	    keystore_file)) < 0) ||
+	    (len >= ATTR_MAX)) {
+		pkgerr_add(err, PKGERR_WEB, gettext(ERR_LEN), keystore_file);
+		return (B_FALSE);
+	}
+
+	if (((len = snprintf(backuppath, MAXPATHLEN, "%s.bak",
+	    keystore_file)) < 0) ||
+	    (len >= ATTR_MAX)) {
+		pkgerr_add(err, PKGERR_WEB, gettext(ERR_LEN), keystore_file);
+		return (B_FALSE);
+	}
+
+	if ((newfd = open(newpath, O_RDWR|O_NONBLOCK, 0)) != -1) {
+		if (fstat(newfd, &buf) != -1) {
+			if (S_ISREG(buf.st_mode)) {
+				/*
+				 * restore the file, waiting on it
+				 * to be free for locking, or for
+				 * it to disappear
+				 */
+				if (!wait_restore(newfd, keystore_file,
+				    newpath, backuppath)) {
+					pkgerr_add(err, PKGERR_WRITE,
+					    gettext(ERR_WRITE),
+					    newpath, strerror(errno));
+					(void) close(newfd);
+					return (B_FALSE);
+				} else {
+					return (B_TRUE);
+				}
+			} else {
+				/* "new" file is not a regular file */
+				pkgerr_add(err, PKGERR_WRITE,
+				    gettext(ERR_NOT_REG), newpath);
+				(void) close(newfd);
+				return (B_FALSE);
+			}
+		} else {
+			/* couldn't stat "new" file */
+			pkgerr_add(err, PKGERR_WRITE,
+			    gettext(ERR_WRITE), newpath,
+			    strerror(errno));
+			(void) close(newfd);
+			return (B_FALSE);
+		}
+	} else {
+		/* "new" file doesn't exist */
+		return (B_TRUE);
+	}
+}
+
+static boolean_t
+wait_restore(int newfd, char *keystore_file,
+    char *origpath, char *backuppath)
+{
+	struct stat buf;
+	FILE *newstream;
+	PKCS12 *p12;
+
+	(void) alarm(LOCK_TIMEOUT);
+	if (file_lock(newfd, F_WRLCK, 1) == -1) {
+		/* could not lock file */
+		(void) alarm(0);
+		return (B_FALSE);
+	}
+	(void) alarm(0);
+
+	if (fstat(newfd, &buf) != -1) {
+		if (S_ISREG(buf.st_mode)) {
+			/*
+			 * The new file still
+			 * exists, with no
+			 * owner.  It must be
+			 * the result of an
+			 * aborted update.
+			 */
+			newstream = fdopen(newfd, "r");
+			if ((p12 =
+			    d2i_PKCS12_fp(newstream,
+				NULL)) != NULL) {
+				/*
+				 * The file
+				 * appears
+				 * complete.
+				 * Replace the
+				 * exsisting
+				 * keystore
+				 * file with
+				 * this one
+				 */
+				(void) rename(keystore_file, backuppath);
+				(void) rename(origpath, keystore_file);
+				PKCS12_free(p12);
+			} else {
+				/* The file is not complete.  Remove it */
+				(void) remove(origpath);
+			}
+			/* remove backup file */
+			(void) remove(backuppath);
+			(void) fclose(newstream);
+			(void) close(newfd);
+			return (B_TRUE);
+		} else {
+			/*
+			 * new file exists, but is not a
+			 * regular file
+			 */
+			(void) close(newfd);
+			return (B_FALSE);
+		}
+	} else {
+		/*
+		 * could not stat file.  Unless
+		 * the reason was that the file
+		 * is now gone, this is an error
+		 */
+		if (errno != ENOENT) {
+			(void) close(newfd);
+			return (B_FALSE);
+		}
+		/*
+		 * otherwise, file is gone.  The process
+		 * that held the lock must have
+		 * successfully cleaned up and
+		 * exited with a valid keystore
+		 * state
+		 */
+		(void) close(newfd);
+		return (B_TRUE);
+	}
+}
+
+/*
+ * resolve_paths - figure out if we are dealing with a single-file
+ * or multi-file keystore
+ *
+ * The flags tell resolve_paths how to behave:
+ *
+ * KEYSTORE_PATH_SOFT
+ * If the keystore file does not exist at <base>/<app> then
+ * use <base> as the path to the keystore.  This can be used,
+ * for example, to access an app-specific keystore iff it
+ * exists, otherwise revert back to an app-generic keystore.
+ *
+ * KEYSTORE_PATH_HARD
+ * Always use the keystore located at <keystore_path>/<app>.
+ * In read/write mode, if the files do not exist, then
+ * they will be created.  This is used to avoid falling
+ * back to an app-generic keystore path when the app-specific
+ * one does not exist.
+ *
+ * Arguments:
+ * err - Error object to add errors to
+ * keystore_file - base keystore file path to lock
+ * app - Application making requests
+ * flags - Control flags (see above description)
+ * keystore - object which is being locked
+ *
+ * Returns:
+ *   B_TRUE - Success - Keystore file is locked, paths to
+ *		appropriate files placed in keystore.
+ *   B_FALSE - Failure, errors and reasons recorded in err
+ */
+static boolean_t
+resolve_paths(PKG_ERR *err, char *keystore_file, char *app,
+    long flags, keystore_t *keystore)
+{
+	char			storepath[PATH_MAX];
+	struct stat		buf;
+	boolean_t		multi = B_FALSE;
+	int			fd1, fd2, len;
+
+	/*
+	 * figure out whether we are dealing with a single-file keystore
+	 * or a multi-file keystore
+	 */
+	if (app != NULL) {
+		if (((len = snprintf(storepath, PATH_MAX, "%s/%s",
+		    keystore_file, app)) < 0) ||
+		    (len >= ATTR_MAX)) {
+			pkgerr_add(err, PKGERR_WEB, gettext(ERR_LEN),
+			    keystore_file);
+			return (B_FALSE);
+		}
+
+		if (((fd1 = open(storepath, O_NONBLOCK|O_RDONLY)) == -1) ||
+		    (fstat(fd1, &buf) == -1) ||
+		    !S_ISDIR(buf.st_mode)) {
+			/*
+			 * app-specific does not exist
+			 * fallback to app-generic, if flags say we can
+			 */
+			if ((flags & KEYSTORE_PATH_MASK) ==
+			    KEYSTORE_PATH_SOFT) {
+
+				if (((fd2 = open(keystore_file,
+				    O_NONBLOCK|O_RDONLY)) != -1) &&
+				    (fstat(fd2, &buf) != -1)) {
+					if (S_ISDIR(buf.st_mode)) {
+						/*
+						 * app-generic dir
+						 * exists, so use it
+						 * as a multi-file
+						 * keystore
+						 */
+						multi = B_TRUE;
+						app = NULL;
+					} else if (S_ISREG(buf.st_mode)) {
+						/*
+						 * app-generic file exists, so
+						 * use it as a single file ks
+						 */
+						multi = B_FALSE;
+						app = NULL;
+					}
+				}
+			}
+		}
+		if (fd1 != -1)
+			(void) close(fd1);
+		if (fd2 != -1)
+			(void) close(fd2);
+	} else {
+		if (((fd1 = open(keystore_file,
+		    O_NONBLOCK|O_RDONLY)) != -1) &&
+		    (fstat(fd1, &buf) != -1) &&
+		    S_ISDIR(buf.st_mode)) {
+			/*
+			 * app-generic dir exists, so use
+			 * it as a multi-file keystore
+			 */
+			multi = B_TRUE;
+		}
+		if (fd1 != -1)
+			(void) close(fd1);
+	}
+
+	if (app != NULL) {
+		/* app-specific keystore */
+		(void) snprintf(storepath, PATH_MAX, "%s/%s/%s",
+		    keystore_file, app, TRUSTSTORE);
+		keystore->capath = xstrdup(storepath);
+		(void) snprintf(storepath, PATH_MAX, "%s/%s/%s",
+		    keystore_file, app, CERTSTORE);
+		keystore->clpath = xstrdup(storepath);
+		(void) snprintf(storepath, PATH_MAX, "%s/%s/%s",
+		    keystore_file, app, KEYSTORE);
+		keystore->keypath = xstrdup(storepath);
+	} else {
+		/* app-generic keystore */
+		if (!multi) {
+			/* single-file app-generic keystore */
+			keystore->capath = xstrdup(keystore_file);
+			keystore->keypath = NULL;
+			keystore->clpath = NULL;
+		} else {
+			/* multi-file app-generic keystore */
+			(void) snprintf(storepath, PATH_MAX, "%s/%s",
+			    keystore_file, TRUSTSTORE);
+			keystore->capath = xstrdup(storepath);
+			(void) snprintf(storepath, PATH_MAX, "%s/%s",
+			    keystore_file, CERTSTORE);
+			keystore->clpath = xstrdup(storepath);
+			(void) snprintf(storepath, PATH_MAX, "%s/%s",
+			    keystore_file, KEYSTORE);
+			keystore->keypath = xstrdup(storepath);
+		}
+	}
+
+	return (B_TRUE);
+}
+
+/*
+ * lock_keystore - Locks a keystore for shared (read-only)
+ * or exclusive (read-write) access.
+ *
+ * The flags tell lock_keystore how to behave:
+ *
+ * KEYSTORE_ACCESS_READONLY
+ * opens keystore read-only.  Attempts to modify results in an error
+ *
+ * KEYSTORE_ACCESS_READWRITE
+ * opens keystore read-write
+ *
+ * KEYSTORE_PATH_SOFT
+ * If the keystore file does not exist at <base>/<app> then
+ * use <base> as the path to the keystore.  This can be used,
+ * for example, to access an app-specific keystore iff it
+ * exists, otherwise revert back to an app-generic keystore.
+ *
+ * KEYSTORE_PATH_HARD
+ * Always use the keystore located at <keystore_path>/<app>.
+ * In read/write mode, if the files do not exist, then
+ * they will be created.  This is used to avoid falling
+ * back to an app-generic keystore path when the app-specific
+ * one does not exist.
+ *
+ * Arguments:
+ * err - Error object to add errors to
+ * flags - Control flags (see above description)
+ * keystore - object which is being locked
+ *
+ * Returns:
+ *   0 - Success - Keystore file is locked, paths to
+ *		appropriate files placed in keystore.
+ * non-zero - Failure, errors and reasons recorded in err
+ */
+static boolean_t
+lock_keystore(PKG_ERR *err, long flags, keystore_t *keystore)
+{
+	boolean_t		ret = B_TRUE;
+	struct stat		buf;
+
+	switch (flags & KEYSTORE_ACCESS_MASK) {
+	case KEYSTORE_ACCESS_READONLY:
+		if ((keystore->cafd =
+		    open(keystore->capath, O_NONBLOCK|O_RDONLY)) == -1) {
+			if (errno == ENOENT) {
+				/*
+				 * no keystore.  try to create an
+				 * empty one so we can lock on it and
+				 * prevent others from gaining
+				 * exclusive access.  It will be
+				 * deleted when the keystore is closed.
+				 */
+				if ((keystore->cafd =
+				    open(keystore->capath,
+					O_NONBLOCK|O_RDWR|O_CREAT|O_EXCL,
+					S_IRUSR|S_IWUSR)) == -1) {
+					pkgerr_add(err, PKGERR_READ,
+					    gettext(ERR_NO_KEYSTORE),
+					    keystore->capath);
+					ret = B_FALSE;
+					goto cleanup;
+				}
+			} else {
+				pkgerr_add(err, PKGERR_READ,
+				    gettext(ERR_KEYSTORE_OPEN),
+				    keystore->capath, strerror(errno));
+				ret = B_FALSE;
+				goto cleanup;
+			}
+		}
+		if (fstat(keystore->cafd, &buf) != -1) {
+			if (S_ISREG(buf.st_mode)) {
+				if (file_lock(keystore->cafd, F_RDLCK,
+				    0) == -1) {
+					pkgerr_add(err, PKGERR_LOCKED,
+					    gettext(ERR_KEYSTORE_LOCKED_READ),
+					    keystore->capath);
+					ret = B_FALSE;
+					goto cleanup;
+				}
+			} else {
+				/* ca file not a regular file! */
+				pkgerr_add(err, PKGERR_READ,
+				    gettext(ERR_NOT_REG),
+				    keystore->capath);
+				ret = B_FALSE;
+				goto cleanup;
+			}
+		} else {
+			pkgerr_add(err, PKGERR_READ,
+			    gettext(ERR_KEYSTORE_OPEN),
+			    keystore->capath, strerror(errno));
+			ret = B_FALSE;
+			goto cleanup;
+		}
+		break;
+	case KEYSTORE_ACCESS_READWRITE:
+
+		if ((keystore->cafd = open(keystore->capath,
+		    O_RDWR|O_NONBLOCK)) == -1) {
+			/* does not exist.  try to create an empty one */
+			if (errno == ENOENT) {
+				if ((keystore->cafd =
+				    open(keystore->capath,
+					O_NONBLOCK|O_RDWR|O_CREAT|O_EXCL,
+					S_IRUSR|S_IWUSR)) == -1) {
+					pkgerr_add(err, PKGERR_READ,
+					    gettext(ERR_KEYSTORE_WRITE),
+					    keystore->capath);
+					ret = B_FALSE;
+					goto cleanup;
+				}
+			} else {
+				pkgerr_add(err, PKGERR_READ,
+				    gettext(ERR_KEYSTORE_OPEN),
+				    keystore->capath, strerror(errno));
+				ret = B_FALSE;
+				goto cleanup;
+			}
+		}
+		if (fstat(keystore->cafd, &buf) != -1) {
+			if (S_ISREG(buf.st_mode)) {
+				if (file_lock(keystore->cafd, F_WRLCK,
+				    0) == -1) {
+					pkgerr_add(err, PKGERR_LOCKED,
+					    gettext(ERR_KEYSTORE_LOCKED),
+					    keystore->capath);
+					ret = B_FALSE;
+					goto cleanup;
+				}
+			} else {
+				/* ca file not a regular file! */
+				pkgerr_add(err, PKGERR_READ,
+				    gettext(ERR_NOT_REG),
+				    keystore->capath);
+				ret = B_FALSE;
+				goto cleanup;
+			}
+		} else {
+			pkgerr_add(err, PKGERR_READ,
+			    gettext(ERR_KEYSTORE_OPEN),
+			    keystore->capath, strerror(errno));
+			ret = B_FALSE;
+			goto cleanup;
+		}
+
+		break;
+	default:
+		pkgerr_add(err, PKGERR_INTERNAL,
+		    gettext(ERR_KEYSTORE_INTERNAL),
+		    __FILE__, __LINE__);
+		ret = B_FALSE;
+		goto cleanup;
+	}
+
+cleanup:
+	if (!ret) {
+		if (keystore->cafd > 0) {
+			(void) file_unlock(keystore->cafd);
+			(void) close(keystore->cafd);
+			keystore->cafd = -1;
+		}
+
+		if (keystore->capath != NULL)
+			free(keystore->capath);
+		if (keystore->clpath != NULL)
+			free(keystore->clpath);
+		if (keystore->keypath != NULL)
+			free(keystore->keypath);
+		keystore->capath = NULL;
+		keystore->clpath = NULL;
+		keystore->keypath = NULL;
+	}
+
+	return (ret);
+}
+
+/*
+ * unlock_keystore - Unocks a keystore
+ *
+ * Arguments:
+ * err - Error object to add errors to
+ * keystore - keystore object to unlock
+ * Returns:
+ *   0 - Success - Keystore files are unlocked, files are closed,
+ * non-zero - Failure, errors and reasons recorded in err
+ */
+/* ARGSUSED */
+static boolean_t
+unlock_keystore(PKG_ERR *err, keystore_t *keystore)
+{
+
+	/*
+	 * Release lock on the CA file.
+	 * Delete file if it is empty
+	 */
+	if (file_empty(keystore->capath)) {
+		(void) remove(keystore->capath);
+	}
+
+	(void) file_unlock(keystore->cafd);
+	(void) close(keystore->cafd);
+	return (B_TRUE);
+}
+
+/*
+ * read_keystore - Reads keystore files of disk, parses
+ * into internal structures.
+ *
+ * Arguments:
+ * err - Error object to add errors to
+ * keystore - keystore object to read into
+ * cb - callback to get password, if required
+ * Returns:
+ *   0 - Success - Keystore files are read, and placed
+ * into keystore structure.
+ * non-zero - Failure, errors and reasons recorded in err
+ */
+static boolean_t
+read_keystore(PKG_ERR *err, keystore_t *keystore, keystore_passphrase_cb cb)
+{
+	boolean_t	ret = B_TRUE;
+	PKCS12		*p12 = NULL;
+	boolean_t	ca_empty;
+	boolean_t	have_passwd = B_FALSE;
+	boolean_t	cl_empty = B_TRUE;
+	boolean_t	key_empty = B_TRUE;
+
+	ca_empty = file_empty(keystore->capath);
+
+	if (keystore->clpath != NULL)
+		cl_empty = file_empty(keystore->clpath);
+	if (keystore->keypath != NULL)
+		key_empty = file_empty(keystore->keypath);
+
+	if (ca_empty && cl_empty && key_empty) {
+	    keystore->new = B_TRUE;
+	}
+
+	if (!ca_empty) {
+		/* first read the ca file */
+		if ((p12 = read_keystore_file(err,
+		    keystore->capath)) == NULL) {
+			pkgerr_add(err, PKGERR_CORRUPT,
+			    gettext(ERR_KEYSTORE_CORRUPT), keystore->capath);
+			ret = B_FALSE;
+			goto cleanup;
+		}
+
+		/* Get password, using callback if necessary */
+		if (!have_passwd) {
+			if (!get_keystore_passwd(err, p12, cb, keystore)) {
+				ret = B_FALSE;
+				goto cleanup;
+			}
+			have_passwd = B_TRUE;
+		}
+
+		/* decrypt and parse keystore file */
+		if (sunw_PKCS12_contents(p12, keystore->passphrase,
+		    &keystore->pkeys, &keystore->cacerts) < 0) {
+			/* could not parse the contents */
+			pkgerr_add(err, PKGERR_CORRUPT,
+			    gettext(ERR_KEYSTORE_CORRUPT), keystore->capath);
+			ret = B_FALSE;
+			goto cleanup;
+		}
+
+		PKCS12_free(p12);
+		p12 = NULL;
+	} else {
+
+		/*
+		 * truststore is empty, so we don't have any trusted
+		 * certs
+		 */
+		keystore->cacerts = NULL;
+	}
+
+	/*
+	 * if there is no cl file or key file, use the cl's and key's found
+	 * in the ca file
+	 */
+	if (keystore->clpath == NULL && !ca_empty) {
+		if (sunw_split_certs(keystore->pkeys, keystore->cacerts,
+		    &keystore->clcerts, NULL) < 0) {
+			pkgerr_add(err, PKGERR_CORRUPT,
+			    gettext(ERR_KEYSTORE_CORRUPT), keystore->capath);
+			ret = B_FALSE;
+			goto cleanup;
+		}
+	} else {
+		/*
+		 * files are in separate files.  read keys out of the keystore
+		 * certs out of the certstore, if they are not empty
+		 */
+		if (!cl_empty) {
+			if ((p12 = read_keystore_file(err,
+			    keystore->clpath)) == NULL) {
+				pkgerr_add(err, PKGERR_CORRUPT,
+				    gettext(ERR_KEYSTORE_CORRUPT),
+				    keystore->clpath);
+				ret = B_FALSE;
+				goto cleanup;
+			}
+
+			/* Get password, using callback if necessary */
+			if (!have_passwd) {
+				if (!get_keystore_passwd(err, p12, cb,
+				    keystore)) {
+					ret = B_FALSE;
+					goto cleanup;
+				}
+				have_passwd = B_TRUE;
+			}
+
+			if (check_password(p12,
+			    keystore->passphrase) == B_FALSE) {
+				/*
+				 * password in client cert file
+				 * is different than
+				 * the one in the other files!
+				 */
+				pkgerr_add(err, PKGERR_BADPASS,
+				    gettext(ERR_MISMATCHPASS),
+				    keystore->clpath,
+				    keystore->capath, keystore->path);
+				ret = B_FALSE;
+				goto cleanup;
+			}
+
+			if (sunw_PKCS12_contents(p12, keystore->passphrase,
+			    NULL, &keystore->clcerts) < 0) {
+				/* could not parse the contents */
+				pkgerr_add(err, PKGERR_CORRUPT,
+				    gettext(ERR_KEYSTORE_CORRUPT),
+				    keystore->clpath);
+				ret = B_FALSE;
+				goto cleanup;
+			}
+
+			PKCS12_free(p12);
+			p12 = NULL;
+		} else {
+			keystore->clcerts = NULL;
+		}
+
+		if (!key_empty) {
+			if ((p12 = read_keystore_file(err,
+			    keystore->keypath)) == NULL) {
+				pkgerr_add(err, PKGERR_CORRUPT,
+				    gettext(ERR_KEYSTORE_CORRUPT),
+				    keystore->keypath);
+				ret = B_FALSE;
+				goto cleanup;
+			}
+
+			/* Get password, using callback if necessary */
+			if (!have_passwd) {
+				if (!get_keystore_passwd(err, p12, cb,
+				    keystore)) {
+					ret = B_FALSE;
+					goto cleanup;
+				}
+				have_passwd = B_TRUE;
+			}
+
+			if (check_password(p12,
+			    keystore->passphrase) == B_FALSE) {
+				pkgerr_add(err, PKGERR_BADPASS,
+				    gettext(ERR_MISMATCHPASS),
+				    keystore->keypath,
+				    keystore->capath, keystore->path);
+				ret = B_FALSE;
+				goto cleanup;
+			}
+
+			if (sunw_PKCS12_contents(p12, keystore->passphrase,
+			    &keystore->pkeys, NULL) < 0) {
+				/* could not parse the contents */
+				pkgerr_add(err, PKGERR_CORRUPT,
+				    gettext(ERR_KEYSTORE_CORRUPT),
+				    keystore->keypath);
+				ret = B_FALSE;
+				goto cleanup;
+			}
+
+			PKCS12_free(p12);
+			p12 = NULL;
+		} else {
+			keystore->pkeys = NULL;
+		}
+	}
+
+cleanup:
+	if (p12 != NULL)
+		PKCS12_free(p12);
+	return (ret);
+}
+
+/*
+ * get_keystore_password - retrieves pasword used to
+ * decrypt PKCS12 structure.
+ *
+ * Arguments:
+ * err - Error object to add errors to
+ * p12 - PKCS12 structure which returned password should
+ * decrypt
+ * cb - callback to collect password.
+ * keystore - The keystore in which the PKCS12 structure
+ * will eventually populate.
+ * Returns:
+ *   B_TRUE - success.
+ *     keystore password is set in keystore->passphrase.
+ *   B_FALSE - failure, errors logged
+ */
+static boolean_t
+get_keystore_passwd(PKG_ERR *err, PKCS12 *p12, keystore_passphrase_cb cb,
+    keystore_t *keystore)
+{
+	char				*passwd;
+	char				passbuf[KEYSTORE_PASS_MAX + 1];
+	keystore_passphrase_data	data;
+
+	/* see if no password is the right password */
+	if (check_password(p12, "") == B_TRUE) {
+		passwd = "";
+	} else if (check_password(p12, NULL) == B_TRUE) {
+		passwd = NULL;
+	} else {
+		/* oops, it's encrypted.  get password */
+		data.err = err;
+		if (cb(passbuf, KEYSTORE_PASS_MAX, 0,
+		    &data) == -1) {
+			/* could not get password */
+			return (B_FALSE);
+		}
+
+		if (check_password(p12, passbuf) == B_FALSE) {
+				/* wrong password */
+			pkgerr_add(err, PKGERR_BADPASS,
+			    gettext(ERR_BADPASS));
+			return (B_FALSE);
+		}
+
+		/*
+		 * make copy of password buffer, since it
+		 * goes away upon return
+		 */
+		passwd = xstrdup(passbuf);
+	}
+	keystore->passphrase = passwd;
+	return (B_TRUE);
+}
+
+/*
+ * write_keystore - Writes keystore files to disk
+ *
+ * Arguments:
+ * err - Error object to add errors to
+ * keystore - keystore object to write from
+ * passwd - password used to encrypt keystore
+ * Returns:
+ *   0 - Success - Keystore contents are written out to
+ *   the same locations as read from
+ * non-zero - Failure, errors and reasons recorded in err
+ */
+static boolean_t
+write_keystore(PKG_ERR *err, keystore_t *keystore,
+    keystore_passphrase_cb cb)
+{
+	PKCS12	*p12 = NULL;
+	boolean_t ret = B_TRUE;
+	keystore_passphrase_data data;
+	char		passbuf[KEYSTORE_PASS_MAX + 1];
+
+	if (keystore->capath != NULL && keystore->clpath == NULL &&
+	    keystore->keypath == NULL) {
+
+		/*
+		 * keystore is a file.
+		 * just write out a single file
+		 */
+		if ((keystore->pkeys == NULL) &&
+		    (keystore->clcerts == NULL) &&
+		    (keystore->cacerts == NULL)) {
+			if (!clear_keystore_file(err, keystore->capath)) {
+				/*
+				 * no keys or certs to write out, so
+				 * blank the ca file.  we do not
+				 * delete it since it is used as a
+				 * lock by lock_keystore() in
+				 * subsequent invocations
+				 */
+				pkgerr_add(err, PKGERR_WRITE,
+				    gettext(ERR_KEYSTORE_WRITE),
+				    keystore->capath);
+				ret = B_FALSE;
+				goto cleanup;
+			}
+		} else {
+			/*
+			 * if the keystore is being created for the first time,
+			 * prompt for a passphrase for encryption
+			 */
+			if (keystore->new) {
+				data.err = err;
+				if (cb(passbuf, KEYSTORE_PASS_MAX,
+				    1, &data) == -1) {
+					ret = B_FALSE;
+					goto cleanup;
+				}
+			} else {
+				/*
+				 * use the one used when the keystore
+				 * was read
+				 */
+				strlcpy(passbuf, keystore->passphrase,
+				    KEYSTORE_PASS_MAX);
+			}
+
+			p12 = sunw_PKCS12_create(passbuf, keystore->pkeys,
+			    keystore->clcerts, keystore->cacerts);
+
+			if (p12 == NULL) {
+				pkgerr_add(err, PKGERR_WRITE,
+				    gettext(ERR_KEYSTORE_FORM),
+				    keystore->capath);
+				ret = B_FALSE;
+				goto cleanup;
+			}
+
+			if (!write_keystore_file(err, keystore->capath, p12)) {
+				pkgerr_add(err, PKGERR_WRITE,
+				    gettext(ERR_KEYSTORE_WRITE),
+				    keystore->capath);
+				ret = B_FALSE;
+				goto cleanup;
+			}
+		}
+
+	} else {
+		/* files are seprate. Do one at a time */
+
+		/*
+		 * if the keystore is being created for the first time,
+		 * prompt for a passphrase for encryption
+		 */
+		if (keystore->new && ((keystore->pkeys != NULL) ||
+		    (keystore->clcerts != NULL) ||
+		    (keystore->cacerts != NULL))) {
+			data.err = err;
+			if (cb(passbuf, KEYSTORE_PASS_MAX,
+			    1, &data) == -1) {
+				ret = B_FALSE;
+				goto cleanup;
+			}
+		} else {
+			/* use the one used when the keystore was read */
+			strlcpy(passbuf, keystore->passphrase,
+			    KEYSTORE_PASS_MAX);
+		}
+
+		/* do private keys first */
+		if (keystore->pkeys != NULL) {
+			p12 = sunw_PKCS12_create(passbuf, keystore->pkeys,
+			    NULL, NULL);
+
+			if (p12 == NULL) {
+				pkgerr_add(err, PKGERR_WRITE,
+				    gettext(ERR_KEYSTORE_FORM),
+				    keystore->keypath);
+				ret = B_FALSE;
+				goto cleanup;
+			}
+
+			if (!write_keystore_file(err, keystore->keypath,
+			    p12)) {
+				pkgerr_add(err, PKGERR_WRITE,
+				    gettext(ERR_KEYSTORE_WRITE),
+				    keystore->keypath);
+				ret = B_FALSE;
+				goto cleanup;
+			}
+
+			PKCS12_free(p12);
+		} else {
+			if ((remove(keystore->keypath) != 0) &&
+			    (errno != ENOENT)) {
+				pkgerr_add(err, PKGERR_WRITE,
+				    gettext(ERR_KEYSTORE_REMOVE),
+				    keystore->keypath);
+				ret = B_FALSE;
+				goto cleanup;
+			}
+		}
+
+		/* do user certs next */
+		if (keystore->clcerts != NULL) {
+			p12 = sunw_PKCS12_create(passbuf, NULL,
+			    keystore->clcerts, NULL);
+
+			if (p12 == NULL) {
+				pkgerr_add(err, PKGERR_WRITE,
+				    gettext(ERR_KEYSTORE_FORM),
+				    keystore->clpath);
+				ret = B_FALSE;
+				goto cleanup;
+			}
+
+			if (!write_keystore_file(err, keystore->clpath, p12)) {
+				pkgerr_add(err, PKGERR_WRITE,
+				    gettext(ERR_KEYSTORE_WRITE),
+				    keystore->clpath);
+				ret = B_FALSE;
+				goto cleanup;
+			}
+
+			PKCS12_free(p12);
+		} else {
+			if ((remove(keystore->clpath) != 0) &&
+			    (errno != ENOENT)) {
+				pkgerr_add(err, PKGERR_WRITE,
+				    gettext(ERR_KEYSTORE_REMOVE),
+				    keystore->clpath);
+				ret = B_FALSE;
+				goto cleanup;
+			}
+		}
+
+
+		/* finally do CA cert file */
+		if (keystore->cacerts != NULL) {
+			p12 = sunw_PKCS12_create(passbuf, NULL,
+			    NULL, keystore->cacerts);
+
+			if (p12 == NULL) {
+				pkgerr_add(err, PKGERR_WRITE,
+				    gettext(ERR_KEYSTORE_FORM),
+				    keystore->capath);
+				ret = B_FALSE;
+				goto cleanup;
+			}
+
+			if (!write_keystore_file(err, keystore->capath, p12)) {
+				pkgerr_add(err, PKGERR_WRITE,
+				    gettext(ERR_KEYSTORE_WRITE),
+				    keystore->capath);
+				ret = B_FALSE;
+				goto cleanup;
+			}
+
+			PKCS12_free(p12);
+			p12 = NULL;
+		} else {
+			/*
+			 * nothing to write out, so truncate the file
+			 * (it will be deleted during close_keystore)
+			 */
+			if (!clear_keystore_file(err, keystore->capath)) {
+				pkgerr_add(err, PKGERR_WRITE,
+				    gettext(ERR_KEYSTORE_WRITE),
+				    keystore->capath);
+				ret = B_FALSE;
+				goto cleanup;
+			}
+		}
+	}
+
+cleanup:
+	if (p12 != NULL)
+		PKCS12_free(p12);
+
+	return (ret);
+}
+
+/*
+ * clear_keystore_file - Clears (zeros out) a keystore file.
+ *
+ * Arguments:
+ * err - Error object to add errors to
+ * dest - Path of keystore file to zero out.
+ * Returns:
+ *   0 - Success - Keystore file is truncated to zero length
+ * non-zero - Failure, errors and reasons recorded in err
+ */
+static boolean_t
+clear_keystore_file(PKG_ERR *err, char *dest)
+{
+	int fd;
+	struct stat buf;
+
+	fd = open(dest, O_RDWR|O_NONBLOCK);
+	if (fd == -1) {
+		/* can't open for writing */
+		pkgerr_add(err, PKGERR_WRITE, gettext(MSG_OPEN),
+		    errno);
+		return (B_FALSE);
+	}
+
+	if ((fstat(fd, &buf) == -1) || !S_ISREG(buf.st_mode)) {
+		/* not a regular file */
+		(void) close(fd);
+		pkgerr_add(err, PKGERR_WRITE, gettext(ERR_NOT_REG),
+		    dest);
+		return (B_FALSE);
+	}
+
+	if (ftruncate(fd, 0) == -1) {
+		(void) close(fd);
+		pkgerr_add(err, PKGERR_WRITE, gettext(ERR_WRITE),
+		    dest, strerror(errno));
+		return (B_FALSE);
+	}
+
+	(void) close(fd);
+	return (B_TRUE);
+}
+
+/*
+ * write_keystore_file - Writes keystore file to disk.
+ *
+ * Keystore files can possibly be corrupted by a variety
+ * of error conditions during reading/writing.  This
+ * routine, along with restore_keystore_file, tries to
+ * maintain keystore integity by writing the files
+ * out in a particular order, minimizing the time period
+ * that the keystore is in an indeterminate state.
+ *
+ * With the current implementation, there are some failures
+ * that are wholly unrecoverable, such as disk corruption.
+ * These routines attempt to minimize the risk, but not
+ * eliminate it.  When better, atomic operations are available
+ * (such as a true database with commit, rollback, and
+ * guaranteed atomicity), this implementation should use that.
+ *
+ *
+ * Arguments:
+ * err - Error object to add errors to
+ * dest - Destination filename
+ * contents - Contents to write to the file
+ * Returns:
+ *   0 - Success - Keystore contents are written out to
+ *   the destination.
+ * non-zero - Failure, errors and reasons recorded in err
+ */
+static boolean_t
+write_keystore_file(PKG_ERR *err, char *dest, PKCS12 *contents)
+{
+	FILE	*newfile = NULL;
+	boolean_t	ret = B_TRUE;
+	char	newpath[MAXPATHLEN];
+	char	backuppath[MAXPATHLEN];
+	struct stat buf;
+	int fd;
+
+	(void) snprintf(newpath, MAXPATHLEN, "%s.new", dest);
+	(void) snprintf(backuppath, MAXPATHLEN, "%s.bak", dest);
+
+	if ((fd = open(newpath, O_CREAT|O_EXCL|O_WRONLY|O_NONBLOCK,
+	    S_IRUSR|S_IWUSR)) == -1) {
+		pkgerr_add(err, PKGERR_READ, gettext(ERR_KEYSTORE_OPEN),
+		    newpath, strerror(errno));
+		ret = B_FALSE;
+		goto cleanup;
+	}
+
+	if (fstat(fd, &buf) == -1) {
+		pkgerr_add(err, PKGERR_READ, gettext(ERR_KEYSTORE_OPEN),
+		    newpath, strerror(errno));
+		ret = B_FALSE;
+		goto cleanup;
+	}
+
+	if (!S_ISREG(buf.st_mode)) {
+		pkgerr_add(err, PKGERR_READ, gettext(ERR_NOT_REG),
+		    newpath);
+		ret = B_FALSE;
+		goto cleanup;
+	}
+
+	if ((newfile = fdopen(fd, "w")) == NULL) {
+		pkgerr_add(err, PKGERR_READ, gettext(ERR_KEYSTORE_OPEN),
+		    newpath, strerror(errno));
+		ret = B_FALSE;
+		goto cleanup;
+	}
+
+	if (i2d_PKCS12_fp(newfile, contents) == 0) {
+		pkgerr_add(err, PKGERR_WRITE, gettext(ERR_KEYSTORE_WRITE),
+		    newpath);
+		ret = B_FALSE;
+		goto cleanup;
+	}
+
+	/* flush, then close */
+	(void) fflush(newfile);
+	(void) fclose(newfile);
+	newfile = NULL;
+
+	/* now back up the original file */
+	(void) rename(dest, backuppath);
+
+	/* put new one in its place */
+	(void) rename(newpath, dest);
+
+	/* remove backup */
+	(void) remove(backuppath);
+
+cleanup:
+	if (newfile != NULL)
+		(void) fclose(newfile);
+	if (fd != -1)
+		(void) close(fd);
+
+	return (ret);
+}
+
+/*
+ * read_keystore_file - Reads single keystore file
+ * off disk in PKCS12 format.
+ *
+ * Arguments:
+ * err - Error object to add errors to
+ * file - File path to read
+ * Returns:
+ *   PKCS12 contents of file, or NULL if an error occurred.
+ *   errors recorded in 'err'.
+ */
+static PKCS12
+*read_keystore_file(PKG_ERR *err, char *file)
+{
+	int fd;
+	struct stat buf;
+	FILE *newfile;
+	PKCS12 *p12 = NULL;
+
+	if ((fd = open(file, O_RDONLY|O_NONBLOCK)) == -1) {
+		pkgerr_add(err, PKGERR_READ, gettext(ERR_KEYSTORE_OPEN),
+		    file, strerror(errno));
+		goto cleanup;
+	}
+
+	if (fstat(fd, &buf) == -1) {
+		pkgerr_add(err, PKGERR_READ, gettext(ERR_KEYSTORE_OPEN),
+		    file, strerror(errno));
+		goto cleanup;
+	}
+
+	if (!S_ISREG(buf.st_mode)) {
+		pkgerr_add(err, PKGERR_READ, gettext(ERR_NOT_REG),
+		    file);
+		goto cleanup;
+	}
+
+	if ((newfile = fdopen(fd, "r")) == NULL) {
+		pkgerr_add(err, PKGERR_READ, gettext(ERR_KEYSTORE_OPEN),
+		    file, strerror(errno));
+		goto cleanup;
+	}
+
+	if ((p12 = d2i_PKCS12_fp(newfile, NULL)) == NULL) {
+		pkgerr_add(err, PKGERR_CORRUPT,
+		    gettext(ERR_KEYSTORE_CORRUPT), file);
+		goto cleanup;
+	}
+
+cleanup:
+	if (newfile != NULL)
+		(void) fclose(newfile);
+	if (fd != -1)
+		(void) close(fd);
+
+	return (p12);
+}
+
+
+/*
+ * Locks the specified file.
+ */
+static int
+file_lock(int fd, int type, int wait)
+{
+	struct flock lock;
+
+	lock.l_type = type;
+	lock.l_start = 0;
+	lock.l_whence = SEEK_SET;
+	lock.l_len = 0;
+
+	if (!wait) {
+		if (file_lock_test(fd, type)) {
+			/*
+			 * The caller would have to wait to get the
+			 * lock on this file.
+			 */
+			return (-1);
+		}
+	}
+
+	return (fcntl(fd, F_SETLKW, &lock));
+}
+
+/*
+ * Returns FALSE if the file is not locked; TRUE
+ * otherwise.
+ */
+static boolean_t
+file_lock_test(int fd, int type)
+{
+	struct flock lock;
+
+	lock.l_type = type;
+	lock.l_start = 0;
+	lock.l_whence = SEEK_SET;
+	lock.l_len = 0;
+
+	if (fcntl(fd, F_GETLK, &lock) != -1) {
+		if (lock.l_type != F_UNLCK) {
+			/*
+			 * The caller would have to wait to get the
+			 * lock on this file.
+			 */
+			return (B_TRUE);
+		}
+	}
+
+	/*
+	 * The file is not locked.
+	 */
+	return (B_FALSE);
+}
+
+/*
+ * Unlocks the specified file.
+ */
+static int
+file_unlock(int fd)
+{
+	struct flock lock;
+
+	lock.l_type = F_UNLCK;
+	lock.l_start = 0;
+	lock.l_whence = SEEK_SET;
+	lock.l_len = 0;
+
+	return (fcntl(fd, F_SETLK, &lock));
+}
+
+/*
+ * Determines if file has a length of 0 or not
+ */
+static boolean_t
+file_empty(char *path)
+{
+	struct stat	buf;
+
+	/* file is empty if size = 0 or it doesn't exist */
+	if (lstat(path, &buf) == 0) {
+		if (buf.st_size == 0) {
+			return (B_TRUE);
+		}
+	} else {
+		if (errno == ENOENT) {
+			return (B_TRUE);
+		}
+	}
+
+	return (B_FALSE);
+}
+
+/*
+ * Name:		get_time_string
+ * Description:	Generates a human-readable string from an ASN1_TIME
+ *
+ * Arguments:	intime - The time to convert
+ *
+ * Returns :	A pointer to a static string representing the passed-in time.
+ */
+static char
+*get_time_string(ASN1_TIME *intime)
+{
+
+	static char	time[ATTR_MAX];
+	BIO		*mem;
+	char	*p;
+
+	if (intime == NULL) {
+		return (NULL);
+	}
+	if ((mem = BIO_new(BIO_s_mem())) == NULL) {
+		return (NULL);
+	}
+
+	if (ASN1_TIME_print(mem, intime) == 0) {
+		(void) BIO_free(mem);
+		return (NULL);
+	}
+
+	if (BIO_gets(mem, time, ATTR_MAX) <= 0) {
+		(void) BIO_free(mem);
+		return (NULL);
+	}
+
+	(void) BIO_free(mem);
+
+	/* trim the end of the string */
+	for (p = time + strlen(time) - 1; isspace(*p); p--) {
+		*p = '\0';
+	}
+
+	return (time);
+}
+
+/*
+ * check_password - do various password checks to see if the current password
+ *                  will work or we need to prompt for a new one.
+ *
+ * Arguments:
+ *   pass   - password to check
+ *
+ * Returns:
+ *   B_TRUE  - Password is OK.
+ *   B_FALSE - Password not valid.
+ */
+static boolean_t
+check_password(PKCS12 *p12, char *pass)
+{
+	boolean_t ret = B_TRUE;
+
+	/*
+	 * If password is zero length or NULL then try verifying both cases
+	 * to determine which password is correct. The reason for this is that
+	 * under PKCS#12 password based encryption no password and a zero
+	 * length password are two different things...
+	 */
+
+	/* Check the mac */
+	if (pass == NULL || *pass == '\0') {
+		if (PKCS12_verify_mac(p12, NULL, 0) == 0 &&
+		    PKCS12_verify_mac(p12, "", 0) == 0)
+			ret = B_FALSE;
+	} else if (PKCS12_verify_mac(p12, pass, -1) == 0) {
+		ret = B_FALSE;
+	}
+	return (ret);
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/usr/src/lib/libpkg/common/keystore.h	Fri Jun 05 10:28:40 2009 -0400
@@ -0,0 +1,145 @@
+/*
+ * 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
+ * or http://www.opensolaris.org/os/licensing.
+ * 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 2003 Sun Microsystems, Inc.  All rights reserved.
+ * Use is subject to license terms.
+ */
+
+#ifndef _KEYSTORE_H
+#define	_KEYSTORE_H
+
+
+/*
+ * Module:	keystore.h
+ * Description:	This module contains the structure definitions for processing
+ *		package keystore files.
+ */
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#include <openssl/evp.h>
+#include <openssl/x509.h>
+#include "pkgerr.h"
+
+/* keystore structures */
+
+/* this opaque type represents a keystore */
+typedef void *keystore_handle_t;
+
+/* flags passed to open_keystore */
+
+/* opens keystore read-only.  Attempts to modify results in an error */
+#define	KEYSTORE_ACCESS_READONLY	0x00000001L
+
+/* opens keystore read-write */
+#define	KEYSTORE_ACCESS_READWRITE	0x00000002L
+
+/*
+ * tells open_keystore to fall back to app-generic paths in the case that
+ * the app-specific paths do not exist.
+ */
+#define	KEYSTORE_PATH_SOFT		0x00000010L
+
+/*
+ * tells open_keystore to use the app-specific paths no matter what,
+ * failing if they cannot be used for any reason.
+ */
+#define	KEYSTORE_PATH_HARD		0x00000020L
+
+/* masks off various types of flags */
+#define	KEYSTORE_ACCESS_MASK		0x0000000FL
+#define	KEYSTORE_PATH_MASK		0x000000F0L
+
+/* default is read-only, soft */
+#define	KEYSTORE_DFLT_FLAGS \
+		(KEYSTORE_ACCESS_READONLY|KEYSTORE_PATH_SOFT)
+
+/*
+ * possible encoding formats used by the library, used
+ * by print_cert
+ */
+typedef enum {
+	KEYSTORE_FORMAT_PEM,
+	KEYSTORE_FORMAT_DER,
+	KEYSTORE_FORMAT_TEXT
+} keystore_encoding_format_t;
+
+/*
+ * structure passed back to password callback for determining how
+ * to prompt for passphrase, and where to record errors
+ */
+typedef struct {
+	PKG_ERR	*err;
+} keystore_passphrase_data;
+
+
+/* max length of a passphrase.  One could use a short story! */
+#define	KEYSTORE_PASS_MAX	1024
+
+/* callback for collecting passphrase when open_keystore() is called */
+typedef int keystore_passphrase_cb(char *, int, int, void *);
+
+/* names of the individual files within the keystore path */
+#define	TRUSTSTORE		"truststore"
+#define	KEYSTORE		"keystore"
+#define	CERTSTORE		"certstore"
+
+/* keystore.c */
+extern int		open_keystore(PKG_ERR *, char *, char *,
+    keystore_passphrase_cb, long flags, keystore_handle_t *);
+
+extern int		print_certs(PKG_ERR *, keystore_handle_t, char *,
+    keystore_encoding_format_t, FILE *);
+
+extern int		check_cert(PKG_ERR *, X509 *);
+
+extern int		check_cert_and_key(PKG_ERR *, X509 *, EVP_PKEY *);
+
+extern int		print_cert(PKG_ERR *, X509 *,
+    keystore_encoding_format_t, char *, boolean_t, FILE *);
+
+extern int		close_keystore(PKG_ERR *, keystore_handle_t,
+    keystore_passphrase_cb);
+
+extern int		merge_ca_cert(PKG_ERR *, X509 *, keystore_handle_t);
+extern int		merge_cert_and_key(PKG_ERR *, X509 *, EVP_PKEY *,
+    char *, keystore_handle_t);
+
+extern int		delete_cert_and_keys(PKG_ERR *, keystore_handle_t,
+    char *);
+
+extern int		find_key_cert_pair(PKG_ERR *, keystore_handle_t,
+    char *, EVP_PKEY **, X509 **);
+
+extern int		find_ca_certs(PKG_ERR *, keystore_handle_t,
+    STACK_OF(X509) **);
+
+extern int		find_cl_certs(PKG_ERR *, keystore_handle_t,
+    STACK_OF(X509) **);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* _KEYSTORE_H */
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/usr/src/lib/libpkg/common/llib-lpkg	Fri Jun 05 10:28:40 2009 -0400
@@ -0,0 +1,36 @@
+/*
+ * 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
+ * or http://www.opensolaris.org/os/licensing.
+ * 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 2009 Sun Microsystems, Inc.  All rights reserved.
+ * Use is subject to license terms.
+ */
+
+
+/* LINTLIBRARY */
+/* PROTOLIB1 */
+#include <cfext.h>
+#include <keystore.h>
+#include <p12lib.h>
+#include <pkgerr.h>
+#include <pkglib.h>
+#include <pkglocale.h>
+#include <pkgweb.h>
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/usr/src/lib/libpkg/common/logerr.c	Fri Jun 05 10:28:40 2009 -0400
@@ -0,0 +1,72 @@
+/*
+ * 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
+ * or http://www.opensolaris.org/os/licensing.
+ * 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 2009 Sun Microsystems, Inc.  All rights reserved.
+ * Use is subject to license terms.
+ */
+
+/* Copyright (c) 1984, 1986, 1987, 1988, 1989 AT&T */
+/* All Rights Reserved */
+
+
+
+#include <stdio.h>
+#include <string.h>
+#include <stdarg.h>
+#include "pkglocale.h"
+
+/*VARARGS*/
+void
+logerr(char *fmt, ...)
+{
+	va_list ap;
+	char	*pt, buffer[2048];
+	int	flag;
+	char	*estr = pkg_gt("ERROR:");
+	char	*wstr = pkg_gt("WARNING:");
+	char	*nstr = pkg_gt("NOTE:");
+
+	va_start(ap, fmt);
+	flag = 0;
+	/* This may have to use the i18n strcmp() routines. */
+	if (strncmp(fmt, estr, strlen(estr)) &&
+	    strncmp(fmt, wstr, strlen(wstr)) &&
+	    strncmp(fmt, nstr, strlen(nstr))) {
+		flag++;
+		(void) fprintf(stderr, "    ");
+	}
+	/*
+	 * NOTE: internationalization in next line REQUIRES that caller of
+	 * this routine be in the same internationalization domain
+	 * as this library.
+	 */
+	(void) vsprintf(buffer, fmt, ap);
+
+	va_end(ap);
+
+	for (pt = buffer; *pt; pt++) {
+		(void) putc(*pt, stderr);
+		if (flag && (*pt == '\n') && pt[1])
+			(void) fprintf(stderr, "    ");
+	}
+	(void) putc('\n', stderr);
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/usr/src/lib/libpkg/common/mapfile-vers	Fri Jun 05 10:28:40 2009 -0400
@@ -0,0 +1,223 @@
+#
+# 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
+# or http://www.opensolaris.org/os/licensing.
+# 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 2009 Sun Microsystems, Inc.  All rights reserved.
+# Use is subject to license terms.
+#
+
+#
+# MAPFILE HEADER START
+#
+# WARNING:  STOP NOW.  DO NOT MODIFY THIS FILE.
+# Object versioning must comply with the rules detailed in
+#
+#	usr/src/lib/README.mapfiles
+#
+# You should not be making modifications here until you've read the most current
+# copy of that file. If you need help, contact a gatekeeper for guidance.
+#
+# MAPFILE HEADER END
+#
+
+SUNWprivate {
+    global:
+	add_cache;
+	attrdefault;
+	attrpreset;
+	averify;
+	backoff;
+	basepath;
+	canonize;
+	canonize_slashes;
+	cgrgid;
+	cgrnam;
+	check_cert;
+	check_cert_and_key;
+	checksum_off;
+	checksum_on;
+	ckparam;
+	ckvolseq;
+	clgrgid;
+	clgrnam;
+	close_keystore;
+	clpwnam;
+	clpwuid;
+	compute_checksum;
+	cpwnam;
+	cpwuid;
+	cverify;
+	delete_cert_and_keys;
+	devtype;
+	disable_attribute_check;
+	ds_close;
+	ds_fd_open;
+	ds_findpkg;
+	ds_getinfo;
+	ds_getpkg;
+	ds_ginit;
+	ds_init;
+	ds_next;
+	ds_order;
+	ds_putinfo;
+	ds_readbuf;
+	ds_skiptoend;
+	ds_validate_signature;
+	e_ExecCmdArray;
+	e_ExecCmdList;
+	echo_out;
+	ecleanup;
+	enable_local_fs;
+	epclose;
+	epopen;
+	esystem;
+	find_ca_certs;
+	find_cl_certs;
+	find_key_cert_pair;
+	fmkdir;
+	fverify;
+	getErrbufAddr;
+	getErrbufSize;
+	getErrstr;
+	get_categories;
+	get_cert_chain;
+	get_disable_attribute_check;
+	get_endof_string;
+	get_prog_name;
+	get_proxy_port;
+	get_signature;
+	get_startof_string;
+	get_subject_display_name;
+	getmapmode;
+	gpkglist;
+	gpkgmap;
+	gpkgmapvfp;
+	holdcinfo;
+	init_cache;
+	isFdRemote;
+	isFstypeRemote;
+	isPathRemote;
+	is_not_valid_category;
+	is_not_valid_length;
+	is_web_install;
+	iscpio;
+	isdir;
+	isfile;
+	logerr;
+	lookup_cache;
+	mappath;
+	mapvar;
+	merge_ca_cert;
+	merge_cert_and_key;
+	nonABI_symlinks;
+	open_keystore;
+	path_valid;
+	pkg_passphrase_cb;
+	pkgalias;
+	pkgerr;
+	pkgerr_add;
+	pkgerr_clear;
+	pkgerr_dump;
+	pkgerr_free;
+	pkgerr_get;
+	pkgerr_new;
+	pkgerr_num;
+	pkgexecl;
+	pkgexecv;
+	pkghead;
+	pkglist_cont;
+	pkgmount;
+	pkgstrAddToken;
+	pkgstrContainsToken;
+	pkgstrConvertPathToBasename;
+	pkgstrConvertPathToDirname;
+	pkgstrConvertUllToTimeString_r;
+	pkgstrExpandTokens;
+	pkgstrGetToken;
+	pkgstrGetToken_r;
+	pkgstrLocatePathBasename;
+	pkgstrNumTokens;
+	pkgstrPrintf;
+	pkgstrPrintf_r;
+	pkgstrRemoveLeadingWhitespace;
+	pkgstrRemoveToken;
+	pkgstrScaleNumericString;
+	pkgtrans;
+	pkgumount;
+	ppkgmap;
+	print_cert;
+	print_certs;
+	progerr;
+	putcfile;
+	putcvfpfile;
+	reset_backoff;
+	restore_local_fs;
+	rpterr;
+	rrmdir;
+	sec_init;
+	setErrstr;
+	set_memalloc_failure_func;
+	set_nonABI_symlinks;
+	set_passphrase_passarg;
+	set_passphrase_prompt;
+	set_prog_name;
+	set_web_install;
+	setmapmode;
+	srchcfile;
+	strip_port;
+	sunw_PKCS12_contents;
+	sunw_PKCS12_create;
+	sunw_check_cert_times;
+	sunw_check_keys;
+	sunw_evp_pkey_free;
+	sunw_find_fname;
+	sunw_find_localkeyid;
+	sunw_get_cert_fname;
+	sunw_get_pkey_fname;
+	sunw_get_pkey_localkeyid;
+	sunw_set_fname;
+	sunw_set_localkeyid;
+	sunw_split_certs;
+	sunw_PEM_contents;
+	tputcfent;
+	validate_signature;
+	vfpCheckpointFile;
+	vfpCheckpointOpen;
+	vfpClearModified;
+	vfpClose;
+	vfpGetModified;
+	vfpOpen;
+	vfpRewind;
+	vfpSafePwrite;
+	vfpSafeWrite;
+	vfpSetFlags;
+	vfpSetModified;
+	vfpSetSize;
+	vfpTruncate;
+	vfpWriteToFile;
+	web_cleanup;
+	web_session_control;
+	xmalloc;
+	xrealloc;
+	xstrdup;
+    local:
+	*;
+};
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/usr/src/lib/libpkg/common/mappath.c	Fri Jun 05 10:28:40 2009 -0400
@@ -0,0 +1,236 @@
+/*
+ * 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
+ * or http://www.opensolaris.org/os/licensing.
+ * 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 2009 Sun Microsystems, Inc.  All rights reserved.
+ * Use is subject to license terms.
+ */
+
+/* Copyright (c) 1984, 1986, 1987, 1988, 1989 AT&T */
+/* All Rights Reserved */
+
+
+
+#include <limits.h>
+#include <string.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <ctype.h>
+
+/* 0 = both upper and lower case */
+/* 1 = initial lower case only (build variables) */
+/* 2 = initial upper case only (install variables) */
+#define	mode(flag, pt)	(!flag || ((flag == 1) && islower(pt[1])) || \
+			((flag == 2) && isupper(pt[1])))
+
+/*
+ * For next and last functions below, values indicate whether resolution
+ * was possible.
+ *
+ *	0 = all OK - the variable resolved within the established parameters
+ *		or it wasn't time for the variable to bind.
+ *	1 = parameter did not resolve because there was no value in the
+ *		environment or because it was a build variable at install
+ *		time.
+ */
+
+/*
+ * This gets a raw path which may contain shell variables and returns in path
+ * a pathname with all appropriate parameters resolved. If it comes in
+ * relative, it goes out relative.
+ */
+int
+mappath(int flag, char *path)
+{
+	char buffer[PATH_MAX];
+	char varname[64];
+	char *npt, *pt, *pt2, *copy;
+	char *token;
+	int retvalue = 0;
+
+	copy = buffer;
+
+	/*
+	 * For each "/" separated token. If the token contains an environment
+	 * variable, then evaluate the variable and insert it into path.
+	 */
+	for (pt = path; *pt; /* void */) {
+		/*
+		 * If this is a token and it's an environment variable
+		 * properly situated in the path...
+		 */
+		if ((*pt == '$') && isalpha(pt[1]) &&
+		    ((pt == path) || (pt[-1] == '/'))) {
+			/* ... and it's the right time to evaluate it... */
+			if (mode(flag, pt)) {
+				/* replace the parameter with its value. */
+				pt2 = varname;
+				for (npt = pt+1; *npt && (*npt != '/');
+				    /* void */)
+					*pt2++ = *npt++;
+				*pt2 = '\0';
+				/*
+				 * At this point EVERY token should evaluate
+				 * to a value. If it doesn't, there's an
+				 * error.
+				 */
+				if ((token = getenv(varname)) != NULL &&
+				    *token != NULL) {
+					/* copy in parameter value */
+					while (*token)
+						*copy++ = *token++;
+					pt = npt;
+				} else {
+					retvalue = 1;
+					*copy++ = *pt++;
+				}
+			/*
+			 * If evaluate time is wrong, determine of this is an
+			 * error.
+			 */
+			} else {
+				if (flag == 2) {	/* install-time. */
+					/*
+					 * ALL variables MUST evaluate at
+					 * install time.
+					 */
+					*copy++ = *pt++;
+					retvalue = 1;
+				} else if (flag == 1 &&	/* build-time */
+				    islower(pt[1])) {
+					/*
+					 * All build-time variables must
+					 * evaluate at build time.
+					 */
+					retvalue = 1;
+					*copy++ = *pt++;
+				} else	/* no problem. */
+					*copy++ = *pt++;
+			}
+		/*
+		 * If it's a separator, copy it over to the target buffer and
+		 * move to the start of the next token.
+		 */
+		} else if (*pt == '/') {
+			while (pt[1] == '/')
+				pt++;
+			if ((pt[1] == '\0') && (pt > path))
+				break;
+			*copy++ = *pt++;
+		/*
+		 * If we're in the middle of a non-parametric token, copy
+		 * that character over and try the next character.
+		 */
+		} else
+			*copy++ = *pt++;
+	}
+	*copy = '\0';
+	(void) strcpy(path, buffer);
+	return (retvalue);
+}
+
+/*
+ * This function resolves the path into an absolute path referred to
+ * an install root of ir.
+ */
+void
+basepath(char *path, char *basedir, char *ir)
+{
+	char buffer[PATH_MAX];
+
+	/* For a relative path, prepend the basedir */
+	if (*path != '/') {
+		(void) strcpy(buffer, path);
+		if (ir && *ir) {
+			while (*ir)
+				*path++ = *ir++;
+			if (path[-1] == '/')
+				path--;
+		}
+		if (basedir && *basedir) {
+			if (ir && *ir && *basedir != '/')
+				*path++ = '/';
+			while (*basedir)
+				*path++ = *basedir++;
+			if (path[-1] == '/')
+				path--;
+		}
+		*path++ = '/';
+		(void) strcpy(path, buffer);
+
+	/* For an absolute path, just prepend the install root */
+	} else {
+		if (ir && *ir) {
+			(void) strcpy(buffer, path);
+			while (*ir)
+				*path++ = *ir++;
+			if (path[-1] == '/')
+				path--;
+			(void) strcpy(path, buffer);
+		}
+	}
+}
+
+/*
+ * Evaluate varname and return with environment variables resolved.
+ * NOTE: This assumes that varname is a buffer long enough to hold the
+ * evaluated string.
+ */
+int
+mapvar(int flag, char *varname)
+{
+	char	*token;
+	int retvalue = 0;
+
+	/* If its a parametric entry beginning with an alpha character. */
+	if (*varname == '$' && isalpha(varname[1])) {
+		/* ...and it's the right time to evaluate it... */
+		if (mode(flag, varname)) {
+			/*
+			 * then it MUST be possible to evaluate it. If not,
+			 * there's an error.
+			 */
+			if (((token = getenv(&varname[1])) != NULL) &&
+			    *token) {
+				/* copy token into varname */
+				while (*token)
+					*varname++ = *token++;
+				*varname = '\0';
+			} else
+				retvalue = 1;
+		} else {
+			if (flag == 2) /* install-time. */
+				/*
+				 * ALL variables MUST evaluate at install
+				 * time.
+				 */
+				retvalue = 1;
+			else if (flag == 1 &&	/* build-time */
+			    islower(varname[1]))
+				/*
+				 * all build-time variables must evaluate at
+				 * build time.
+				 */
+				retvalue = 1;
+		}
+	}
+	return (retvalue);
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/usr/src/lib/libpkg/common/ncgrpw.c	Fri Jun 05 10:28:40 2009 -0400
@@ -0,0 +1,740 @@
+/*
+ * 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
+ * or http://www.opensolaris.org/os/licensing.
+ * 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 2009 Sun Microsystems, Inc.  All rights reserved.
+ * Use is subject to license terms.
+ */
+
+
+/*
+ * This module fetches group and passwd structs for the caller. It
+ * uses a hash table to speed up retrieval of repeated entries. If
+ * the attempts to initialize the hash tables fail, this just
+ * continues the slow way.
+ */
+
+#include <pwd.h>
+#include <grp.h>
+#include <string.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include "pkglib.h"
+#include "pkglocale.h"
+#include "nhash.h"
+
+#define	HASHSIZE	151
+#define	BSZ		4
+
+#define	ERR_DUPFAIL	"%s: strdup(%s) failed.\n"
+#define	ERR_ADDFAIL	"%s: add_cache() failed.\n"
+#define	ERR_BADMEMB	"%s: %s in \"%s\" %s structure is invalid.\n"
+#define	ERR_NOGRP	"dup_gr_ent(): no group entry provided.\n"
+#define	ERR_NOPWD	"dup_pw_ent(): no passwd entry provided.\n"
+#define	ERR_NOINIT	"%s: init_cache() failed.\n"
+#define	ERR_MALLOC	"%s: malloc(%d) failed for %s.\n"
+
+static Cache *pwnam_cache = (Cache *) NULL;
+static Cache *grnam_cache = (Cache *) NULL;
+static Cache *pwuid_cache = (Cache *) NULL;
+static Cache *grgid_cache = (Cache *) NULL;
+
+static int dup_gr_ent(struct group *grp);
+static int dup_pw_ent(struct passwd *pwp);
+
+/*
+ * These indicate whether the hash table has been initialized for the four
+ * categories.
+ */
+static int is_a_pwnam_cache;
+static int is_a_grnam_cache;
+static int is_a_pwuid_cache;
+static int is_a_grgid_cache;
+
+extern char *get_install_root(void);
+
+/*
+ * If there's a grnam cache, then update it with this new
+ * group, otherwise, skip it.
+ */
+static Item *
+cache_alloc(char *fname, int len, size_t struct_size)
+{
+	Item *itemp;
+
+	/*
+	 * Allocate space for the Item pointer, key and data.
+	 */
+	if ((itemp = (Item *) malloc(sizeof (*itemp))) ==
+	    Null_Item) {
+		(void) fprintf(stderr,
+		    pkg_gt(ERR_MALLOC), fname,
+		    sizeof (*itemp), "itemp");
+	} else if ((itemp->key = (char *)malloc(len)) == NULL) {
+		(void) fprintf(stderr, pkg_gt(ERR_MALLOC), fname, len,
+		    "itemp->key");
+		free(itemp);
+	} else if ((itemp->data = malloc(struct_size)) == NULL) {
+		(void) fprintf(stderr, pkg_gt(ERR_MALLOC), fname,
+		    struct_size, "itemp->data");
+		free(itemp->key);
+		free(itemp);
+	} else {
+		/* Set length parameters. */
+		itemp->keyl = len;
+		itemp->datal = struct_size;
+
+		return (itemp);
+	}
+
+	return ((Item *) NULL);
+}
+
+/* Get the required group structure based upon the group name. */
+struct group *
+cgrnam(char *nam)
+{
+	struct group *grp;
+	Item *itemp;
+	int len;
+	static int cache_failed;
+
+	/* Attempt to initialize the grname cache. */
+	if (!is_a_grnam_cache && !cache_failed) {
+		if (init_cache(&grnam_cache, HASHSIZE, BSZ,
+		    (int (*)())NULL, (int (*)())NULL) == -1) {
+			(void) fprintf(stderr, pkg_gt(ERR_NOINIT), "cgrnam()");
+			grnam_cache = (Cache *) NULL;
+			cache_failed = 1;
+		} else
+			is_a_grnam_cache = 1;
+	}
+
+	len = strlen(nam) + 1;
+
+	/* First look in the cache. Failing that, do it the hard way. */
+	if ((itemp = lookup_cache(grnam_cache, nam, len)) == Null_Item) {
+
+		/* Get the group by name. */
+		if ((grp = clgrnam(nam)) != NULL ||
+				(grp = getgrnam(nam)) != NULL) {
+			/* A group by that name exists on this machine. */
+			if (dup_gr_ent(grp))
+				/*
+				 * Effectively no such group since struct
+				 * couldn't be duplicated.
+				 */
+				grp = (struct group *)NULL;
+			/*
+			 * If there's a grnam cache, then update it with this
+			 * new group, otherwise, skip it.
+			 */
+			else if (is_a_grnam_cache) {
+				if ((itemp = cache_alloc("cgrnam()", len,
+				    sizeof (struct group))) != Null_Item) {
+					/*
+					 * With that allocated, insert the
+					 * group name as key and set the key
+					 * length.
+					 */
+					(void) memmove(itemp->key, nam, len);
+
+					/*
+					 * Insert the data associated with
+					 * the key and the data length.
+					 */
+					(void) memmove(itemp->data, grp,
+					    sizeof (struct group));
+
+					/* Insert the Item into the cache. */
+					if (add_cache(grnam_cache, itemp) == -1)
+						(void) fprintf(stderr,
+						    pkg_gt(ERR_ADDFAIL),
+						    "cgrnam()");
+				}
+			}
+		}
+		return (grp);
+	} else	/* Found it in the cache. */
+		return ((struct group *)itemp->data);
+}
+
+struct passwd *
+cpwnam(char *nam)
+{
+	struct passwd *pwd;
+	Item *itemp;
+	int len;
+	static int cache_failed;
+
+	if (!is_a_pwnam_cache && !cache_failed) {
+		if (init_cache(&pwnam_cache, HASHSIZE, BSZ,
+		    (int (*)())NULL, (int (*)())NULL) == -1) {
+			(void) fprintf(stderr, pkg_gt(ERR_NOINIT), "cpwnam()");
+			pwnam_cache = (Cache *) NULL;
+			cache_failed = 1;
+		} else
+			is_a_pwnam_cache = 1;
+	}
+
+	len = strlen(nam) + 1;
+
+	/* First look in the cache. Failing that, do it the hard way. */
+	if ((itemp = lookup_cache(pwnam_cache, nam, len)) == Null_Item) {
+
+		/* Get the passwd by name. */
+		if ((pwd = clpwnam(nam)) != NULL ||
+				(pwd = getpwnam(nam)) != NULL) {
+			/* A passwd by that name exists on this machine. */
+			if (dup_pw_ent(pwd))
+				/*
+				 * Effectively no such passwd since struct
+				 * couldn't be duplicated.
+				 */
+				pwd = (struct passwd *)NULL;
+			/*
+			 * If there's a pwnam cache, then update it with this
+			 * new passwd, otherwise, skip it.
+			 */
+			else if (is_a_pwnam_cache) {
+				/*
+				 * Allocate space for the Item pointer, key
+				 * and data.
+				 */
+				if ((itemp = cache_alloc("cpwnam()", len,
+				    sizeof (struct passwd))) != Null_Item) {
+					/*
+					 * With that allocated, insert the
+					 * group name as key and set the key
+					 * length.
+					 */
+					(void) memmove(itemp->key, nam, len);
+
+					/*
+					 * Insert the data associated with
+					 * the key and the data length.
+					 */
+					(void) memmove(itemp->data, pwd,
+					    sizeof (struct passwd));
+
+					if (add_cache(pwnam_cache, itemp) == -1)
+						(void) fprintf(stderr,
+						    pkg_gt(ERR_ADDFAIL),
+						    "cpwnam()");
+				}
+			}
+		}
+		return (pwd);
+	} else	/* Found it in the cache. */
+		return ((struct passwd *)itemp->data);
+}
+
+static int
+uid_hash(void *datap, int datalen, int hsz)
+{
+#ifdef lint
+	int i = datalen;
+	datalen = i;
+#endif	/* lint */
+
+	return (*((uid_t *)datap) % hsz);
+}
+
+static int
+uid_comp(void *datap1, void *datap2, int datalen)
+{
+#ifdef lint
+	int i = datalen;
+	datalen = i;
+#endif	/* lint */
+
+	return (*((uid_t *)datap1) - *((uid_t *)datap2));
+}
+
+struct group *
+cgrgid(gid_t gid)
+{
+	struct group *grp;
+	Item *itemp;
+	int len;
+	static int cache_failed;
+
+	if (!is_a_grgid_cache && !cache_failed) {
+		if (init_cache(&grgid_cache, HASHSIZE, BSZ,
+		    uid_hash, uid_comp) == -1) {
+			(void) fprintf(stderr, pkg_gt(ERR_NOINIT), "cgrgid()");
+			grgid_cache = (Cache *) NULL;
+			cache_failed = 1;
+		} else
+			is_a_grgid_cache = 1;
+	}
+
+	len = sizeof (uid_t);
+
+	/* First look in the cache. Failing that, do it the hard way. */
+	if ((itemp = lookup_cache(grgid_cache, &gid, len)) == Null_Item) {
+		if ((grp = clgrgid(gid)) != NULL ||
+				(grp = getgrgid(gid)) != NULL) {
+			/* A group by that number exists on this machine. */
+			if (dup_gr_ent(grp))
+				/*
+				 * Effectively no such group since struct
+				 * couldn't be duplicated.
+				 */
+				grp = (struct group *)NULL;
+			/*
+			 * If there's a grnam cache, then update it with this
+			 * new group, otherwise, skip it.
+			 */
+			else if (is_a_grgid_cache) {
+				if ((itemp = cache_alloc("cgrgid()", len,
+				    sizeof (struct group))) != Null_Item) {
+					/*
+					 * With that allocated, insert the
+					 * group name as key and set the key
+					 * length.
+					 */
+					(void) memmove(itemp->key, &gid, len);
+
+					/*
+					 * Insert the data associated with
+					 * the key and the data length.
+					 */
+					(void) memmove(itemp->data, grp,
+					    sizeof (struct group));
+
+					if (add_cache(grgid_cache, itemp) == -1)
+						(void) fprintf(stderr,
+						    pkg_gt(ERR_ADDFAIL),
+						    "cgrgid()");
+				}
+			}
+		}
+		return (grp);
+	} else	/* Found it in the cache. */
+		return ((struct group *)itemp->data);
+}
+
+struct passwd *
+cpwuid(uid_t uid)
+{
+	struct passwd *pwd;
+	Item *itemp;
+	int len;
+	static int cache_failed;
+
+	if (!is_a_pwuid_cache && !cache_failed) {
+		if (init_cache(&pwuid_cache, HASHSIZE, BSZ,
+		    uid_hash, uid_comp) == -1) {
+			(void) fprintf(stderr,
+			    pkg_gt(ERR_NOINIT), "cpwuid()");
+			pwuid_cache = (Cache *) NULL;
+			cache_failed = 1;
+		} else
+			is_a_pwuid_cache = 1;
+	}
+
+	len = sizeof (uid_t);
+
+	/* First look in the cache. Failing that, do it the hard way. */
+	if ((itemp = lookup_cache(pwuid_cache, &uid, len)) == Null_Item) {
+
+		/* Get the passwd by number. */
+		if ((pwd = clpwuid(uid)) != NULL ||
+				(pwd = getpwuid(uid)) != NULL) {
+			/* A passwd by that user ID exists on this machine. */
+			if (dup_pw_ent(pwd))
+				/*
+				 * Effectively no such passwd since struct
+				 * couldn't be duplicated.
+				 */
+				pwd = (struct passwd *)NULL;
+			/*
+			 * If there's a pwuid cache, then update it with this
+			 * new passwd, otherwise, skip it.
+			 */
+			else if (is_a_pwuid_cache) {
+				if ((itemp = cache_alloc("cpwuid()", len,
+				    sizeof (struct passwd))) != Null_Item) {
+					/*
+					 * With that allocated, insert the
+					 * group name as key and set the key
+					 * length.
+					 */
+					(void) memmove(itemp->key, &uid, len);
+
+					/*
+					 * Insert the data associated with
+					 * the key and the data length.
+					 */
+					(void) memmove(itemp->data, pwd,
+					    sizeof (struct passwd));
+
+					if (add_cache(pwuid_cache, itemp) == -1)
+						(void) fprintf(stderr,
+						    pkg_gt(ERR_ADDFAIL),
+						    "cpwuid()");
+				}
+			}
+		}
+		return (pwd);
+	} else	/* Found it in the cache. */
+		return ((struct passwd *)itemp->data);
+}
+
+/*
+ * This function duplicates the group structure provided from kernel static
+ * memory. There is a lot of defensive coding here because there have been
+ * problems with the getgr*() functions. They will sometimes provide NULL
+ * values instead of pointers to NULL values. There has been no explanation
+ * for the reason behind this; but, this function takes a NULL to be an
+ * invalid (char *) and returns an error.
+ */
+static int
+dup_gr_ent(struct group *grp)
+{
+	char **tp = NULL;
+	char **memp = NULL;
+	int	nent = 0;	/* Number of entries in the member list. */
+
+	if (grp) {
+		if (grp->gr_name == NULL) {
+			(void) fprintf(stderr,
+			    pkg_gt(ERR_BADMEMB), "dup_gr_ent()", "gr_name",
+			    "unknown", "group");
+			return (-1);
+		} else if ((grp->gr_name = strdup(grp->gr_name)) == NULL) {
+			(void) fprintf(stderr,
+			    pkg_gt(ERR_DUPFAIL), "dup_gr_ent()", "gr_name");
+			return (-1);
+		}
+		if (grp->gr_passwd == NULL) {
+			(void) fprintf(stderr,
+			    pkg_gt(ERR_BADMEMB), "dup_gr_ent()", "gr_passwd",
+			    grp->gr_name, "group");
+			return (-1);
+		} else if ((grp->gr_passwd = strdup(grp->gr_passwd)) == NULL) {
+			(void) fprintf(stderr,
+			    pkg_gt(ERR_DUPFAIL), "dup_gr_ent()", "gr_passwd");
+			return (-1);
+		}
+		/*
+		 * Allocate space for the member list and move the members
+		 * into it.
+		 */
+		if (grp->gr_mem) {
+			/*
+			 * First count the members. The nent variable will be
+			 * the number of members + 1 for the terminator.
+			 */
+			for (tp = grp->gr_mem; *tp; nent++, tp++);
+
+			/* Now allocate room for the pointers. */
+			memp = malloc(sizeof (char **)* (nent+1));
+
+			if (memp == NULL) {
+				(void) fprintf(stderr,
+				    pkg_gt(ERR_MALLOC), "dup_gr_ent()",
+				    (sizeof (char **)* (nent+1)),
+				    "memp");
+				return (-1);
+			}
+
+			/*
+			 * Now copy over the pointers and entries. It should
+			 * be noted that if the structure is messed up here,
+			 * the resulting member list will be truncated at the
+			 * NULL entry.
+			 */
+			for (nent = 0, tp = grp->gr_mem; *tp; tp++) {
+				if ((memp[nent++] = strdup(*tp)) == NULL) {
+					(void) fprintf(stderr,
+					    pkg_gt(ERR_DUPFAIL), "dup_gr_ent()",
+					    "gr_mem");
+					return (-1);
+				}
+			}
+		} else {
+			(void) fprintf(stderr,
+			    pkg_gt(ERR_BADMEMB), "dup_gr_ent()", "gr_mem",
+			    grp->gr_name, "group");
+			return (-1);
+		}
+	} else {
+		(void) fprintf(stderr, pkg_gt(ERR_NOGRP));
+		return (-1);
+	}
+	memp[nent++] = '\0';
+	return (0);
+}
+
+/*
+ * This function duplicates the passwd structure provided from kernel static
+ * memory. As in the above function, since there have been problems with the
+ * getpw*() functions, the structure provided is rigorously scrubbed. This
+ * function takes a NULL to be an invalid (char *) and returns an error if
+ * one is detected.
+ */
+static int
+dup_pw_ent(struct passwd *pwd)
+{
+	if (pwd) {
+		if (pwd->pw_name == NULL) {
+			(void) fprintf(stderr,
+			    pkg_gt(ERR_BADMEMB), "dup_pw_ent()", "pw_name",
+			    "unknown", "passwd");
+			return (-1);
+		} else if ((pwd->pw_name = strdup(pwd->pw_name)) == NULL) {
+			(void) fprintf(stderr,
+			    pkg_gt(ERR_DUPFAIL), "dup_pw_ent()", "pw_name");
+			return (-1);
+		}
+
+		if (pwd->pw_passwd == NULL) {
+			(void) fprintf(stderr,
+			    pkg_gt(ERR_BADMEMB), "dup_pw_ent()", "pw_passwd",
+			    pwd->pw_name, "passwd");
+			return (-1);
+		} else if ((pwd->pw_passwd = strdup(pwd->pw_passwd)) == NULL) {
+			(void) fprintf(stderr,
+			    pkg_gt(ERR_DUPFAIL), "dup_pw_ent()", "pw_passwd");
+			return (-1);
+		}
+
+		if (pwd->pw_age == NULL) {
+			(void) fprintf(stderr,
+			    pkg_gt(ERR_BADMEMB), "dup_pw_ent()", "pw_age",
+			    pwd->pw_name, "passwd");
+			return (-1);
+		} else if ((pwd->pw_age = strdup(pwd->pw_age)) == NULL) {
+			(void) fprintf(stderr,
+			    pkg_gt(ERR_DUPFAIL), "dup_pw_ent()", "pw_age");
+			return (-1);
+		}
+
+		if (pwd->pw_comment == NULL) {
+			(void) fprintf(stderr,
+			    pkg_gt(ERR_BADMEMB), "dup_pw_ent()", "pw_comment",
+			    pwd->pw_name, "passwd");
+			return (-1);
+		} else if ((pwd->pw_comment = strdup(pwd->pw_comment)) ==
+		    NULL) {
+			(void) fprintf(stderr,
+			    pkg_gt(ERR_DUPFAIL), "dup_pw_ent()", "pw_comment");
+			return (-1);
+		}
+
+		if (pwd->pw_gecos == NULL) {
+			(void) fprintf(stderr,
+			    pkg_gt(ERR_BADMEMB), "dup_pw_ent()", "pw_gecos",
+			    pwd->pw_name, "passwd");
+			return (-1);
+		} else if ((pwd->pw_gecos = strdup(pwd->pw_gecos)) == NULL) {
+			(void) fprintf(stderr,
+			    pkg_gt(ERR_DUPFAIL), "dup_pw_ent()", "pw_gecos");
+			return (-1);
+		}
+
+		if (pwd->pw_dir == NULL) {
+			(void) fprintf(stderr,
+			    pkg_gt(ERR_BADMEMB), "dup_pw_ent()", "pw_dir",
+			    pwd->pw_name, "passwd");
+			return (-1);
+		} else if ((pwd->pw_dir = strdup(pwd->pw_dir)) == NULL) {
+			(void) fprintf(stderr,
+			    pkg_gt(ERR_DUPFAIL), "dup_pw_ent()", "pw_dir");
+			return (-1);
+		}
+
+		if (pwd->pw_shell == NULL) {
+			(void) fprintf(stderr,
+			    pkg_gt(ERR_BADMEMB), "dup_pw_ent()", "pw_shell",
+			    pwd->pw_name, "passwd");
+			return (-1);
+		} else if ((pwd->pw_shell = strdup(pwd->pw_shell)) == NULL) {
+			(void) fprintf(stderr,
+			    pkg_gt(ERR_DUPFAIL), "dup_pw_ent()", "pw_shell");
+			return (-1);
+		}
+	} else {
+		(void) fprintf(stderr, pkg_gt(ERR_NOPWD));
+		return (-1);
+	}
+
+	return (0);
+}
+
+/*
+ * Check the client's etc/group file for the group name
+ *
+ * returns a pointer to the group structure if the group is found
+ * returns NULL if not found
+ */
+struct group *
+clgrnam(char *nam)
+{
+	struct group *gr;
+	char *instroot, *buf;
+	FILE *gr_ptr;
+
+	if ((instroot = get_install_root()) != NULL) {
+		if ((buf = (char *)malloc(strlen(instroot) +
+			strlen(GROUP) + 1)) == NULL) {
+			(void) fprintf(stderr,
+				pkg_gt(ERR_MALLOC), "clgrnam()",
+				strlen(instroot) + strlen(GROUP), "buf");
+		}
+		(void) sprintf(buf, "%s%s", instroot, GROUP);
+		if ((gr_ptr = fopen(buf, "r")) == NULL) {
+			free(buf);
+			return (NULL);
+		} else {
+			while ((gr = fgetgrent(gr_ptr)) != NULL) {
+				if (strcmp(gr->gr_name, nam) == 0) {
+					break;
+				}
+			}
+		}
+		free(buf);
+		(void) fclose(gr_ptr);
+		return (gr);
+	} else {
+		return (NULL);
+	}
+}
+
+/*
+ * Check the client's etc/passwd file for the user name
+ *
+ * returns a pointer to the passwd structure if the passwd is found
+ * returns NULL if not found
+ */
+struct passwd *
+clpwnam(char *nam)
+{
+	struct passwd *pw;
+	char *instroot, *buf;
+	FILE *pw_ptr;
+
+	if ((instroot = get_install_root()) != NULL) {
+		if ((buf = (char *)malloc(strlen(instroot) +
+			strlen(PASSWD) + 1)) == NULL) {
+			(void) fprintf(stderr,
+				pkg_gt(ERR_MALLOC), "clpwnam()",
+				strlen(instroot) + strlen(PASSWD), "buf");
+		}
+		(void) sprintf(buf, "%s%s", instroot, PASSWD);
+		if ((pw_ptr = fopen(buf, "r")) == NULL) {
+			free(buf);
+			return (NULL);
+		} else {
+			while ((pw = fgetpwent(pw_ptr)) != NULL) {
+				if (strcmp(pw->pw_name, nam) == 0) {
+					break;
+				}
+			}
+		}
+		free(buf);
+		(void) fclose(pw_ptr);
+		return (pw);
+	} else {
+		return (NULL);
+	}
+}
+
+/*
+ * Check the client's etc/group file for the group id
+ *
+ * returns a pointer to the group structure if the group id is found
+ * returns NULL if not found
+ */
+struct group *
+clgrgid(gid_t gid)
+{
+	struct group *gr;
+	char *instroot, *buf;
+	FILE *gr_ptr;
+
+	if ((instroot = get_install_root()) != NULL) {
+		if ((buf = (char *)malloc(strlen(instroot) +
+			strlen(GROUP) + 1)) == NULL) {
+			(void) fprintf(stderr,
+				pkg_gt(ERR_MALLOC), "clgrgid()",
+				strlen(instroot) + strlen(GROUP), "buf");
+		}
+		(void) sprintf(buf, "%s%s", instroot, GROUP);
+		if ((gr_ptr = fopen(buf, "r")) == NULL) {
+			free(buf);
+			return (NULL);
+		} else {
+			while ((gr = fgetgrent(gr_ptr)) != NULL) {
+				if (gr->gr_gid == gid) {
+					break;
+				}
+			}
+		}
+		free(buf);
+		(void) fclose(gr_ptr);
+		return (gr);
+	} else {
+		return (NULL);
+	}
+}
+
+/*
+ * Check the client's etc/passwd file for the user id
+ *
+ * returns a pointer to the passwd structure if the user id is found
+ * returns NULL if not found
+ */
+struct passwd *
+clpwuid(uid_t uid)
+{
+	struct passwd *pw;
+	char *instroot, *buf;
+	FILE *pw_ptr;
+
+	if ((instroot = get_install_root()) != NULL) {
+		if ((buf = (char *)malloc(strlen(instroot) +
+			strlen(PASSWD) + 1)) == NULL) {
+			(void) fprintf(stderr,
+				pkg_gt(ERR_MALLOC), "clpwuid()",
+				strlen(instroot) + strlen(PASSWD), "buf");
+		}
+		(void) sprintf(buf, "%s%s", instroot, PASSWD);
+		if ((pw_ptr = fopen(buf, "r")) == NULL) {
+			free(buf);
+			return (NULL);
+		} else {
+			while ((pw = fgetpwent(pw_ptr)) != NULL) {
+				if (pw->pw_uid == uid) {
+					break;
+				}
+			}
+		}
+		free(buf);
+		(void) fclose(pw_ptr);
+		return (pw);
+	} else {
+		return (NULL);
+	}
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/usr/src/lib/libpkg/common/nhash.c	Fri Jun 05 10:28:40 2009 -0400
@@ -0,0 +1,182 @@
+/*
+ * 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
+ * or http://www.opensolaris.org/os/licensing.
+ * 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 2009 Sun Microsystems, Inc.  All rights reserved.
+ * Use is subject to license terms.
+ */
+
+
+#include <stdio.h>
+#include <string.h>
+#include <stdlib.h>
+#include <strings.h>
+#include "pkglib.h"
+#include "nhash.h"
+#include "pkglocale.h"
+
+#ifndef _KERNEL
+#define	bcopy(a, b, c)	(void) memmove(b, a, c)
+#define	bcmp		memcmp
+#define	bzero(a, c)	(void) memset(a, '\0', c)
+#else	/* _KERNEL */
+#define	malloc		bkmem_alloc
+#endif	/* _KERNEL */
+
+#define	VERIFY_HASH_REALLOC
+
+static int
+BCMP(void *str1, void *str2, int len)
+{
+	return (bcmp((char *)str1, (char *)str2, len));
+}
+
+static int
+HASH(void *datap, int datalen, int hsz)
+{
+	char	*cp;
+	char	*np;
+	int	hv = 0;
+
+	/* determine starting and ending positions */
+
+	cp = (char *)datap;
+	np =  ((char *)cp + datalen);
+
+	/* compute hash over all characters from start to end */
+
+	while (cp != np) {
+		hv += ((int)*cp++);
+	}
+
+	/* return computed hash */
+
+	return (hv % hsz);
+}
+
+int
+init_cache(Cache **cp, int hsz, int bsz,
+	    int (*hfunc)(void *, int, int), int (*cfunc)(void *, void *, int))
+{
+	if ((*cp = (Cache *) malloc(sizeof (**cp))) == NULL) {
+		(void) fprintf(stderr, pkg_gt("malloc(Cache **cp)"));
+		return (-1);
+	}
+	if (((*cp)->bp =
+	    (Bucket *) malloc(sizeof (*(*cp)->bp) * hsz)) == NULL) {
+		(void) fprintf(stderr, pkg_gt("malloc(Bucket cp->bp)"));
+		return (-1);
+	}
+
+	(*cp)->hsz = hsz;
+	(*cp)->bsz = bsz;
+
+	bzero((*cp)->bp, sizeof (*(*cp)->bp) * hsz);
+
+	if (hfunc != (int (*)()) NULL) {
+		(*cp)->hfunc = hfunc;
+	} else {
+		(*cp)->hfunc = HASH;
+	}
+
+	if (cfunc != (int (*)()) NULL) {
+		(*cp)->cfunc = cfunc;
+	} else {
+		(*cp)->cfunc = BCMP;
+	}
+	return (0);
+}
+
+int
+add_cache(Cache *cp, Item *itemp)
+{
+	Bucket *bp;
+	Item **titempp;
+
+	/*
+	 * If cp is NULL, then init_cache() wasn't called. Quietly return the
+	 * error code and let the caller deal with it.
+	 */
+	if (cp == NULL)
+		return (-1);
+
+	bp = &cp->bp[(*cp->hfunc)(itemp->key, itemp->keyl, cp->hsz)];
+	if (bp->nent >= bp->nalloc) {
+		if (bp->nalloc == 0) {
+			bp->itempp =
+			    (Item **) malloc(sizeof (*bp->itempp) * cp->bsz);
+		} else {
+#ifdef	VERIFY_HASH_REALLOC
+			(void) fprintf(stderr,
+			    pkg_gt("realloc(%d) bucket=%d\n"),
+			    bp->nalloc + cp->bsz,
+			    (*cp->hfunc)(itemp->key, itemp->keyl, cp->hsz));
+#endif	/* VERIFY_HASH_REALLOC */
+			if ((titempp =
+			    (Item **) malloc(sizeof (*bp->itempp) *
+			    (bp->nalloc + cp->bsz))) != NULL) {
+				bcopy((char *)bp->itempp, (char *)titempp,
+				    (sizeof (*bp->itempp) * bp->nalloc));
+#ifdef _KERNEL
+				bkmem_free(bp->itempp,
+					(sizeof (*bp->itempp) * bp->nalloc));
+#else	/* !_KERNEL */
+				free(bp->itempp);
+#endif	/* _KERNEL */
+				bp->itempp = titempp;
+			} else
+				bp->itempp = NULL;
+		}
+		if (bp->itempp == NULL) {
+			(void) fprintf(stderr,
+			    pkg_gt("add_cache(): out of memory\n"));
+			return (-1);
+		}
+		bp->nalloc += cp->bsz;
+	}
+	bp->itempp[bp->nent] = itemp;
+	bp->nent++;
+	return (0);
+}
+
+Item *
+lookup_cache(Cache *cp, void *datap, int datalen)
+{
+	int	i;
+	Bucket *bp;
+
+	/*
+	 * If cp is NULL, then init_cache() wasn't called. Quietly return the
+	 * error code and let the caller deal with it.
+	 */
+	if (cp == NULL) {
+	    return (Null_Item);
+	}
+
+	bp = &cp->bp[(*cp->hfunc)(datap, datalen, cp->hsz)];
+
+	for (i = 0; i < bp->nent; i++) {
+		if (!(*cp->cfunc)((void *)bp->itempp[i]->key, datap, datalen)) {
+			return (bp->itempp[i]);
+		}
+	}
+	return (Null_Item);
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/usr/src/lib/libpkg/common/nhash.h	Fri Jun 05 10:28:40 2009 -0400
@@ -0,0 +1,75 @@
+/*
+ * 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
+ * or http://www.opensolaris.org/os/licensing.
+ * 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 2003 Sun Microsystems, Inc.  All rights reserved.
+ * Use is subject to license terms.
+ */
+
+#ifndef _NHASH_H
+#define	_NHASH_H
+
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#ifndef NULL
+#define	NULL	0
+#endif	/* NULL */
+
+typedef struct item_t {
+    void *key;
+    int	  keyl;
+    void *data;
+    int	  datal;
+} Item;
+
+#define	Null_Item ((Item *) NULL)
+
+typedef struct bucket_t {
+	int nent;
+	int nalloc;
+	Item **itempp;
+} Bucket;
+
+typedef struct cache_t {
+	int	hsz;
+	int	bsz;
+	Bucket *bp;
+	int (*hfunc)(void *, int, int);
+	int (*cfunc)(void *, void *, int);
+} Cache;
+
+#ifdef _KERNEL
+#define	malloc	bkmem_alloc
+#endif	/* _KERNEL */
+
+extern int init_cache(Cache **cp, int hsz, int bsz,
+	    int (*hfunc)(void *, int, int), int (*cfunc)(void *, void *, int));
+extern int add_cache(Cache *cp, Item *itemp);
+extern Item *lookup_cache(Cache *cp, void *datap, int datalen);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* _NHASH_H */
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/usr/src/lib/libpkg/common/p12lib.c	Fri Jun 05 10:28:40 2009 -0400
@@ -0,0 +1,2786 @@
+/*
+ * ====================================================================
+ * Copyright (c) 1999 The OpenSSL Project.  All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in
+ *    the documentation and/or other materials provided with the
+ *    distribution.
+ *
+ * 3. All advertising materials mentioning features or use of this
+ *    software must display the following acknowledgment:
+ *    "This product includes software developed by the OpenSSL Project
+ *    for use in the OpenSSL Toolkit. (http://www.OpenSSL.org/)"
+ *
+ * 4. The names "OpenSSL Toolkit" and "OpenSSL Project" must not be used to
+ *    endorse or promote products derived from this software without
+ *    prior written permission. For written permission, please contact
+ *    licensing@OpenSSL.org.
+ *
+ * 5. Products derived from this software may not be called "OpenSSL"
+ *    nor may "OpenSSL" appear in their names without prior written
+ *    permission of the OpenSSL Project.
+ *
+ * 6. Redistributions of any form whatsoever must retain the following
+ *    acknowledgment:
+ *    "This product includes software developed by the OpenSSL Project
+ *    for use in the OpenSSL Toolkit (http://www.OpenSSL.org/)"
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE OpenSSL PROJECT ``AS IS'' AND ANY
+ * EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE OpenSSL PROJECT OR
+ * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+ * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
+ * OF THE POSSIBILITY OF SUCH DAMAGE.
+ * ====================================================================
+ *
+ * This product includes cryptographic software written by Eric Young
+ * (eay@cryptsoft.com).  This product includes software written by Tim
+ * Hudson (tjh@cryptsoft.com).
+ *
+ */
+
+/*
+ * Copyright 2003 Sun Microsystems, Inc.  All rights reserved.
+ * Use is subject to license terms.
+ */
+
+
+#include <strings.h>
+#include <stdlib.h>
+#include <assert.h>
+
+#include <openssl/crypto.h>
+#include <openssl/err.h>
+#include <openssl/x509.h>
+#include <openssl/pem.h>
+
+#include <openssl/pkcs12.h>
+#include "p12lib.h"
+
+/*
+ * OpenSSL provides a framework for pushing error codes onto a stack.
+ * When an error occurs, the consumer may use the framework to
+ * pop the errors off the stack and provide a trace of where the
+ * errors occurred.
+ *
+ * Our PKCS12 code plugs into this framework by calling
+ * ERR_load_SUNW_strings(). To push an error (which by the way, consists
+ * of a function code and an error code) onto the stack our PKCS12 code
+ * calls SUNWerr().
+ *
+ * Consumers of our PKCS12 code can then call the OpenSSL error routines
+ * when an error occurs and retrieve the stack of errors.
+ */
+
+#ifndef OPENSSL_NO_ERR
+
+/* Function codes and their matching strings */
+static ERR_STRING_DATA SUNW_str_functs[] = {
+	{ ERR_PACK(0, SUNW_F_USE_X509CERT, 0),	   "sunw_use_x509cert" },
+	{ ERR_PACK(0, SUNW_F_USE_PKEY, 0),	   "sunw_use_pkey" },
+	{ ERR_PACK(0, SUNW_F_USE_TASTORE, 0),	   "sunw_use_tastore" },
+	{ ERR_PACK(0, SUNW_F_USE_CERTFILE, 0),	   "sunw_p12_use_certfile" },
+	{ ERR_PACK(0, SUNW_F_USE_KEYFILE, 0),	   "sunw_p12_use_keyfile" },
+	{ ERR_PACK(0, SUNW_F_USE_TRUSTFILE, 0),	   "sunw_p12_use_trustfile" },
+	{ ERR_PACK(0, SUNW_F_READ_FILE, 0),	   "p12_read_file" },
+	{ ERR_PACK(0, SUNW_F_DOPARSE, 0),	   "p12_doparse" },
+	{ ERR_PACK(0, SUNW_F_PKCS12_PARSE, 0),	   "sunw_PKCS12_parse" },
+	{ ERR_PACK(0, SUNW_F_PKCS12_CONTENTS, 0),  "sunw_PKCS12_contents" },
+	{ ERR_PACK(0, SUNW_F_PARSE_ONE_BAG, 0),	   "parse_one_bag" },
+	{ ERR_PACK(0, SUNW_F_PKCS12_CREATE, 0),	   "sunw_PKCS12_create" },
+	{ ERR_PACK(0, SUNW_F_SPLIT_CERTS, 0),	   "sunw_split_certs" },
+	{ ERR_PACK(0, SUNW_F_FIND_LOCALKEYID, 0),  "sunw_find_localkeyid" },
+	{ ERR_PACK(0, SUNW_F_SET_LOCALKEYID, 0),   "sunw_set_localkeyid" },
+	{ ERR_PACK(0, SUNW_F_GET_LOCALKEYID, 0),   "sunw_get_localkeyid" },
+	{ ERR_PACK(0, SUNW_F_SET_FNAME, 0),	   "sunw_set_fname" },
+	{ ERR_PACK(0, SUNW_F_GET_PKEY_FNAME, 0),   "sunw_get_pkey_fname" },
+	{ ERR_PACK(0, SUNW_F_APPEND_KEYS, 0),	   "sunw_append_keys" },
+	{ ERR_PACK(0, SUNW_F_PEM_CONTENTS, 0),	   "sunw_PEM_contents" },
+	{ ERR_PACK(0, SUNW_F_PEM_INFO, 0),	   "pem_info" },
+	{ ERR_PACK(0, SUNW_F_ASC2BMPSTRING, 0),	   "asc2bmpstring" },
+	{ ERR_PACK(0, SUNW_F_UTF82ASCSTR, 0),	   "utf82ascstr" },
+	{ ERR_PACK(0, SUNW_F_FINDATTR, 0),	   "findattr" },
+	{ ERR_PACK(0, SUNW_F_TYPE2ATTRIB, 0),	   "type2attrib" },
+	{ ERR_PACK(0, SUNW_F_MOVE_CERTS, 0),	   "move_certs" },
+	{ ERR_PACK(0, SUNW_F_FIND_FNAME, 0),	   "sunw_find_fname" },
+	{ ERR_PACK(0, SUNW_F_PARSE_OUTER, 0),	   "parse_outer" },
+	{ ERR_PACK(0, SUNW_F_CHECKFILE, 0),	   "checkfile" },
+	{ 0, NULL }
+};
+
+/* Error codes and their matching strings */
+static ERR_STRING_DATA SUNW_str_reasons[] = {
+	{ SUNW_R_INVALID_ARG,		"invalid argument" },
+	{ SUNW_R_MEMORY_FAILURE,	"memory failure" },
+	{ SUNW_R_MAC_VERIFY_FAILURE,	"mac verify failure" },
+	{ SUNW_R_MAC_CREATE_FAILURE,	"mac create failure" },
+	{ SUNW_R_BAD_FILETYPE,		"bad file type" },
+	{ SUNW_R_BAD_PKEY,		"bad or missing private key" },
+	{ SUNW_R_BAD_PKEYTYPE,		"unsupported key type" },
+	{ SUNW_R_PKEY_READ_ERR,		"unable to read private key" },
+	{ SUNW_R_NO_TRUST_ANCHOR,	"no trust anchors found" },
+	{ SUNW_R_READ_TRUST_ERR,	"unable to read trust anchor" },
+	{ SUNW_R_ADD_TRUST_ERR,		"unable to add trust anchor" },
+	{ SUNW_R_PKCS12_PARSE_ERR,	"PKCS12 parse error" },
+	{ SUNW_R_PKCS12_CREATE_ERR,	"PKCS12 create error" },
+	{ SUNW_R_BAD_CERTTYPE,		"unsupported certificate type" },
+	{ SUNW_R_PARSE_CERT_ERR,	"error parsing PKCS12 certificate" },
+	{ SUNW_R_PARSE_BAG_ERR,		"error parsing PKCS12 bag" },
+	{ SUNW_R_MAKE_BAG_ERR,		"error making PKCS12 bag" },
+	{ SUNW_R_BAD_LKID,		"bad localKeyID format" },
+	{ SUNW_R_SET_LKID_ERR,		"error setting localKeyID" },
+	{ SUNW_R_BAD_FNAME,		"bad friendlyName format" },
+	{ SUNW_R_SET_FNAME_ERR,		"error setting friendlyName" },
+	{ SUNW_R_BAD_TRUST,		"bad or missing trust anchor" },
+	{ SUNW_R_BAD_BAGTYPE,		"unsupported bag type" },
+	{ SUNW_R_CERT_ERR,		"certificate error" },
+	{ SUNW_R_PKEY_ERR,		"private key error" },
+	{ SUNW_R_READ_ERR,		"error reading file" },
+	{ SUNW_R_ADD_ATTR_ERR,		"error adding attribute" },
+	{ SUNW_R_STR_CONVERT_ERR,	"error converting string" },
+	{ SUNW_R_PKCS12_EMPTY_ERR,	"empty PKCS12 structure" },
+	{ SUNW_R_PASSWORD_ERR,		"bad password" },
+	{ 0, NULL }
+};
+
+/*
+ * The library name that our module will be known as. This name
+ * may be retrieved via OpenSSLs error APIs.
+ */
+static ERR_STRING_DATA SUNW_lib_name[] = {
+	{ 0,	SUNW_LIB_NAME },
+	{ 0, NULL }
+};
+#endif
+
+/*
+ * The value of this variable (initialized by a call to
+ * ERR_load_SUNW_strings()) is what identifies our errors
+ * to OpenSSL as being ours.
+ */
+static int SUNW_lib_error_code = 0;
+
+/* local routines */
+static int	parse_pkcs12(PKCS12 *, const char *, int, char *, int, char *,
+    EVP_PKEY **, X509 **, STACK_OF(X509) **);
+static int	pem_info(FILE *, pem_password_cb, void *,
+    STACK_OF(EVP_PKEY) **, STACK_OF(X509) **);
+
+static int	parse_outer(PKCS12 *, const char *, STACK_OF(EVP_PKEY) *,
+    STACK_OF(X509) *);
+
+static int	parse_all_bags(STACK_OF(PKCS12_SAFEBAG) *, const char *,
+    STACK_OF(EVP_PKEY) *, STACK_OF(X509) *);
+
+static int	parse_one_bag(PKCS12_SAFEBAG *, const char *,
+    STACK_OF(EVP_PKEY) *, STACK_OF(X509) *);
+
+static X509_ATTRIBUTE	*type2attrib(ASN1_TYPE *, int);
+static ASN1_TYPE	*attrib2type(X509_ATTRIBUTE *);
+static uchar_t		*utf82ascstr(ASN1_UTF8STRING *);
+static ASN1_BMPSTRING	*asc2bmpstring(const char *, int);
+static int		find_attr_by_nid(STACK_OF(X509_ATTRIBUTE) *, int);
+static int		find_attr(int, ASN1_STRING *, STACK_OF(EVP_PKEY) *,
+    EVP_PKEY **, STACK_OF(X509) *, X509 **);
+
+static chk_errs_t	check_time(chk_actions_t, X509 *);
+static int		get_key_cert(int, STACK_OF(EVP_PKEY) *, EVP_PKEY **,
+    STACK_OF(X509) *, X509 **cert);
+static int		move_certs(STACK_OF(X509) *, STACK_OF(X509) *);
+static int		sunw_append_keys(STACK_OF(EVP_PKEY) *,
+    STACK_OF(EVP_PKEY) *);
+static int		set_results(STACK_OF(EVP_PKEY) **,
+    STACK_OF(EVP_PKEY) **, STACK_OF(X509) **, STACK_OF(X509) **,
+    STACK_OF(X509) **, STACK_OF(X509) **,
+    STACK_OF(EVP_PKEY) **, STACK_OF(EVP_PKEY) **);
+
+/*
+ * ----------------------------------------------------------------------------
+ * Public routines
+ * ----------------------------------------------------------------------------
+ */
+
+/*
+ * sunw_PKCS12_parse - Parse a PKCS12 structure and break it into its parts.
+ *
+ * Parse and decrypt a PKCS#12 structure returning user key, user cert and/or
+ * other (CA) certs. Note either ca should be NULL, *ca should be NULL,
+ * or it should point to a valid STACK_OF(X509) structure. pkey and cert can
+ * be passed uninitialized.
+ *
+ * Arguments:
+ *   p12      - Structure with pkcs12 info to be parsed
+ *   pass     - Pass phrase for the private key (possibly empty) or NULL if
+ *              there is none.
+ *   matchty  - Info about which certs/keys to return if many are in the file.
+ *   keyid    - If private key localkeyids friendlynames are to match a
+ *              predetermined value, the value to match. This value should
+ *		be an octet string.
+ *   keyid_len- Length of the keyid byte string.
+ *   name_str - If friendlynames are to match a predetermined value, the value
+ *		 to match. This value should be a NULL terminated string.
+ *   pkey     - Points to location pointing to the private key returned.
+ *   cert     - Points to locaiton which points to the client cert returned
+ *   ca       - Points to location that points to a stack of 'certificate
+ *               authority' certs/trust anchors.
+ *
+ * Match based on the value of 'matchty' and the contents of 'keyid'
+ * and/or 'name_str', as appropriate.  Go through the lists of certs and
+ * private keys which were taken from the pkcs12 structure, looking for
+ * matches of the requested type.  This function only searches the lists of
+ * matching private keys and client certificates.  Kinds of matches allowed,
+ * and the order in which they will be checked, are:
+ *
+ *   1) Find the key and/or cert whose localkeyid attributes matches
+ *      'keyid'.
+ *   2) Find the key and/or cert whose friendlyname attributes matches
+ *	'name_str'
+ *   3) Return the first matching key/cert pair found.
+ *   4) Return the last matching key/cert pair found.
+ *   5) Return whatever cert and/or key are available, even unmatching.
+ *
+ *   Append to the CA list, the certs which do not have matching private
+ *   keys and which were not selected.
+ *
+ * If none of the bits are set, no client certs or private keys will be
+ * returned.  CA (aka trust anchor) certs can be.
+ *
+ * Notes: If #3 is selected, then #4 will never occur.  CA certs will be
+ * selected after a cert/key pairs are isolated.
+ *
+ * Returns:
+ *  <  0 - An error returned.  Call ERR_get_error() to get errors information.
+ *         Where possible, memory has been freed.
+ *  >= 0 - Objects were found and returned.  Which objects are indicated by
+ *         which bits are set (FOUND_PKEY, FOUND_CERT, FOUND_CA_CERTS).
+ */
+int
+sunw_PKCS12_parse(PKCS12 *p12, const char *pass, int matchty, char *keyid,
+    int keyid_len, char *name_str, EVP_PKEY **pkey, X509 **cert,
+    STACK_OF(X509) **ca)
+{
+	boolean_t ca_supplied;
+	int retval = -1;
+
+	/* If NULL PKCS12 structure, this is an error */
+	if (p12 == NULL) {
+		SUNWerr(SUNW_F_PKCS12_PARSE, SUNW_R_INVALID_ARG);
+		return (-1);
+	}
+
+	/* Set up arguments....  These will be allocated if needed */
+	if (pkey)
+		*pkey = NULL;
+	if (cert)
+		*cert = NULL;
+
+	/*
+	 * If there is already a ca list, use it.  Otherwise, allocate one
+	 * and free is later if an error occurs or whatever.)
+	 */
+	ca_supplied = (ca != NULL && *ca != NULL);
+	if (ca != NULL && *ca == NULL) {
+		if ((*ca = sk_X509_new_null()) == NULL) {
+			SUNWerr(SUNW_F_PKCS12_PARSE, SUNW_R_MEMORY_FAILURE);
+			return (-1);
+		}
+	}
+
+	/*
+	 * If password is zero length or NULL then try verifying both cases
+	 * to determine which password is correct. The reason for this is that
+	 * under PKCS#12 password based encryption no password and a zero
+	 * length password are two different things. If the password has a
+	 * non-zero length and is not NULL then call PKCS12_verify_mac() with
+	 * a length of '-1' and let it use strlen() to figure out the length
+	 * of the password.
+	 */
+	/* Check the mac */
+	if (pass == NULL || *pass == '\0') {
+		if (PKCS12_verify_mac(p12, NULL, 0))
+			pass = NULL;
+		else if (PKCS12_verify_mac(p12, "", 0))
+			pass = "";
+		else {
+			SUNWerr(SUNW_F_PKCS12_PARSE,
+			    SUNW_R_MAC_VERIFY_FAILURE);
+			goto err;
+		}
+	} else if (PKCS12_verify_mac(p12, pass, -1) == 0) {
+		SUNWerr(SUNW_F_PKCS12_PARSE, SUNW_R_MAC_VERIFY_FAILURE);
+		goto err;
+	}
+
+	retval = parse_pkcs12(p12, pass, matchty, keyid, keyid_len,
+	    name_str, pkey, cert, ca);
+	if (retval < 0) {
+		SUNWerr(SUNW_F_PKCS12_PARSE, SUNW_R_PKCS12_PARSE_ERR);
+		goto err;
+	}
+	return (retval);
+
+err:
+	if (pkey && *pkey) {
+		sunw_evp_pkey_free(*pkey);
+	}
+	if (cert && *cert)
+		X509_free(*cert);
+	if (ca_supplied == B_FALSE && ca != NULL)
+		sk_X509_pop_free(*ca, X509_free);
+
+	return (-1);
+
+}
+
+
+/*
+ * sunw_PEM_contents() parses a PEM file and returns component parts found
+ *
+ * Parse and decrypt a PEM file, returning any user keys and certs.
+ *
+ * There are some limits to this function.  It will ignore the following:
+ * - certificates identified by "TRUSTED CERTIFICATE"
+ * - CERTIFICATE REQUEST and NEW CERTIFICATE REQUEST records.
+ * - X509 CRL
+ * - DH PARAMETERS
+ * - DSA PARAMETERS
+ * - Any PUBLIC KEY
+ * - PKCS7
+ * - PRIVATE KEY or ENCRYPTED PRIVATE KEY (PKCS 8)
+ *
+ * Arguments:
+ *   fp       - File pointer for file containing PEM data.
+ *   pass     - Pass phrase for the private key or NULL if there is none.
+ *   pkeys    - Points to address of a stack of private keys to return.
+ *   certs    - Points to address of a stack of client certs to return.
+ *
+ *   The pointers to stacks should either be NULL or their contents should
+ *   either be NULL or should point to a valid STACK_OF(X509) structure.
+ *   If the stacks contain information, corresponding information from the
+ *   file will be appended to the original contents.
+ *
+ *   Note:  Client certs and and their matching private keys will be in any
+ *   order.
+ *
+ *   Certs which have no matching private key are assumed to be ca certs.
+ *
+ * Returns:
+ *  <  0 - An error returned.  Call ERR_get_error() to get errors information.
+ *         Where possible, memory has been freed.
+ *  >= 0 - Objects were found and returned.  Which objects are indicated by
+ *         which bits are set (FOUND_PKEY, FOUND_CERT)
+ */
+int sunw_PEM_contents(FILE *fp, pem_password_cb *cb, void *userdata,
+    STACK_OF(EVP_PKEY) **pkey, STACK_OF(X509) **certs)
+{
+	STACK_OF(EVP_PKEY) *work_kl = NULL;
+	STACK_OF(X509) *work_ca = NULL;
+	int retval = -1;
+
+	/*
+	 * Allocate the working stacks for private key and for the
+	 * ca certs.
+	 */
+	if ((work_kl = sk_EVP_PKEY_new_null()) == NULL) {
+		SUNWerr(SUNW_F_PEM_CONTENTS, SUNW_R_MEMORY_FAILURE);
+		goto cleanup;
+	}
+
+	if ((work_ca = sk_X509_new_null()) == NULL) {
+		SUNWerr(SUNW_F_PEM_CONTENTS, SUNW_R_MEMORY_FAILURE);
+		goto cleanup;
+	}
+
+	/* Error strings are set within the following. */
+	if (pem_info(fp, cb, userdata, &work_kl, &work_ca) <= 0) {
+		goto cleanup;
+	}
+
+	/* on error, set_results() returns an error on the stack */
+	retval = set_results(pkey, &work_kl, certs, &work_ca, NULL, NULL, NULL,
+	    NULL);
+cleanup:
+	if (work_kl != NULL) {
+		sk_EVP_PKEY_pop_free(work_kl, sunw_evp_pkey_free);
+	}
+	if (work_ca != NULL)
+		sk_X509_pop_free(work_ca, X509_free);
+
+	return (retval);
+}
+
+
+/*
+ * sunw_PKCS12_contents() parses a pkcs#12 structure and returns component
+ *     parts found, without evaluation.
+ *
+ * Parse and decrypt a PKCS#12 structure returning any user keys and/or
+ * various certs. Note these should either be NULL, *whatever should
+ * be NULL, or it should point to a valid STACK_OF(X509) structure.
+ *
+ * Arguments:
+ *   p12      - Structure with pkcs12 info to be parsed
+ *   pass     - Pass phrase for the private key and entire pkcs12 wad (possibly
+ *              empty) or NULL if there is none.
+ *   pkeys    - Points to address of a stack of private keys to return.
+ *   certs    - Points to address of a stack of client certs return.
+ *
+ *   Note:  The certs and keys being returned are in random order.
+ *
+ * Returns:
+ *  <  0 - An error returned.  Call ERR_get_error() to get errors information.
+ *         Where possible, memory has been freed.
+ *  >= 0 - Objects were found and returned.  Which objects are indicated by
+ *         which bits are set (FOUND_PKEY or FOUND_CERT)
+ */
+int
+sunw_PKCS12_contents(PKCS12 *p12, const char *pass, STACK_OF(EVP_PKEY) **pkey,
+    STACK_OF(X509) **certs)
+{
+	STACK_OF(EVP_PKEY) *work_kl = NULL;
+	STACK_OF(X509) *work_ca = NULL;
+	int retval = -1;
+
+	/*
+	 * Allocate the working stacks for private key and for the
+	 * ca certs.
+	 */
+	if ((work_kl = sk_EVP_PKEY_new_null()) == NULL) {
+		SUNWerr(SUNW_F_PKCS12_CONTENTS, SUNW_R_MEMORY_FAILURE);
+		goto cleanup;
+	}
+
+	if ((work_ca = sk_X509_new_null()) == NULL) {
+		SUNWerr(SUNW_F_PKCS12_CONTENTS, SUNW_R_MEMORY_FAILURE);
+		goto cleanup;
+	}
+
+	if (parse_outer(p12, pass, work_kl, work_ca) == 0) {
+		/*
+		 * Error already on stack
+		 */
+		goto cleanup;
+	}
+
+	/* on error, set_results() returns an error on the stack */
+	retval = set_results(pkey, &work_kl, certs, &work_ca, NULL,
+	    NULL, NULL, NULL);
+
+cleanup:
+	if (work_kl != NULL) {
+		sk_EVP_PKEY_pop_free(work_kl, sunw_evp_pkey_free);
+	}
+
+	return (retval);
+}
+
+
+
+/*
+ * sunw_split_certs() - Given a list of certs and a list of private keys,
+ *     moves certs which match one of the keys to a different stack.
+ *
+ * Arguments:
+ *   allkeys  - Points to a stack of private keys to search.
+ *   allcerts - Points to a stack of certs to be searched.
+ *   keycerts - Points to address of a stack of certs with matching private
+ *              keys.  They are moved from 'allcerts'.  This may not be NULL
+ *              when called.  If *keycerts is NULL upon entry, a new stack will
+ *              be allocated.  Otherwise, it must be a valid STACK_OF(509).
+ *   nocerts  - Points to address of a stack for keys which have no matching
+ *              certs.  Keys are moved from 'allkeys' here when they have no
+ *              matching certs.  If this is NULL, matchless keys will be
+ *              discarded.
+ *
+ *   Notes:  If an error occurs while moving certs, the cert being move may be
+ *   lost.  'keycerts' may only contain part of the matching certs.  The number
+ *   of certs successfully moved can be found by checking sk_X509_num(keycerts).
+ *
+ *   If there is a key which does not have a matching cert, it is moved to
+ *   the list nocerts.
+ *
+ *   If all certs are removed from 'certs' and/or 'pkeys', it will be the
+ *   caller's responsibility to free the empty stacks.
+ *
+ * Returns:
+ *  <  0 - An error returned.  Call ERR_get_error() to get errors information.
+ *         Where possible, memory has been freed.
+ *  >= 0 - The number of certs moved from 'cert' to 'pkcerts'.
+ */
+int
+sunw_split_certs(STACK_OF(EVP_PKEY) *allkeys, STACK_OF(X509) *allcerts,
+    STACK_OF(X509) **keycerts, STACK_OF(EVP_PKEY) **nocerts)
+{
+	STACK_OF(X509) *matching;
+	STACK_OF(EVP_PKEY) *nomatch;
+	EVP_PKEY *tmpkey;
+	X509 *tmpcert;
+	int count = 0;
+	int found;
+	int res;
+	int i;
+	int k;
+
+	*keycerts = NULL;
+	if (nocerts != NULL)
+		*nocerts = NULL;
+	nomatch = NULL;
+
+	if ((matching = sk_X509_new_null()) == NULL) {
+		SUNWerr(SUNW_F_SPLIT_CERTS, SUNW_R_MEMORY_FAILURE);
+		return (-1);
+	}
+	*keycerts = matching;
+
+	k = 0;
+	while (k < sk_EVP_PKEY_num(allkeys)) {
+		found = 0;
+		tmpkey = sk_EVP_PKEY_value(allkeys, k);
+
+		for (i = 0; i < sk_X509_num(allcerts); i++) {
+			tmpcert = sk_X509_value(allcerts, i);
+			res = X509_check_private_key(tmpcert, tmpkey);
+			if (res != 0) {
+				count++;
+				found = 1;
+				tmpcert = sk_X509_delete(allcerts, i);
+				if (sk_X509_push(matching, tmpcert) == 0) {
+					X509_free(tmpcert);
+					SUNWerr(SUNW_F_SPLIT_CERTS,
+					    SUNW_R_MEMORY_FAILURE);
+					return (-1);
+				}
+				break;
+			}
+		}
+		if (found != 0) {
+			/*
+			 * Found a match - keep the key & check out the next
+			 * one.
+			 */
+			k++;
+		} else {
+			/*
+			 * No cert matching this key.  Move the key if
+			 * possible or discard it.  Don't increment the
+			 * index.
+			 */
+			if (nocerts == NULL) {
+				tmpkey = sk_EVP_PKEY_delete(allkeys, k);
+				sunw_evp_pkey_free(tmpkey);
+			} else {
+				if (*nocerts == NULL) {
+					nomatch = sk_EVP_PKEY_new_null();
+					if (nomatch == NULL) {
+						SUNWerr(SUNW_F_SPLIT_CERTS,
+						    SUNW_R_MEMORY_FAILURE);
+						return (-1);
+					}
+					*nocerts = nomatch;
+				}
+				tmpkey = sk_EVP_PKEY_delete(allkeys, k);
+				if (sk_EVP_PKEY_push(nomatch, tmpkey) == 0) {
+					sunw_evp_pkey_free(tmpkey);
+					SUNWerr(SUNW_F_SPLIT_CERTS,
+					    SUNW_R_MEMORY_FAILURE);
+					return (-1);
+				}
+			}
+		}
+	}
+
+	return (count);
+}
+
+/*
+ * sunw_PKCS12_create() creates a pkcs#12 structure and given component parts.
+ *
+ * Given one or more of user private key, user cert and/or other (CA) certs,
+ * return an encrypted PKCS12 structure containing them.
+ *
+ * Arguments:
+ *   pass     - Pass phrase for the pkcs12 structure and private key (possibly
+ *              empty) or NULL if there is none.  It will be used to encrypt
+ *              both the private key(s) and as the pass phrase for the whole
+ *              pkcs12 wad.
+ *   pkeys    - Points to stack of private keys.
+ *   certs    - Points to stack of client (public ke) certs
+ *   cacerts  - Points to stack of 'certificate authority' certs (or trust
+ *              anchors).
+ *
+ *   Note that any of these may be NULL.
+ *
+ * Returns:
+ *   NULL     - An error occurred.
+ *   != NULL  - Address of PKCS12 structure.  The user is responsible for
+ *              freeing the memory when done.
+ */
+PKCS12 *
+sunw_PKCS12_create(const char *pass, STACK_OF(EVP_PKEY) *pkeys,
+    STACK_OF(X509) *certs, STACK_OF(X509) *cacerts)
+{
+	int nid_cert = NID_pbe_WithSHA1And40BitRC2_CBC;
+	int nid_key = NID_pbe_WithSHA1And3_Key_TripleDES_CBC;
+	STACK_OF(PKCS12_SAFEBAG) *bags = NULL;
+	STACK_OF(PKCS7) *safes = NULL;
+	PKCS12_SAFEBAG *bag = NULL;
+	PKCS8_PRIV_KEY_INFO *p8 = NULL;
+	EVP_PKEY *pkey = NULL;
+	PKCS12 *ret_p12 = NULL;
+	PKCS12 *p12 = NULL;
+	PKCS7 *authsafe = NULL;
+	X509 *cert = NULL;
+	uchar_t *str = NULL;
+	int certs_there = 0;
+	int keys_there = 0;
+	int len;
+	int i;
+
+	if ((safes = sk_PKCS7_new_null()) == NULL) {
+		SUNWerr(SUNW_F_PKCS12_CREATE, SUNW_R_MEMORY_FAILURE);
+		return (NULL);
+	}
+
+	if ((bags = sk_PKCS12_SAFEBAG_new_null()) == NULL) {
+		SUNWerr(SUNW_F_PKCS12_CREATE, SUNW_R_MEMORY_FAILURE);
+		goto err_ret;
+	}
+
+	if (certs != NULL && sk_X509_num(certs) > 0) {
+
+		for (i = 0; i < sk_X509_num(certs); i++) {
+			cert = sk_X509_value(certs, i);
+
+			/* Add user certificate */
+			if ((bag = M_PKCS12_x5092certbag(cert)) == NULL) {
+				SUNWerr(SUNW_F_PKCS12_CREATE, SUNW_R_CERT_ERR);
+				goto err_ret;
+			}
+			if (cert->aux != NULL && cert->aux->alias != NULL &&
+			    cert->aux->alias->type == V_ASN1_UTF8STRING) {
+				str = utf82ascstr(cert->aux->alias);
+				if (str == NULL) {
+					/*
+					 * Error already on stack
+					 */
+					goto err_ret;
+				}
+				if (PKCS12_add_friendlyname_asc(bag,
+				    (char const *) str,
+				    strlen((char const *) str)) == 0) {
+					SUNWerr(SUNW_F_PKCS12_CREATE,
+					    SUNW_R_ADD_ATTR_ERR);
+					goto err_ret;
+				}
+			}
+			if (cert->aux != NULL && cert->aux->keyid != NULL &&
+			    cert->aux->keyid->type == V_ASN1_OCTET_STRING) {
+				str = cert->aux->keyid->data;
+				len = cert->aux->keyid->length;
+
+				if (str != NULL &&
+				    PKCS12_add_localkeyid(bag, str, len) == 0) {
+					SUNWerr(SUNW_F_PKCS12_CREATE,
+					    SUNW_R_ADD_ATTR_ERR);
+					goto err_ret;
+				}
+			}
+			if (sk_PKCS12_SAFEBAG_push(bags, bag) == 0) {
+				SUNWerr(SUNW_F_PKCS12_CREATE,
+				    SUNW_R_MEMORY_FAILURE);
+				goto err_ret;
+			}
+			certs_there++;
+			bag = NULL;
+		}
+	}
+
+	if (cacerts != NULL && sk_X509_num(cacerts) > 0) {
+
+		/* Put all certs in structure */
+		for (i = 0; i < sk_X509_num(cacerts); i++) {
+			cert = sk_X509_value(cacerts, i);
+			if ((bag = M_PKCS12_x5092certbag(cert)) == NULL) {
+				SUNWerr(SUNW_F_PKCS12_CREATE, SUNW_R_CERT_ERR);
+				goto err_ret;
+			}
+
+			if (cert->aux != NULL && cert->aux->alias != NULL &&
+			    cert->aux->alias->type == V_ASN1_UTF8STRING) {
+				str = utf82ascstr(cert->aux->alias);
+				if (str == NULL) {
+					/*
+					 * Error already on stack
+					 */
+					goto err_ret;
+				}
+				if (PKCS12_add_friendlyname_asc(
+				    bag, (char const *) str,
+				    strlen((char const *) str)) == 0) {
+					SUNWerr(SUNW_F_PKCS12_CREATE,
+					    SUNW_R_ADD_ATTR_ERR);
+					goto err_ret;
+				}
+			}
+			if (cert->aux != NULL && cert->aux->keyid != NULL &&
+			    cert->aux->keyid->type == V_ASN1_OCTET_STRING) {
+				str = cert->aux->keyid->data;
+				len = cert->aux->keyid->length;
+
+				if (str != NULL &&
+				    PKCS12_add_localkeyid(bag, str, len) == 0) {
+					SUNWerr(SUNW_F_PKCS12_CREATE,
+					    SUNW_R_ADD_ATTR_ERR);
+					goto err_ret;
+				}
+			}
+			if (sk_PKCS12_SAFEBAG_push(bags, bag) == 0) {
+				SUNWerr(SUNW_F_PKCS12_CREATE,
+				    SUNW_R_MEMORY_FAILURE);
+				goto err_ret;
+			}
+			certs_there++;
+			bag = NULL;
+		}
+	}
+
+	if (certs != NULL || cacerts != NULL && certs_there) {
+		/* Turn certbags into encrypted authsafe */
+		authsafe = PKCS12_pack_p7encdata(nid_cert, pass, -1,
+		    NULL, 0, PKCS12_DEFAULT_ITER, bags);
+		if (authsafe == NULL) {
+			SUNWerr(SUNW_F_PKCS12_CREATE, SUNW_R_CERT_ERR);
+			goto err_ret;
+		}
+		sk_PKCS12_SAFEBAG_pop_free(bags, PKCS12_SAFEBAG_free);
+		bags = NULL;
+
+		if (sk_PKCS7_push(safes, authsafe) == 0) {
+			SUNWerr(SUNW_F_PKCS12_CREATE, SUNW_R_MEMORY_FAILURE);
+			goto err_ret;
+		}
+		authsafe = NULL;
+	}
+
+	if (pkeys != NULL && sk_EVP_PKEY_num(pkeys) > 0) {
+
+		if (bags == NULL &&
+		    (bags = sk_PKCS12_SAFEBAG_new_null()) == NULL) {
+			SUNWerr(SUNW_F_PKCS12_CREATE, SUNW_R_MEMORY_FAILURE);
+			goto err_ret;
+		}
+
+		for (i = 0; i < sk_EVP_PKEY_num(pkeys); i++) {
+
+			pkey = sk_EVP_PKEY_value(pkeys, i);
+
+			/* Make a shrouded key bag */
+			if ((p8 = EVP_PKEY2PKCS8(pkey)) == NULL) {
+				SUNWerr(SUNW_F_PKCS12_CREATE, SUNW_R_PKEY_ERR);
+				goto err_ret;
+			}
+
+			bag = PKCS12_MAKE_SHKEYBAG(nid_key, pass, -1, NULL, 0,
+			    PKCS12_DEFAULT_ITER, p8);
+			if (bag == NULL) {
+				SUNWerr(SUNW_F_PKCS12_CREATE,
+				    SUNW_R_MAKE_BAG_ERR);
+				goto err_ret;
+			}
+			PKCS8_PRIV_KEY_INFO_free(p8);
+			p8 = NULL;
+
+			len = sunw_get_pkey_fname(GETDO_COPY, pkey,
+			    (char **)&str);
+			if (str != NULL) {
+				if (PKCS12_add_friendlyname_asc(bag,
+				    (const char *)str, len) == 0) {
+					SUNWerr(SUNW_F_PKCS12_CREATE,
+					    SUNW_R_ADD_ATTR_ERR);
+					goto err_ret;
+				}
+			}
+			str = NULL;
+
+			len = sunw_get_pkey_localkeyid(GETDO_COPY, pkey,
+			    (char **)&str, &len);
+			if (str != NULL) {
+				if (PKCS12_add_localkeyid(bag, str, len) == 0) {
+					SUNWerr(SUNW_F_PKCS12_CREATE,
+					    SUNW_R_ADD_ATTR_ERR);
+					goto err_ret;
+				}
+			}
+			str = NULL;
+
+			if (sk_PKCS12_SAFEBAG_push(bags, bag) == 0) {
+				SUNWerr(SUNW_F_PKCS12_CREATE,
+				    SUNW_R_MEMORY_FAILURE);
+				goto err_ret;
+			}
+			keys_there++;
+			bag = NULL;
+		}
+
+		if (keys_there) {
+			/* Turn into unencrypted authsafe */
+			authsafe = PKCS12_pack_p7data(bags);
+			if (authsafe == NULL) {
+				SUNWerr(SUNW_F_PKCS12_CREATE,
+				    SUNW_R_PKCS12_CREATE_ERR);
+				goto err_ret;
+			}
+			sk_PKCS12_SAFEBAG_pop_free(bags, PKCS12_SAFEBAG_free);
+			bags = NULL;
+
+			if (sk_PKCS7_push(safes, authsafe) == 0) {
+				SUNWerr(SUNW_F_PKCS12_CREATE,
+				    SUNW_R_MEMORY_FAILURE);
+			}
+			authsafe = NULL;
+		}
+	}
+
+	if (certs_there == 0 && keys_there == 0) {
+		SUNWerr(SUNW_F_PKCS12_CREATE, SUNW_R_PKCS12_EMPTY_ERR);
+		goto err_ret;
+	}
+
+	if ((p12 = PKCS12_init(NID_pkcs7_data)) == NULL) {
+		SUNWerr(SUNW_F_PKCS12_CREATE, SUNW_R_PKCS12_CREATE_ERR);
+		goto err_ret;
+	}
+
+	/*
+	 * Note that safes is copied by the following.  Therefore, it needs
+	 * to be freed whether or not the following succeeds.
+	 */
+	if (M_PKCS12_pack_authsafes(p12, safes) == 0) {
+		SUNWerr(SUNW_F_PKCS12_CREATE, SUNW_R_PKCS12_CREATE_ERR);
+		goto err_ret;
+	}
+	if (PKCS12_set_mac(p12, pass, -1, NULL, 0, 2048, NULL) == 0) {
+		SUNWerr(SUNW_F_PKCS12_CREATE, SUNW_R_MAC_CREATE_FAILURE);
+		goto err_ret;
+	}
+
+	ret_p12 = p12;
+	p12 = NULL;
+
+	/* Fallthrough is intentional */
+
+err_ret:
+
+	if (str != NULL)
+		free(str);
+
+	if (p8 != NULL)
+		PKCS8_PRIV_KEY_INFO_free(p8);
+
+	if (bag != NULL)
+		PKCS12_SAFEBAG_free(bag);
+	if (bags != NULL)
+		sk_PKCS12_SAFEBAG_pop_free(bags, PKCS12_SAFEBAG_free);
+	if (authsafe != NULL)
+		PKCS7_free(authsafe);
+	if (safes != NULL)
+		sk_PKCS7_pop_free(safes, PKCS7_free);
+	if (p12 != NULL)
+		PKCS12_free(p12);
+
+	return (ret_p12);
+}
+
+/*
+ * sunw_evp_pkey_free() Given an EVP_PKEY structure, free any attributes
+ *     that are attached.  Then free the EVP_PKEY itself.
+ *
+ *     This is a replacement for EVP_PKEY_free() for the sunw stuff.
+ *     It should be used in places where EVP_PKEY_free would be used,
+ *     including calls to sk_EVP_PKEY_pop_free().
+ *
+ * Arguments:
+ *   pkey     - Entry which potentially has attributes to be freed.
+ *
+ * Returns:
+ *   None.
+ */
+void
+sunw_evp_pkey_free(EVP_PKEY *pkey)
+{
+	if (pkey != NULL) {
+		if (pkey->attributes != NULL) {
+			sk_X509_ATTRIBUTE_pop_free(pkey->attributes,
+			    X509_ATTRIBUTE_free);
+			pkey->attributes = NULL;
+		}
+		EVP_PKEY_free(pkey);
+	}
+}
+
+/*
+ * sunw_set_localkeyid() sets the localkeyid in a cert, a private key or
+ *     both.  Any existing localkeyid will be discarded.
+ *
+ * Arguments:
+ *   keyid_str- A byte string with the localkeyid to set
+ *   keyid_len- Length of the keyid byte string.
+ *   pkey     - Points to a private key to set the keyidstr in.
+ *   cert     - Points to a cert to set the keyidstr in.
+ *
+ * Note that setting a keyid into a cert which will not be written out as
+ * a PKCS12 cert is pointless since it will be lost.
+ *
+ * Returns:
+ *   0        - Success.
+ *   < 0      - An error occurred.  It was probably an error in allocating
+ *              memory.  The error will be set in the error stack.  Call
+ *              ERR_get_error() to get specific information.
+ */
+int
+sunw_set_localkeyid(const char *keyid_str, int keyid_len, EVP_PKEY *pkey,
+    X509 *cert)
+{
+	X509_ATTRIBUTE *attr = NULL;
+	ASN1_STRING *str = NULL;
+	ASN1_TYPE *keyid = NULL;
+	int retval = -1;
+	int i;
+
+	if (cert != NULL) {
+		if (X509_keyid_set1(cert, (uchar_t *)keyid_str, keyid_len)
+		    == 0) {
+			SUNWerr(SUNW_F_SET_LOCALKEYID, SUNW_R_SET_LKID_ERR);
+			goto cleanup;
+		}
+	}
+	if (pkey != NULL) {
+		str = (ASN1_STRING *)M_ASN1_OCTET_STRING_new();
+		if (str == NULL ||
+		    M_ASN1_OCTET_STRING_set(str, keyid_str, keyid_len) == 0 ||
+		    (keyid = ASN1_TYPE_new()) == NULL) {
+			SUNWerr(SUNW_F_SET_LOCALKEYID, SUNW_R_MEMORY_FAILURE);
+			goto cleanup;
+		}
+
+		ASN1_TYPE_set(keyid, V_ASN1_OCTET_STRING, str);
+		str = NULL;
+
+		attr = type2attrib(keyid, NID_localKeyID);
+		if (attr == NULL) {
+			/*
+			 * Error already on stack
+			 */
+			goto cleanup;
+		}
+		keyid = NULL;
+
+		if (pkey->attributes == NULL) {
+			pkey->attributes = sk_X509_ATTRIBUTE_new_null();
+			if (pkey->attributes == NULL) {
+				SUNWerr(SUNW_F_SET_LOCALKEYID,
+				    SUNW_R_MEMORY_FAILURE);
+				goto cleanup;
+			}
+		} else {
+			i = find_attr_by_nid(pkey->attributes, NID_localKeyID);
+			if (i >= 0)
+				sk_X509_ATTRIBUTE_delete(pkey->attributes, i);
+		}
+		if (sk_X509_ATTRIBUTE_push(pkey->attributes, attr) == 0) {
+			SUNWerr(SUNW_F_SET_LOCALKEYID, SUNW_R_MEMORY_FAILURE);
+			goto cleanup;
+		}
+		attr = NULL;
+	}
+	retval = 0;
+
+cleanup:
+	if (str != NULL)
+		ASN1_STRING_free(str);
+	if (keyid != NULL)
+		ASN1_TYPE_free(keyid);
+	if (attr != NULL)
+		X509_ATTRIBUTE_free(attr);
+
+	return (retval);
+}
+
+/*
+ * sunw_get_pkey_localkeyid() gets the localkeyid from a private key.  It can
+ *     optionally remove the value found.
+ *
+ * Arguments:
+ *   dowhat   - What to do with the attributes (remove them or copy them).
+ *   pkey     - Points to a private key to set the keyidstr in.
+ *   keyid_str- Points to a location which will receive the pointer to
+ *              a byte string containing the binary localkeyid.  Note that
+ *              this is a copy, and the caller must free it.
+ *   keyid_len- Length of keyid_str.
+ *
+ * Returns:
+ *   >= 0     - The number of characters in the keyid returned.
+ *   < 0      - An error occurred.  It was probably an error in allocating
+ *              memory.  The error will be set in the error stack.  Call
+ *              ERR_get_error() to get specific information.
+ */
+int
+sunw_get_pkey_localkeyid(getdo_actions_t dowhat, EVP_PKEY *pkey,
+    char **keyid_str, int *keyid_len)
+{
+	X509_ATTRIBUTE *attr = NULL;
+	ASN1_OCTET_STRING *str = NULL;
+	ASN1_TYPE *ty = NULL;
+	int len = 0;
+	int i;
+
+	if (keyid_str != NULL)
+		*keyid_str = NULL;
+	if (keyid_len != NULL)
+		*keyid_len = 0;
+
+	if (pkey == NULL || pkey->attributes == NULL) {
+		return (0);
+	}
+
+	if ((i = find_attr_by_nid(pkey->attributes, NID_localKeyID)) < 0) {
+		return (0);
+	}
+	attr = sk_X509_ATTRIBUTE_value(pkey->attributes, i);
+
+	if ((ty = attrib2type(attr)) == NULL ||
+	    ty->type != V_ASN1_OCTET_STRING) {
+		return (0);
+	}
+
+	if (dowhat == GETDO_DEL) {
+		attr = sk_X509_ATTRIBUTE_delete(pkey->attributes, i);
+		if (attr != NULL)
+			X509_ATTRIBUTE_free(attr);
+		return (0);
+	}
+
+	str = ty->value.octet_string;
+	len = str->length;
+	if ((*keyid_str = malloc(len)) == NULL) {
+		SUNWerr(SUNW_F_GET_LOCALKEYID, SUNW_R_MEMORY_FAILURE);
+		return (-1);
+	}
+
+	(void) memcpy(*keyid_str, str->data, len);
+	*keyid_len = len;
+
+	return (len);
+}
+
+/*
+ * sunw_get_pkey_fname() gets the friendlyName from a private key.  It can
+ *     optionally remove the value found.
+ *
+ * Arguments:
+ *   dowhat   - What to do with the attributes (remove them or copy them).
+ *   pkey     - Points to a private key to get the frientlyname from
+ *   fname    - Points to a location which will receive the pointer to a
+ *              byte string with the ASCII friendlyname
+ *
+ * Returns:
+ *   >= 0     - The number of characters in the frienlyname returned.
+ *   < 0      - An error occurred.  It was probably an error in allocating
+ *              memory.  The error will be set in the error stack.  Call
+ *              ERR_get_error() to get specific information.
+ */
+int
+sunw_get_pkey_fname(getdo_actions_t dowhat, EVP_PKEY *pkey, char **fname)
+{
+	X509_ATTRIBUTE *attr = NULL;
+	ASN1_BMPSTRING *str = NULL;
+	ASN1_TYPE *ty = NULL;
+	int len = 0;
+	int i;
+
+	if (fname != NULL)
+		*fname = NULL;
+
+	if (pkey == NULL || pkey->attributes == NULL) {
+		return (0);
+	}
+
+	if ((i = find_attr_by_nid(pkey->attributes, NID_friendlyName)) < 0) {
+		return (0);
+	}
+	attr = sk_X509_ATTRIBUTE_value(pkey->attributes, i);
+
+	if ((ty = attrib2type(attr)) == NULL ||
+	    ty->type != V_ASN1_BMPSTRING) {
+		return (0);
+	}
+
+	if (dowhat == GETDO_DEL) {
+		attr = sk_X509_ATTRIBUTE_delete(pkey->attributes, i);
+		if (attr != NULL)
+			X509_ATTRIBUTE_free(attr);
+		return (0);
+	}
+
+	str = ty->value.bmpstring;
+	*fname = uni2asc(str->data, str->length);
+	if (*fname == NULL) {
+		SUNWerr(SUNW_F_GET_PKEY_FNAME, SUNW_R_MEMORY_FAILURE);
+		return (-1);
+	}
+
+	len = strlen(*fname);
+
+	return (len);
+}
+
+/*
+ * sunw_find_localkeyid() searches stacks of certs and private keys,
+ *     and returns  the first matching cert/private key found.
+ *
+ * Look for a keyid in a stack of certs.  if 'certs' is NULL and 'pkeys' is
+ * not NULL, search the list of private keys.  Move the matching cert to
+ * 'matching_cert' and its matching private key to 'matching_pkey'.  If no
+ * cert or keys match, no match occurred.
+ *
+ * Arguments:
+ *   keyid_str- A byte string with the localkeyid to match
+ *   keyid_len- Length of the keyid byte string.
+ *   pkeys    - Points to a stack of private keys which match the certs.
+ *              This may be NULL, in which case no keys are returned.
+ *   certs    - Points to a stack of certs to search.  If NULL, search the
+ *              stack of keys instead.
+ *   matching_pkey
+ *            - Pointer to receive address of first matching pkey found.
+ *              'matching_pkey' must not be NULL; '*matching_pkey' will be
+ *              reset.
+ *   matching_cert
+ *            - Pointer to receive address of first matching cert found.
+ *              'matching_cert' must not be NULL; '*matching_cert' will be
+ *              reset.
+ *
+ * Returns:
+ *  <  0 - An error returned.  Call ERR_get_error() to get errors information.
+ *         Where possible, memory has been freed.
+ *  >= 0 - Objects were found and returned.  Which objects are indicated by
+ *         which bits are set (FOUND_PKEY and/or FOUND_CERT).
+ */
+int
+sunw_find_localkeyid(char *keyid_str, int len, STACK_OF(EVP_PKEY) *pkeys,
+STACK_OF(X509) *certs, EVP_PKEY **matching_pkey, X509 **matching_cert)
+{
+	ASN1_STRING *cmpstr = NULL;
+	EVP_PKEY *tmp_pkey = NULL;
+	X509 *tmp_cert = NULL;
+	int retval = 0;
+
+	/* If NULL arguments, this is an error */
+	if (keyid_str == NULL ||
+	    (pkeys == NULL || certs == NULL) ||
+	    (pkeys != NULL && matching_pkey == NULL) ||
+	    (certs != NULL && matching_cert == NULL)) {
+		SUNWerr(SUNW_F_FIND_LOCALKEYID, SUNW_R_INVALID_ARG);
+		return (-1);
+	}
+
+	if (matching_pkey != NULL)
+		*matching_pkey = NULL;
+	if (matching_cert != NULL)
+		*matching_cert = NULL;
+
+	cmpstr = (ASN1_STRING *)M_ASN1_OCTET_STRING_new();
+	if (cmpstr == NULL ||
+	    M_ASN1_OCTET_STRING_set(cmpstr, keyid_str, len) == 0) {
+		SUNWerr(SUNW_F_FIND_LOCALKEYID, SUNW_R_MEMORY_FAILURE);
+		return (-1);
+	}
+
+	retval = find_attr(NID_localKeyID, cmpstr, pkeys, &tmp_pkey, certs,
+	    &tmp_cert);
+	if (retval == 0) {
+		ASN1_STRING_free(cmpstr);
+		return (retval);
+	}
+
+	if (matching_pkey != NULL)
+		*matching_pkey = tmp_pkey;
+	if (matching_cert != NULL)
+		*matching_cert = tmp_cert;
+
+	return (retval);
+}
+
+/*
+ * sunw_find_fname() searches stacks of certs and private keys for one with
+ *     a matching friendlyname and returns the first matching cert/private
+ *     key found.
+ *
+ * Look for a friendlyname in a stack of certs.  if 'certs' is NULL and 'pkeys'
+ * is not NULL, search the list of private keys.  Move the matching cert to
+ * 'matching_cert' and its matching private key to 'matching_pkey'.  If no
+ * cert or keys match, no match occurred.
+ *
+ * Arguments:
+ *   fname    - Friendlyname to find (NULL-terminated ASCII string).
+ *   pkeys    - Points to a stack of private keys which match the certs.
+ *              This may be NULL, in which case no keys are returned.
+ *   certs    - Points to a stack of certs to search.  If NULL, search the
+ *              stack of keys instead.
+ *   matching_pkey
+ *            - Pointer to receive address of first matching pkey found.
+ *   matching_cert
+ *            - Pointer to receive address of first matching cert found.
+ *
+ * Returns:
+ *  <  0 - An error returned.  Call ERR_get_error() to get errors information.
+ *         Where possible, memory has been freed.
+ *  >= 0 - Objects were found and returned.  Which objects are indicated by
+ *         which bits are set (FOUND_PKEY and/or FOUND_CERT).
+ */
+int
+sunw_find_fname(char *fname, STACK_OF(EVP_PKEY) *pkeys, STACK_OF(X509) *certs,
+    EVP_PKEY **matching_pkey, X509 ** matching_cert)
+{
+	ASN1_STRING *cmpstr = NULL;
+	EVP_PKEY *tmp_pkey = NULL;
+	X509 *tmp_cert = NULL;
+	int retval = 0;
+
+	/* If NULL arguments, this is an error */
+	if (fname == NULL ||
+	    (pkeys == NULL && certs == NULL) ||
+	    (pkeys != NULL && matching_pkey == NULL) ||
+	    (certs != NULL && matching_cert == NULL)) {
+		SUNWerr(SUNW_F_FIND_FNAME, SUNW_R_INVALID_ARG);
+		return (-1);
+	}
+
+	if (matching_pkey != NULL)
+		*matching_pkey = NULL;
+	if (matching_cert != NULL)
+		*matching_cert = NULL;
+
+	cmpstr = (ASN1_STRING *)asc2bmpstring(fname, strlen(fname));
+	if (cmpstr == NULL) {
+		/*
+		 * Error already on stack
+		 */
+		return (-1);
+	}
+
+	retval = find_attr(NID_friendlyName, cmpstr, pkeys, &tmp_pkey, certs,
+	    &tmp_cert);
+	if (retval == 0) {
+		ASN1_STRING_free(cmpstr);
+		return (retval);
+	}
+
+	if (matching_pkey != NULL)
+		*matching_pkey = tmp_pkey;
+	if (matching_cert != NULL)
+		*matching_cert = tmp_cert;
+
+	return (retval);
+}
+
+/*
+ * sunw_get_cert_fname() gets the fiendlyname from a cert.  It can
+ *     optionally remove the value found.
+ *
+ * Arguments:
+ *   dowhat   - What to do with the attributes (remove them or copy them).
+ *   cert     - Points to a cert to get the friendlyName from.
+ *   fname    - Points to a location which will receive the pointer to a
+ *              byte string with the ASCII friendlyname
+ *
+ * Returns:
+ *   >= 0     - The number of characters in the friendlyname returned.
+ *   < 0      - An error occurred.  It was probably an error in allocating
+ *              memory.  The error will be set in the error stack.  Call
+ *              ERR_get_error() to get specific information.
+ */
+int
+sunw_get_cert_fname(getdo_actions_t dowhat, X509 *cert, char **fname)
+{
+	int len;
+
+	if (fname != NULL)
+		*fname = NULL;
+
+	if (cert == NULL || cert->aux == NULL || cert->aux->alias == NULL) {
+		return (0);
+	}
+
+	if (dowhat == GETDO_DEL) {
+		/* Delete the entry */
+		ASN1_UTF8STRING_free(cert->aux->alias);
+		cert->aux->alias = NULL;
+		return (0);
+	}
+
+	*((uchar_t **)fname) = utf82ascstr(cert->aux->alias);
+	if (*fname == NULL) {
+		/*
+		 * Error already on stack
+		 */
+		return (-1);
+	}
+
+	len = strlen(*fname);
+
+	return (len);
+}
+
+/*
+ * sunw_set_fname() sets the friendlyName in a cert, a private key or
+ *     both.  Any existing friendlyname will be discarded.
+ *
+ * Arguments:
+ *   ascname  - An ASCII string with the friendlyName to set
+ *   pkey     - Points to a private key to set the fname in.
+ *   cert     - Points to a cert to set the fname in.
+ *
+ * Note that setting a friendlyName into a cert which will not be written out
+ * as a PKCS12 cert is pointless since it will be lost.
+ *
+ * Returns:
+ *   0        - Success.
+ *   <0       - An error occurred.  It was probably an error in allocating
+ *              memory.  The error will be set in the error stack.  Call
+ *              ERR_get_error() to get specific information.
+ */
+int
+sunw_set_fname(const char *ascname, EVP_PKEY *pkey, X509 *cert)
+{
+	X509_ATTRIBUTE *attr = NULL;
+	ASN1_BMPSTRING *str = NULL;
+	ASN1_TYPE *fname = NULL;
+	unsigned char *data = NULL;
+	int retval = -1;
+	int len;
+	int i;
+
+	str = asc2bmpstring(ascname, strlen(ascname));
+	if (str == NULL) {
+		/*
+		 * Error already on stack
+		 */
+		return (-1);
+	}
+
+	if (cert != NULL) {
+		if (cert->aux != NULL && cert->aux->alias != NULL) {
+			ASN1_UTF8STRING_free(cert->aux->alias);
+		}
+
+		len = ASN1_STRING_to_UTF8(&data, str);
+		i = -23;
+		if (len <= 0 || (i = X509_alias_set1(cert, data, len)) == 0) {
+			SUNWerr(SUNW_F_SET_FNAME, SUNW_R_SET_FNAME_ERR);
+			goto cleanup;
+		}
+	}
+	if (pkey != NULL) {
+		if ((fname = ASN1_TYPE_new()) == NULL) {
+			SUNWerr(SUNW_F_SET_FNAME, SUNW_R_MEMORY_FAILURE);
+			goto cleanup;
+		}
+
+		ASN1_TYPE_set(fname, V_ASN1_BMPSTRING, str);
+		str = NULL;
+
+		attr = type2attrib(fname, NID_friendlyName);
+		if (attr == NULL) {
+			/*
+			 * Error already on stack
+			 */
+			goto cleanup;
+		}
+		fname = NULL;
+
+		if (pkey->attributes == NULL) {
+			pkey->attributes = sk_X509_ATTRIBUTE_new_null();
+			if (pkey->attributes == NULL) {
+				SUNWerr(SUNW_F_SET_FNAME,
+				    SUNW_R_MEMORY_FAILURE);
+				goto cleanup;
+			}
+		} else if ((i = find_attr_by_nid(pkey->attributes,
+		    NID_friendlyName)) >= 0) {
+			(void) sk_X509_ATTRIBUTE_delete(pkey->attributes, i);
+		}
+
+		if (sk_X509_ATTRIBUTE_push(pkey->attributes, attr) == 0) {
+			SUNWerr(SUNW_F_SET_FNAME, SUNW_R_MEMORY_FAILURE);
+			goto cleanup;
+		}
+
+		attr = NULL;
+	}
+	retval = 0;
+
+cleanup:
+	if (data != NULL)
+		OPENSSL_free(data);
+	if (str != NULL)
+		ASN1_BMPSTRING_free(str);
+	if (fname != NULL)
+		ASN1_TYPE_free(fname);
+	if (attr != NULL)
+		X509_ATTRIBUTE_free(attr);
+
+	return (retval);
+}
+
+/*
+ * sunw_check_keys() compares the public key in the certificate and a
+ *     private key to ensure that they match.
+ *
+ * Arguments:
+ *   cert     - Points to a certificate.
+ *   pkey     - Points to a private key.
+ *
+ * Returns:
+ *  == 0 - These do not match.
+ *  != 0 - The cert's public key and the private key match.
+ */
+int
+sunw_check_keys(X509 *cert, EVP_PKEY *pkey)
+{
+	int retval = 0;
+
+	if (pkey != NULL && cert != NULL)
+		retval = X509_check_private_key(cert, pkey);
+
+	return (retval);
+}
+
+/*
+ * sunw_check_cert_times() compares the time fields in a certificate
+ *
+ * Compare the 'not before' and the 'not after' times in the cert
+ * to the current time.  Return the results of the comparison (bad time formats,
+ * cert not yet in force, cert expired or in range)
+ *
+ * Arguments:
+ *   dowhat   - what field(s) to check.
+ *   cert     - Points to a cert to check
+ *
+ * Returns:
+ *   Results of the comparison.
+ */
+chk_errs_t
+sunw_check_cert_times(chk_actions_t chkwhat, X509 *cert)
+{
+	return (check_time(chkwhat, cert));
+}
+
+/*
+ * ----------------------------------------------------------------------------
+ * Local routines
+ * ----------------------------------------------------------------------------
+ */
+
+
+/*
+ * parse_pkcs12 - Oversee parsing of the pkcs12 structure.  Get it
+ *         parsed.  After that either return what's found directly, or
+ *         do any required matching.
+ *
+ * Arguments:
+ *   p12      - Structure with pkcs12 info to be parsed
+ *   pass     - Pass phrase for the private key (possibly empty) or NULL if
+ *              there is none.
+ *   matchty  - Info about which certs/keys to return if many are in the file.
+ *   keyid    - If private key localkeyids friendlynames are to match a
+ *              predetermined value, the value to match. This value should
+ *		be an octet string.
+ *   keyid_len- Length of the keyid byte string.
+ *   name_str - If friendlynames are to match a predetermined value, the value
+ *		 to match. This value should be a NULL terminated string.
+ *   pkey     - Points to location pointing to the private key returned.
+ *   cert     - Points to locaiton which points to the client cert returned
+ *   ca       - Points to location that points to a stack of 'certificate
+ *              authority' certs/trust anchors.
+ *
+ *   Note about error codes:  This function is an internal function, and the
+ *   place where it is called sets error codes.  Therefore only set an error
+ *   code if it is something that is unique or if the function which detected
+ *   the error doesn't set one.
+ *
+ * Returns:
+ *   == -1 - An error occurred.  Call ERR_get_error() to get error information.
+ *           Where possible, memory has been freed.
+ *   == 0  - No matching returns were found.
+ *    > 0  - This is the aithmetic 'or' of the FOUND_* bits that indicate which
+ *           of the requested entries were found.
+ */
+static int
+parse_pkcs12(PKCS12 *p12, const char *pass, int matchty, char *keyid,
+    int kstr_len, char *name_str, EVP_PKEY **pkey, X509 **cert,
+    STACK_OF(X509) **ca)
+{
+	STACK_OF(EVP_PKEY) *work_kl = NULL;	/* Head for private key list */
+	STACK_OF(EVP_PKEY) *nocerts = NULL;	/* Head for alt. key list */
+	STACK_OF(X509) *work_ca = NULL;		/* Head for cert list */
+	STACK_OF(X509) *work_cl = NULL;
+	int retval = 0;
+	int n;
+
+	retval = sunw_PKCS12_contents(p12, pass, &work_kl, &work_ca);
+	if (retval < 0) {
+		goto cleanup;
+	} else if (retval == 0) {
+		/*
+		 * Not really an error here - its just that nothing was found.
+		 */
+		goto cleanup;
+	}
+
+	if (sk_EVP_PKEY_num(work_kl) > 0) {
+
+		if (sunw_split_certs(work_kl, work_ca, &work_cl, &nocerts)
+		    < 0) {
+			goto cleanup;
+		}
+	}
+
+	/*
+	 * Go through the lists of certs and private keys which were
+	 * returned, looking for matches of the appropriate type.  Do these
+	 * in the order described above.
+	 */
+	if ((matchty & DO_FIND_KEYID) != 0) {
+
+		if (keyid == NULL) {
+			SUNWerr(SUNW_F_PKCS12_PARSE, SUNW_R_INVALID_ARG);
+			retval = -1;
+			goto cleanup;
+		}
+
+		/* See if string matches localkeyid's */
+		retval = sunw_find_localkeyid(keyid, kstr_len,
+		    work_kl, work_cl, pkey, cert);
+		if (retval != 0) {
+			if (retval == -1)
+				goto cleanup;
+			else
+				goto last_part;
+		}
+	}
+	if ((matchty & DO_FIND_FN) != 0) {
+
+		if (name_str == NULL) {
+			SUNWerr(SUNW_F_PKCS12_PARSE, SUNW_R_INVALID_ARG);
+			retval = -1;
+			goto cleanup;
+		}
+
+		/* See if string matches friendly names */
+		retval = sunw_find_fname(name_str, work_kl, work_cl,
+		    pkey, cert);
+		if (retval != 0) {
+			if (retval == -1)
+				goto cleanup;
+			else
+				goto last_part;
+		}
+	}
+
+	if (matchty & DO_FIRST_PAIR) {
+
+		/* Find the first cert and private key and return them */
+		retval = get_key_cert(0, work_kl, pkey, work_cl, cert);
+		if (retval != 0) {
+			if (retval == -1)
+				goto cleanup;
+			else
+				goto last_part;
+		}
+	}
+
+	if (matchty & DO_LAST_PAIR) {
+
+		/*
+		 * Find the last matching cert and private key and return
+		 * them.  Since keys which don't have matching client certs
+		 * are at the end of the list of keys, use the number of
+		 * client certs to compute the position of the last private
+		 * key which matches a client cert.
+		 */
+		n = sk_X509_num(work_cl) - 1;
+		retval = get_key_cert(n, work_kl, pkey, work_cl, cert);
+		if (retval != 0) {
+			if (retval == -1)
+				goto cleanup;
+			else
+				goto last_part;
+		}
+	}
+
+	if (matchty & DO_UNMATCHING) {
+		STACK_OF(EVP_PKEY) *tmpk;
+		STACK_OF(X509) *tmpc;
+
+		/* Find the first cert and private key and return them */
+		tmpc = work_cl;
+		if (work_cl == NULL || sk_X509_num(work_cl) == 0)
+			tmpc = work_ca;
+		tmpk = work_kl;
+		if (work_kl == NULL || sk_EVP_PKEY_num(work_kl) == 0)
+			tmpk = nocerts;
+		retval = get_key_cert(0, tmpk, pkey, tmpc, cert);
+		if (retval != 0) {
+			if (retval == -1)
+				goto cleanup;
+			else
+				goto last_part;
+		}
+	}
+
+last_part:
+	/* If no errors, terminate normally */
+	if (retval != -1)
+		retval |= set_results(NULL, NULL, NULL, NULL, ca, &work_ca,
+		    NULL, NULL);
+	if (retval >= 0) {
+		goto clean_part;
+	}
+
+	/* Fallthrough is intentional in error cases. */
+cleanup:
+	if (pkey != NULL && *pkey != NULL) {
+		sunw_evp_pkey_free(*pkey);
+		*pkey = NULL;
+	}
+	if (cert != NULL && *cert != NULL) {
+		X509_free(*cert);
+		*cert = NULL;
+	}
+
+clean_part:
+
+	if (work_kl != NULL) {
+		sk_EVP_PKEY_pop_free(work_kl, sunw_evp_pkey_free);
+	}
+	if (work_ca != NULL)
+		sk_X509_pop_free(work_ca, X509_free);
+	if (work_cl != NULL)
+		sk_X509_pop_free(work_cl, X509_free);
+
+	return (retval);
+}
+
+/*
+ * parse_outer - Unpack the outer PKCS#12 structure and go through the
+ *         individual bags.  Return stacks of certs, private keys found and
+ *         CA certs found.
+ *
+ *   Note about error codes:  This function is an internal function, and the
+ *   place where it is called sets error codes.
+ *
+ * Returns:
+ *    0 - An error returned.  Call ERR_get_error() to get errors information.
+ *        Where possible, memory has been freed.
+ *    1 - PKCS12 data object was parsed and lists of certs and private keys
+ *        were returned.
+ */
+static int
+parse_outer(PKCS12 *p12, const char *pass, STACK_OF(EVP_PKEY) *kl,
+    STACK_OF(X509) *cl)
+{
+	STACK_OF(PKCS12_SAFEBAG) *bags;
+	STACK_OF(PKCS7) *asafes;
+	int i, bagnid;
+	PKCS7 *p7;
+
+	if ((asafes = M_PKCS12_unpack_authsafes(p12)) == NULL)
+		return (0);
+
+	for (i = 0; i < sk_PKCS7_num(asafes); i++) {
+		p7 = sk_PKCS7_value(asafes, i);
+		bagnid = OBJ_obj2nid(p7->type);
+		if (bagnid == NID_pkcs7_data) {
+			bags = M_PKCS12_unpack_p7data(p7);
+		} else if (bagnid == NID_pkcs7_encrypted) {
+			/*
+			 * A length of '-1' means strlen() can be used
+			 * to determine the password length.
+			 */
+			bags = M_PKCS12_unpack_p7encdata(p7, pass, -1);
+		} else {
+			SUNWerr(SUNW_F_PARSE_OUTER, SUNW_R_BAD_BAGTYPE);
+			return (0);
+		}
+
+		if (bags == NULL) {
+			SUNWerr(SUNW_F_PARSE_OUTER, SUNW_R_PARSE_BAG_ERR);
+			sk_PKCS7_pop_free(asafes, PKCS7_free);
+			return (0);
+		}
+		if (parse_all_bags(bags, pass, kl, cl) == 0) {
+			sk_PKCS12_SAFEBAG_pop_free(bags, PKCS12_SAFEBAG_free);
+			sk_PKCS7_pop_free(asafes, PKCS7_free);
+			return (0);
+		}
+	}
+
+	return (1);
+}
+
+/*
+ * parse_all_bags - go through the stack of bags, parsing each.
+ *
+ *   Note about error codes:  This function is an internal function, and the
+ *   place where it is called sets error codes.
+ *
+ * Returns:
+ *    0 - An error returned.  Call ERR_get_error() to get errors information.
+ *        Where possible, memory has been freed.
+ *    1 - Stack of safebags was parsed and lists of certs and private keys
+ *        were returned.
+ */
+static int
+parse_all_bags(STACK_OF(PKCS12_SAFEBAG) *bags, const char *pass,
+    STACK_OF(EVP_PKEY) *kl, STACK_OF(X509) *cl)
+{
+	int i;
+	for (i = 0; i < sk_PKCS12_SAFEBAG_num(bags); i++) {
+		if (parse_one_bag(sk_PKCS12_SAFEBAG_value(bags, i),
+		    pass, kl, cl) == 0)
+			return (0);
+	}
+	return (1);
+}
+
+/*
+ * parse_one_bag - Parse an individual bag
+ *
+ *   i = parse_one_bag(bag, pass, kl, cl);
+ *
+ * Arguments:
+ *   bag	- pkcs12 safebag to parse.
+ *   pass 	- password for use in decryption of shrouded keybag
+ *   kl         - Stack of private keys found so far.  New private keys will
+ *                be added here if found.
+ *   cl         - Stack of certs found so far.  New certificates will be
+ *                added here if found.
+ *
+ * Returns:
+ *    0 - An error returned.  Call ERR_get_error() to get errors information.
+ *        Where possible, memory has been freed.
+ *    1 - one safebag was parsed. If it contained a cert or private key, it
+ *        was added to the stack of certs or private keys found, respectively.
+ *        localKeyId or friendlyName attributes are returned with the
+ *        private key or certificate.
+ */
+static int
+parse_one_bag(PKCS12_SAFEBAG *bag, const char *pass, STACK_OF(EVP_PKEY) *kl,
+    STACK_OF(X509) *cl)
+{
+	X509_ATTRIBUTE *attr = NULL;
+	ASN1_TYPE *keyid = NULL;
+	ASN1_TYPE *fname = NULL;
+	PKCS8_PRIV_KEY_INFO *p8;
+	EVP_PKEY *pkey = NULL;
+	X509 *x509 = NULL;
+	uchar_t *data = NULL;
+	char *str = NULL;
+	int retval = 1;
+
+	keyid = PKCS12_get_attr(bag, NID_localKeyID);
+	fname = PKCS12_get_attr(bag, NID_friendlyName);
+
+	switch (M_PKCS12_bag_type(bag)) {
+	case NID_keyBag:
+		if ((pkey = EVP_PKCS82PKEY(bag->value.keybag)) == NULL) {
+			SUNWerr(SUNW_F_PARSE_ONE_BAG, SUNW_R_PARSE_BAG_ERR);
+			retval = 0;
+			break;
+		}
+		break;
+
+	case NID_pkcs8ShroudedKeyBag:
+		/*
+		 * A length of '-1' means strlen() can be used
+		 * to determine the password length.
+		 */
+		if ((p8 = M_PKCS12_decrypt_skey(bag, pass, -1)) == NULL) {
+			SUNWerr(SUNW_F_PARSE_ONE_BAG, SUNW_R_PARSE_BAG_ERR);
+			retval = 0;
+			break;
+		}
+		pkey = EVP_PKCS82PKEY(p8);
+		PKCS8_PRIV_KEY_INFO_free(p8);
+		if (pkey == NULL) {
+			SUNWerr(SUNW_F_PARSE_ONE_BAG, SUNW_R_PARSE_BAG_ERR);
+			retval = 0;
+		}
+		break;
+
+	case NID_certBag:
+		if (M_PKCS12_cert_bag_type(bag) != NID_x509Certificate) {
+			SUNWerr(SUNW_F_PARSE_ONE_BAG, SUNW_R_BAD_CERTTYPE);
+			break;
+		}
+		if ((x509 = M_PKCS12_certbag2x509(bag)) == NULL) {
+			SUNWerr(SUNW_F_PARSE_ONE_BAG,
+			    SUNW_R_PARSE_CERT_ERR);
+			retval = 0;
+			break;
+		}
+
+		if (keyid != NULL) {
+			if (keyid->type != V_ASN1_OCTET_STRING) {
+				SUNWerr(SUNW_F_PARSE_ONE_BAG,
+				    SUNW_R_BAD_LKID);
+				retval = 0;
+				break;
+			}
+			if (X509_keyid_set1(x509,
+			    keyid->value.octet_string->data,
+			    keyid->value.octet_string->length) == 0) {
+				SUNWerr(SUNW_F_PARSE_ONE_BAG,
+				    SUNW_R_SET_LKID_ERR);
+				retval = 0;
+				break;
+			}
+		}
+
+		if (fname != NULL) {
+			ASN1_STRING *tmpstr = NULL;
+			int len;
+
+			if (fname->type != V_ASN1_BMPSTRING) {
+				SUNWerr(SUNW_F_PARSE_ONE_BAG,
+				    SUNW_R_BAD_FNAME);
+				retval = 0;
+				break;
+			}
+
+			tmpstr = fname->value.asn1_string;
+			len = ASN1_STRING_to_UTF8(&data, tmpstr);
+			if (len < 0) {
+				SUNWerr(SUNW_F_PARSE_ONE_BAG,
+				    SUNW_R_SET_FNAME_ERR);
+				retval = 0;
+				break;
+			}
+
+			if (X509_alias_set1(x509, data, len) == 0) {
+				SUNWerr(SUNW_F_PARSE_ONE_BAG,
+				    SUNW_R_SET_FNAME_ERR);
+				retval = 0;
+				break;
+			}
+		}
+
+		if (sk_X509_push(cl, x509) == 0) {
+			SUNWerr(SUNW_F_PARSE_ONE_BAG, SUNW_R_MEMORY_FAILURE);
+			retval = 0;
+			break;
+		}
+		x509 = NULL;
+		break;
+
+	case NID_safeContentsBag:
+		if (keyid != NULL)
+			ASN1_TYPE_free(keyid);
+		if (fname != NULL)
+			ASN1_TYPE_free(fname);
+		if (parse_all_bags(bag->value.safes, pass, kl, cl) == 0) {
+			/*
+			 * Error already on stack
+			 */
+			return (0);
+		}
+		return (1);
+
+	default:
+		if (keyid != NULL)
+			ASN1_TYPE_free(keyid);
+		if (fname != NULL)
+			ASN1_TYPE_free(fname);
+		SUNWerr(SUNW_F_PARSE_ONE_BAG, SUNW_R_BAD_BAGTYPE);
+		return (0);
+	}
+
+
+	if (pkey != NULL) {
+		if (retval != 0 && (keyid != NULL || fname != NULL) &&
+		    pkey->attributes == NULL) {
+			pkey->attributes = sk_X509_ATTRIBUTE_new_null();
+			if (pkey->attributes == NULL) {
+				SUNWerr(SUNW_F_PARSE_ONE_BAG,
+				    SUNW_R_MEMORY_FAILURE);
+				retval = 0;
+			}
+		}
+
+		if (retval != 0 && keyid != NULL) {
+			attr = type2attrib(keyid, NID_localKeyID);
+			if (attr == NULL)
+				/*
+				 * Error already on stack
+				 */
+				retval = 0;
+			else {
+				keyid = NULL;
+				if (sk_X509_ATTRIBUTE_push(pkey->attributes,
+				    attr) == 0) {
+					SUNWerr(SUNW_F_PARSE_ONE_BAG,
+					    SUNW_R_MEMORY_FAILURE);
+					retval = 0;
+				} else {
+					attr = NULL;
+				}
+			}
+		}
+
+		if (retval != 0 && fname != NULL) {
+			attr = type2attrib(fname, NID_friendlyName);
+			if (attr == NULL) {
+				/*
+				 * Error already on stack
+				 */
+				retval = 0;
+			} else {
+				fname = NULL;
+				if (sk_X509_ATTRIBUTE_push(pkey->attributes,
+				    attr) == 0) {
+					SUNWerr(SUNW_F_PARSE_ONE_BAG,
+					    SUNW_R_MEMORY_FAILURE);
+					retval = 0;
+				} else {
+					attr = NULL;
+				}
+			}
+		}
+
+		/* Save the private key */
+		if (retval != 0) {
+			if (sk_EVP_PKEY_push(kl, pkey) == 0) {
+				SUNWerr(SUNW_F_PARSE_ONE_BAG,
+				    SUNW_R_MEMORY_FAILURE);
+				retval = 0;
+			} else {
+				pkey = NULL;
+			}
+		}
+	}
+
+	if (pkey != NULL) {
+		sunw_evp_pkey_free(pkey);
+	}
+
+	if (x509 != NULL)
+		X509_free(x509);
+
+	if (keyid != NULL)
+		ASN1_TYPE_free(keyid);
+
+	if (fname != NULL)
+		ASN1_TYPE_free(fname);
+
+	if (attr != NULL)
+		X509_ATTRIBUTE_free(attr);
+
+	if (data != NULL)
+		OPENSSL_free(data);
+
+	if (str != NULL)
+		OPENSSL_free(str);
+
+	return (retval);
+}
+
+/*
+ * This function uses the only function that reads PEM files, regardless of
+ * the kinds of information included (private keys, public keys, cert requests,
+ * certs).  Other interfaces that read files require that the application
+ * specifically know what kinds of things to read next, and call different
+ * interfaces for the different kinds of entities.
+ *
+ * There is only one aspect of this function that's a bit problematic.
+ * If it finds an encrypted private key, it does not decrypt it.  It returns
+ * the encrypted data and other information needed to decrypt it.  The caller
+ * must do the decryption.  This function does the decoding.
+ */
+static int
+pem_info(FILE *fp, pem_password_cb cb, void *userdata,
+    STACK_OF(EVP_PKEY) **pkeys, STACK_OF(X509) **certs)
+{
+	STACK_OF(X509_INFO) *info;
+	STACK_OF(EVP_PKEY) *work_kl;
+	STACK_OF(X509) *work_cl;
+	X509_INFO *x;
+	int retval = 0;
+	int i;
+
+	info = PEM_X509_INFO_read(fp, NULL, cb, userdata);
+	if (info == NULL) {
+		SUNWerr(SUNW_F_PEM_INFO, SUNW_R_READ_ERR);
+		return (-1);
+	}
+
+	/*
+	 * Allocate the working stacks for private key(s) and for the cert(s).
+	 */
+	if ((work_kl = sk_EVP_PKEY_new_null()) == NULL) {
+		SUNWerr(SUNW_F_PEM_INFO, SUNW_R_MEMORY_FAILURE);
+		retval = -1;
+		goto cleanup;
+	}
+
+	if ((work_cl = sk_X509_new_null()) == NULL) {
+		SUNWerr(SUNW_F_PEM_INFO, SUNW_R_MEMORY_FAILURE);
+		retval = -1;
+		goto cleanup;
+	}
+
+	/*
+	 * Go through the entries in the info structure.
+	 */
+	for (i = 0; i < sk_X509_INFO_num(info); i++) {
+		x = sk_X509_INFO_value(info, i);
+		if (x->x509) {
+			if (sk_X509_push(work_cl, x->x509) == 0) {
+				retval = -1;
+				break;
+			}
+			x->x509 = NULL;
+		}
+		if (x->x_pkey != NULL && x->x_pkey->dec_pkey != NULL &&
+		    (x->x_pkey->dec_pkey->type == EVP_PKEY_RSA ||
+		    x->x_pkey->dec_pkey->type == EVP_PKEY_DSA)) {
+			const uchar_t *p;
+
+			/*
+			 * If the key was encrypted, PEM_X509_INFO_read does
+			 * not decrypt it.  If that is the case, the 'enc_pkey'
+			 * field is set to point to the unencrypted key data.
+			 * Go through the additional steps to decode it before
+			 * going on.
+			 */
+			if (x->x_pkey->enc_pkey != NULL) {
+
+				if (PEM_do_header(&x->enc_cipher,
+				    (uchar_t *)x->enc_data,
+				    (long *)&x->enc_len,
+				    cb, userdata) == 0) {
+					if (ERR_GET_REASON(ERR_peek_error()) ==
+					    PEM_R_BAD_PASSWORD_READ) {
+						SUNWerr(SUNW_F_PEM_INFO,
+						    SUNW_R_PASSWORD_ERR);
+					} else {
+						SUNWerr(SUNW_F_PEM_INFO,
+						    SUNW_R_PKEY_READ_ERR);
+					}
+					retval = -1;
+					break;
+				}
+				if (x->x_pkey->dec_pkey->type == EVP_PKEY_RSA) {
+					RSA **pp;
+
+					pp = &(x->x_pkey->dec_pkey->pkey.rsa);
+					p = (uchar_t *)x->enc_data;
+					if (d2i_RSAPrivateKey(pp, &p,
+					    x->enc_len) == NULL) {
+						SUNWerr(SUNW_F_PEM_INFO,
+						    SUNW_R_PKEY_READ_ERR);
+						retval = -1;
+						break;
+					}
+				} else {
+					DSA **pp;
+
+					pp = &(x->x_pkey->dec_pkey->pkey.dsa);
+					p = (uchar_t *)x->enc_data;
+					if (d2i_DSAPrivateKey(pp, &p,
+					    x->enc_len) == NULL) {
+						SUNWerr(SUNW_F_PEM_INFO,
+						    SUNW_R_PKEY_READ_ERR);
+						retval = -1;
+						break;
+					}
+				}
+			}
+
+			/* Save the key. */
+			retval = sk_EVP_PKEY_push(work_kl, x->x_pkey->dec_pkey);
+			if (retval == 0) {
+				retval = -1;
+				break;
+			}
+			x->x_pkey->dec_pkey = NULL;
+		} else if (x->x_pkey != NULL) {
+			SUNWerr(SUNW_F_PEM_INFO, SUNW_R_BAD_PKEYTYPE);
+			retval = -1;
+			break;
+		}
+	}
+	if (retval == -1)
+		goto cleanup;
+
+	/* If error occurs, then error already on stack */
+	retval = set_results(pkeys, &work_kl, certs, &work_cl, NULL, NULL,
+	    NULL, NULL);
+
+cleanup:
+	if (work_kl != NULL) {
+		sk_EVP_PKEY_pop_free(work_kl, sunw_evp_pkey_free);
+	}
+	if (work_cl != NULL)
+		sk_X509_pop_free(work_cl, X509_free);
+
+	sk_X509_INFO_pop_free(info, X509_INFO_free);
+
+	return (retval);
+}
+
+/*
+ * sunw_append_keys - Given two stacks of private keys, remove the keys from
+ *      the second stack and append them to the first.  Both stacks must exist
+ *      at time of call.
+ *
+ * Arguments:
+ *   dst 	- the stack to receive the keys from 'src'
+ *   src	- the stack whose keys are to be moved.
+ *
+ * Returns:
+ *   -1  	- An error occurred.  The error status is set.
+ *   >= 0       - The number of keys that were copied.
+ */
+static int
+sunw_append_keys(STACK_OF(EVP_PKEY) *dst, STACK_OF(EVP_PKEY) *src)
+{
+	EVP_PKEY *tmpk;
+	int count = 0;
+
+	while (sk_EVP_PKEY_num(src) > 0) {
+		tmpk = sk_EVP_PKEY_delete(src, 0);
+		if (sk_EVP_PKEY_push(dst, tmpk) == 0) {
+			sunw_evp_pkey_free(tmpk);
+			SUNWerr(SUNW_F_APPEND_KEYS, SUNW_R_MEMORY_FAILURE);
+			return (-1);
+		}
+		count ++;
+	}
+
+	return (count);
+}
+
+/*
+ * move_certs - Given two stacks of certs, remove the certs from
+ *      the second stack and append them to the first.
+ *
+ * Arguments:
+ *   dst 	- the stack to receive the certs from 'src'
+ *   src	- the stack whose certs are to be moved.
+ *
+ * Returns:
+ *   -1  	- An error occurred.  The error status is set.
+ *   >= 0       - The number of certs that were copied.
+ */
+static int
+move_certs(STACK_OF(X509) *dst, STACK_OF(X509) *src)
+{
+	X509 *tmpc;
+	int count = 0;
+
+	while (sk_X509_num(src) > 0) {
+		tmpc = sk_X509_delete(src, 0);
+		if (sk_X509_push(dst, tmpc) == 0) {
+			X509_free(tmpc);
+			SUNWerr(SUNW_F_MOVE_CERTS, SUNW_R_MEMORY_FAILURE);
+			return (-1);
+		}
+		count++;
+	}
+
+	return (count);
+}
+
+/*
+ * get_key_cert - Get a cert and its matching key from the stacks of certs
+ *      and keys.  They are removed from the stacks.
+ *
+ * Arguments:
+ *   n        - Offset of the entries to return.
+ *   kl       - Points to a stack of private keys that matches the list of
+ *              certs below.
+ *   pkey     - Points at location where the address of the matching private
+ *              key will be stored.
+ *   cl       - Points to a stack of client certs with matching private keys.
+ *   cert     - Points to locaiton where the address of the matching client cert
+ *              will be returned
+ *
+ * The assumption is that the stacks of keys and certs contain key/cert pairs,
+ * with entries in the same order and hence at the same offset.  Provided
+ * the key and cert selected match, each will be removed from its stack and
+ * returned.
+ *
+ * A stack of certs can be passed in without a stack of private keys, and vise
+ * versa.  In that case, the indicated key/cert will be returned.
+ *
+ * Returns:
+ *     0 - No matches were found.
+ *   > 0 - Bits set based on FOUND_* definitions, indicating what is returned.
+ *         This can be FOUND_PKEY, FOUND_CERT or (FOUND_PKEY | FOUND_CERT).
+ */
+static int
+get_key_cert(int n, STACK_OF(EVP_PKEY) *kl, EVP_PKEY **pkey, STACK_OF(X509) *cl,
+    X509 **cert)
+{
+	int retval = 0;
+	int nk;
+	int nc;
+
+	nk = (kl != NULL) ? sk_EVP_PKEY_num(kl) : 0;
+	nc = (cl != NULL) ? sk_X509_num(cl) : 0;
+
+	if (pkey != NULL && *pkey == NULL) {
+		if (nk > 0 && n >= 0 || n < nk) {
+			*pkey = sk_EVP_PKEY_delete(kl, n);
+			if (*pkey != NULL)
+				retval |= FOUND_PKEY;
+		}
+	}
+
+	if (cert != NULL && *cert == NULL) {
+		if (nc > 0 && n >= 0 && n < nc) {
+			*cert = sk_X509_delete(cl, n);
+			if (*cert != NULL)
+				retval |= FOUND_CERT;
+		}
+	}
+
+	return (retval);
+}
+
+
+/*
+ * asc2bmpstring - Convert a regular C ASCII string to an ASn1_STRING in
+ *         ASN1_BMPSTRING format.
+ *
+ * Arguments:
+ *   str      - String to be convered.
+ *   len      - Length of the string.
+ *
+ * Returns:
+ *   == NULL  - An error occurred.  Error information (accessible by
+ *              ERR_get_error()) is set.
+ *   != NULL  - Points to an ASN1_BMPSTRING structure with the converted
+ *              string as a value.
+ */
+static ASN1_BMPSTRING *
+asc2bmpstring(const char *str, int len)
+{
+	ASN1_BMPSTRING *bmp = NULL;
+	uchar_t *uni = NULL;
+	int unilen;
+
+	/* Convert the character to the bmp format. */
+	if (asc2uni(str, len, &uni, &unilen) == 0) {
+		SUNWerr(SUNW_F_ASC2BMPSTRING, SUNW_R_MEMORY_FAILURE);
+		return (NULL);
+	}
+
+	/*
+	 * Adjust for possible pair of NULL bytes at the end because
+	 * asc2uni() returns a doubly null terminated string.
+	 */
+	if (uni[unilen - 1] == '\0' && uni[unilen - 2] == '\0')
+		unilen -= 2;
+
+	/* Construct comparison string with correct format */
+	bmp = M_ASN1_BMPSTRING_new();
+	if (bmp == NULL) {
+		SUNWerr(SUNW_F_ASC2BMPSTRING, SUNW_R_MEMORY_FAILURE);
+		OPENSSL_free(uni);
+		return (NULL);
+	}
+
+	bmp->data = uni;
+	bmp->length = unilen;
+
+	return (bmp);
+}
+
+/*
+ * utf82ascstr - Convert a UTF8STRING string to a regular C ASCII string.
+ *         This goes through an intermediate step with a ASN1_STRING type of
+ *         IA5STRING (International Alphabet 5, which is the same as ASCII).
+ *
+ * Arguments:
+ *   str      - UTF8STRING to be converted.
+ *
+ * Returns:
+ *   == NULL  - An error occurred.  Error information (accessible by
+ *              ERR_get_error()) is set.
+ *   != NULL  - Points to a NULL-termianted ASCII string.  The caller must
+ *              free it.
+ */
+static uchar_t *
+utf82ascstr(ASN1_UTF8STRING *ustr)
+{
+	ASN1_STRING tmpstr;
+	ASN1_STRING *astr = &tmpstr;
+	uchar_t *retstr = NULL;
+	int mbflag;
+	int ret;
+
+	if (ustr == NULL || ustr->type != V_ASN1_UTF8STRING) {
+		SUNWerr(SUNW_F_UTF82ASCSTR, SUNW_R_INVALID_ARG);
+		return (NULL);
+	}
+
+	mbflag = MBSTRING_ASC;
+	tmpstr.data = NULL;
+	tmpstr.length = 0;
+
+	ret = ASN1_mbstring_copy(&astr, ustr->data, ustr->length, mbflag,
+	    B_ASN1_IA5STRING);
+	if (ret < 0) {
+		SUNWerr(SUNW_F_UTF82ASCSTR, SUNW_R_STR_CONVERT_ERR);
+		return (NULL);
+	}
+
+	retstr = OPENSSL_malloc(astr->length + 1);
+	if (retstr == NULL) {
+		SUNWerr(SUNW_F_UTF82ASCSTR, SUNW_R_MEMORY_FAILURE);
+		return (NULL);
+	}
+
+	(void) memcpy(retstr, astr->data, astr->length);
+	retstr[astr->length] = '\0';
+	OPENSSL_free(astr->data);
+
+	return (retstr);
+}
+
+
+/*
+ * type2attrib - Given a ASN1_TYPE, return a X509_ATTRIBUTE of the type
+ *     specified by the given NID.
+ *
+ * Arguments:
+ *   ty       - Type structure to be made into an attribute
+ *   nid      - NID of the attribute
+ *
+ * Returns:
+ *   NULL	An error occurred.
+ *   != NULL	An X509_ATTRIBUTE structure.
+ */
+X509_ATTRIBUTE *
+type2attrib(ASN1_TYPE *ty, int nid)
+{
+	X509_ATTRIBUTE *a;
+
+	if ((a = X509_ATTRIBUTE_new()) == NULL ||
+	    (a->value.set = sk_ASN1_TYPE_new_null()) == NULL ||
+	    sk_ASN1_TYPE_push(a->value.set, ty) == 0) {
+		if (a != NULL)
+			X509_ATTRIBUTE_free(a);
+			SUNWerr(SUNW_F_TYPE2ATTRIB, SUNW_R_MEMORY_FAILURE);
+		return (NULL);
+	}
+	a->single = 0;
+	a->object = OBJ_nid2obj(nid);
+
+	return (a);
+}
+
+/*
+ * attrib2type - Given a X509_ATTRIBUTE, return pointer to the ASN1_TYPE
+ *     component
+ *
+ * Arguments:
+ *   attr     - Attribute structure containing a type.
+ *
+ * Returns:
+ *   NULL	An error occurred.
+ *   != NULL	An ASN1_TYPE structure.
+ */
+static ASN1_TYPE *
+attrib2type(X509_ATTRIBUTE *attr)
+{
+	ASN1_TYPE *ty = NULL;
+
+	if (attr == NULL || attr->single == 1)
+		return (NULL);
+
+	if (sk_ASN1_TYPE_num(attr->value.set) > 0)
+		ty = sk_ASN1_TYPE_value(attr->value.set, 0);
+
+	return (ty);
+}
+
+/*
+ * find_attr_by_nid - Given a ASN1_TYPE, return the offset of a X509_ATTRIBUTE
+ *     of the type specified by the given NID.
+ *
+ * Arguments:
+ *   attrs    - Stack of attributes to search
+ *   nid      - NID of the attribute being searched for
+ *
+ * Returns:
+ *   -1 	None found
+ *   != -1	Offset of the matching attribute.
+ */
+static int
+find_attr_by_nid(STACK_OF(X509_ATTRIBUTE) *attrs, int nid)
+{
+	X509_ATTRIBUTE *a;
+	int i;
+
+	if (attrs == NULL)
+		return (-1);
+
+	for (i = 0; i < sk_X509_ATTRIBUTE_num(attrs); i++) {
+		a = sk_X509_ATTRIBUTE_value(attrs, i);
+		if (OBJ_obj2nid(a->object) == nid)
+			return (i);
+	}
+	return (-1);
+}
+
+/*
+ * Called by our PKCS12 code to read our function and error codes
+ * into memory so that the OpenSSL framework can retrieve them.
+ */
+void
+ERR_load_SUNW_strings(void)
+{
+	assert(SUNW_lib_error_code == 0);
+#ifndef OPENSSL_NO_ERR
+	/*
+	 * Have OpenSSL provide us with a unique ID.
+	 */
+	SUNW_lib_error_code = ERR_get_next_error_library();
+
+	ERR_load_strings(SUNW_lib_error_code, SUNW_str_functs);
+	ERR_load_strings(SUNW_lib_error_code, SUNW_str_reasons);
+
+	SUNW_lib_name->error = ERR_PACK(SUNW_lib_error_code, 0, 0);
+	ERR_load_strings(0, SUNW_lib_name);
+#endif
+}
+
+/*
+ * The SUNWerr macro resolves to this routine. So when we need
+ * to push an error, this routine does it for us. Notice that
+ * the SUNWerr macro provides a filename and line #.
+ */
+void
+ERR_SUNW_error(int function, int reason, char *file, int line)
+{
+	assert(SUNW_lib_error_code != 0);
+#ifndef OPENSSL_NO_ERR
+	ERR_PUT_error(SUNW_lib_error_code, function, reason, file, line);
+#endif
+}
+
+/*
+ * check_time - Given an indication of the which time(s) to check, check
+ *      that time or those times against the current time and return the
+ *      relationship.
+ *
+ * Arguments:
+ *   chkwhat    - What kind of check to do.
+ *   cert	- The cert to check.
+ *
+ * Returns:
+ *   CHKERR_* values.
+ */
+static chk_errs_t
+check_time(chk_actions_t chkwhat, X509 *cert)
+{
+	int i;
+
+	if (chkwhat == CHK_NOT_BEFORE || chkwhat == CHK_BOTH) {
+		i = X509_cmp_time(X509_get_notBefore(cert), NULL);
+		if (i == 0)
+			return (CHKERR_TIME_BEFORE_BAD);
+		if (i > 0)
+			return (CHKERR_TIME_IS_BEFORE);
+
+		/* The current time is after the 'not before' time */
+	}
+
+	if (chkwhat == CHK_NOT_AFTER || chkwhat == CHK_BOTH) {
+		i = X509_cmp_time(X509_get_notAfter(cert), NULL);
+		if (i == 0)
+			return (CHKERR_TIME_AFTER_BAD);
+		if (i < 0)
+			return (CHKERR_TIME_HAS_EXPIRED);
+	}
+
+	return (CHKERR_TIME_OK);
+}
+
+/*
+ * find_attr - Look for a given attribute of the type associated with the NID.
+ *
+ * Arguments:
+ *   nid      - NID for the attribute to be found (either NID_friendlyName or
+ *              NID_locakKeyId)
+ *   str      - ASN1_STRING-type structure containing the value to be found,
+ *              FriendlyName expects a ASN1_BMPSTRING and localKeyID uses a
+ *              ASN1_STRING.
+ *   kl       - Points to a stack of private keys.
+ *   pkey     - Points at a location where the address of the matching private
+ *              key will be stored.
+ *   cl       - Points to a stack of client certs with matching private keys.
+ *   cert     - Points to locaiton where the address of the matching client cert
+ *              will be returned
+ *
+ * This function is designed to process lists of certs and private keys.
+ * This is made complex because these the attributes are stored differently
+ * for certs and for keys.  For certs, only a few attributes are retained.
+ * FriendlyName is stored in the aux structure, under the name 'alias'.
+ * LocalKeyId is also stored in the aux structure, under the name 'keyid'.
+ * A pkey structure has a stack of attributes.
+ *
+ * The basic approach is:
+ *   - If there there is no stack of certs but a stack of private keys exists,
+ *     search the stack of keys for a match. Alternately, if there is a stack
+ *     of certs and no private keys, search the certs.
+ *
+ *   - If there are both certs and keys, assume that the matching certs and
+ *     keys are in their respective stacks, with matching entries in the same
+ *     order.  Search for the name or keyid in the stack of certs.  If it is
+ *     not found, then this function returns 0 (nothing found).
+ *
+ *   - Once a cert is found, verify that the key actually matches by
+ *     comparing the private key with the public key (in the cert).
+ *     If they don't match, return an error.
+ *
+ *   A pointer to cert and/or pkey which matches the name or keyid is stored
+ *   in the return arguments.
+ *
+ * Returns:
+ *     0 - No matches were found.
+ *   > 0 - Bits set based on FOUND_* definitions, indicating what was found.
+ *         This can be FOUND_PKEY, FOUND_CERT or (FOUND_PKEY | FOUND_CERT).
+ */
+static int
+find_attr(int nid, ASN1_STRING *str, STACK_OF(EVP_PKEY) *kl, EVP_PKEY **pkey,
+    STACK_OF(X509) *cl, X509 **cert)
+{
+	ASN1_UTF8STRING *ustr = NULL;
+	ASN1_STRING *s;
+	ASN1_TYPE *t;
+	EVP_PKEY *p;
+	uchar_t *fname = NULL;
+	X509 *x;
+	int found = 0;
+	int chkcerts;
+	int len;
+	int res;
+	int c = -1;
+	int k = -1;
+
+	chkcerts = (cert != NULL || pkey != NULL) && cl != NULL;
+	if (chkcerts && nid == NID_friendlyName &&
+	    str->type == V_ASN1_BMPSTRING) {
+		ustr = ASN1_UTF8STRING_new();
+		if (ustr == NULL) {
+			SUNWerr(SUNW_F_FINDATTR, SUNW_R_MEMORY_FAILURE);
+			return (0);
+		}
+		len = ASN1_STRING_to_UTF8(&fname, str);
+		if (fname == NULL) {
+			ASN1_UTF8STRING_free(ustr);
+			SUNWerr(SUNW_F_FINDATTR, SUNW_R_STR_CONVERT_ERR);
+			return (0);
+		}
+
+		if (ASN1_STRING_set(ustr, fname, len) == 0) {
+			ASN1_UTF8STRING_free(ustr);
+			OPENSSL_free(fname);
+			SUNWerr(SUNW_F_FINDATTR, SUNW_R_MEMORY_FAILURE);
+			return (0);
+		}
+	}
+
+	if (chkcerts) {
+		for (c = 0; c < sk_X509_num(cl); c++) {
+			res = -1;
+			x = sk_X509_value(cl, c);
+			if (nid == NID_friendlyName && ustr != NULL) {
+				if (x->aux == NULL || x->aux->alias == NULL)
+					continue;
+				s = x->aux->alias;
+				if (s != NULL && s->type == ustr->type &&
+				    s->data != NULL) {
+					res = ASN1_STRING_cmp(s, ustr);
+				}
+			} else {
+				if (x->aux == NULL || x->aux->keyid == NULL)
+					continue;
+				s = x->aux->keyid;
+				if (s != NULL && s->type == str->type &&
+				    s->data != NULL) {
+					res = ASN1_STRING_cmp(s, str);
+				}
+			}
+			if (res == 0) {
+				if (cert != NULL)
+					*cert = sk_X509_delete(cl, c);
+				found = FOUND_CERT;
+				break;
+			}
+		}
+		if (ustr != NULL) {
+			ASN1_UTF8STRING_free(ustr);
+			OPENSSL_free(fname);
+		}
+	}
+
+	if (pkey != NULL && kl != NULL) {
+		/*
+		 * Looking for pkey to match a cert?  If so, assume that
+		 * lists of certs and their matching pkeys are in the same
+		 * order.  Call X509_check_private_key() to verify this
+		 * assumption.
+		 */
+		if (found != 0 && cert != NULL) {
+			k = c;
+			p = sk_EVP_PKEY_value(kl, k);
+			if (X509_check_private_key(x, p) != 0) {
+				if (pkey != NULL)
+					*pkey = sk_EVP_PKEY_delete(kl, k);
+				found |= FOUND_PKEY;
+			}
+		} else if (cert == NULL) {
+			for (k = 0; k < sk_EVP_PKEY_num(kl); k++) {
+				p = sk_EVP_PKEY_value(kl, k);
+				if (p == NULL || p->attributes == NULL)
+					continue;
+
+				t = PKCS12_get_attr_gen(p->attributes, nid);
+				if (t != NULL || ASN1_STRING_cmp(str,
+				    t->value.asn1_string) == 0)
+					continue;
+
+				found |= FOUND_PKEY;
+				if (pkey != NULL)
+					*pkey = sk_EVP_PKEY_delete(kl, k);
+				break;
+			}
+		}
+	}
+
+	return (found);
+}
+
+/*
+ * set_results - Given two pointers to stacks of private keys, certs or CA
+ *     CA certs, either copy the second stack to the first, or append the
+ *     contents of the second to the first.
+ *
+ * Arguments:
+ *   pkeys    - Points to stack of pkeys
+ *   work_kl  - Points to working stack of pkeys
+ *   certs    - Points to stack of certs
+ *   work_cl  - Points to working stack of certs
+ *   cacerts  - Points to stack of CA certs
+ *   work_ca  - Points to working stack of CA certs
+ *   xtrakeys - Points to stack of unmatcned pkeys
+ *   work_xl  - Points to working stack of unmatcned pkeys
+ *
+ *   The arguments are in pairs.  The first of each pair points to a stack
+ *   of keys or certs.  The second of the pair points at a 'working stack'
+ *   of the same type of entities.   Actions taken are as follows:
+ *
+ *   - If either the first or second argument is NULL, or if there are no
+ *     members in the second stack, there is nothing to do.
+ *   - If the first argument points to a pointer which is NULL, then there
+ *     is no existing stack for the first argument.  Copy the stack pointer
+ *     from the second argument to the first argument and NULL out the stack
+ *     pointer for the second.
+ *   - Otherwise, go through the elements of the second stack, removing each
+ *     and adding it to the first stack.
+ *
+ * Returns:
+ *   == -1 - An error occurred.  Call ERR_get_error() to get error information.
+ *   == 0  - No matching returns were found.
+ *    > 0  - This is the arithmetic 'or' of the FOUND_* bits that indicate which
+ *           of the requested entries were manipulated.
+ */
+static int
+set_results(STACK_OF(EVP_PKEY) **pkeys, STACK_OF(EVP_PKEY) **work_kl,
+    STACK_OF(X509) **certs, STACK_OF(X509) **work_cl,
+    STACK_OF(X509) **cacerts, STACK_OF(X509) **work_ca,
+    STACK_OF(EVP_PKEY) **xtrakeys, STACK_OF(EVP_PKEY) **work_xl)
+{
+	int retval = 0;
+
+	if (pkeys != NULL && work_kl != NULL && *work_kl != NULL &&
+	    sk_EVP_PKEY_num(*work_kl) > 0) {
+		if (*pkeys == NULL) {
+			*pkeys = *work_kl;
+			*work_kl = NULL;
+		} else {
+			if (sunw_append_keys(*pkeys, *work_kl) < 0) {
+				return (-1);
+			}
+		}
+		retval |= FOUND_PKEY;
+	}
+	if (certs != NULL && work_cl != NULL && *work_cl != NULL &&
+	    sk_X509_num(*work_cl) > 0) {
+		if (*certs == NULL) {
+			*certs = *work_cl;
+			*work_cl = NULL;
+		} else {
+			if (move_certs(*certs, *work_cl) < 0) {
+				return (-1);
+			}
+		}
+		retval |= FOUND_CERT;
+	}
+
+	if (cacerts != NULL && work_ca != NULL && *work_ca != NULL &&
+	    sk_X509_num(*work_ca) > 0) {
+		if (*cacerts == NULL) {
+			*cacerts = *work_ca;
+			*work_ca = NULL;
+		} else {
+			if (move_certs(*cacerts, *work_ca) < 0) {
+				return (-1);
+			}
+		}
+		retval |= FOUND_CA_CERTS;
+	}
+
+	if (xtrakeys != NULL && work_xl != NULL && *work_xl != NULL &&
+	    sk_EVP_PKEY_num(*work_xl) > 0) {
+		if (*xtrakeys == NULL) {
+			*xtrakeys = *work_xl;
+			*work_xl = NULL;
+		} else {
+			if (sunw_append_keys(*xtrakeys, *work_xl) < 0) {
+				return (-1);
+			}
+		}
+		retval |= FOUND_XPKEY;
+	}
+
+	return (retval);
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/usr/src/lib/libpkg/common/p12lib.h	Fri Jun 05 10:28:40 2009 -0400
@@ -0,0 +1,245 @@
+/*
+ * ====================================================================
+ * Copyright (c) 1999 The OpenSSL Project.  All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in
+ *    the documentation and/or other materials provided with the
+ *    distribution.
+ *
+ * 3. All advertising materials mentioning features or use of this
+ *    software must display the following acknowledgment:
+ *    "This product includes software developed by the OpenSSL Project
+ *    for use in the OpenSSL Toolkit. (http://www.OpenSSL.org/)"
+ *
+ * 4. The names "OpenSSL Toolkit" and "OpenSSL Project" must not be used to
+ *    endorse or promote products derived from this software without
+ *    prior written permission. For written permission, please contact
+ *    licensing@OpenSSL.org.
+ *
+ * 5. Products derived from this software may not be called "OpenSSL"
+ *    nor may "OpenSSL" appear in their names without prior written
+ *    permission of the OpenSSL Project.
+ *
+ * 6. Redistributions of any form whatsoever must retain the following
+ *    acknowledgment:
+ *    "This product includes software developed by the OpenSSL Project
+ *    for use in the OpenSSL Toolkit (http://www.OpenSSL.org/)"
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE OpenSSL PROJECT ``AS IS'' AND ANY
+ * EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE OpenSSL PROJECT OR
+ * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+ * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
+ * OF THE POSSIBILITY OF SUCH DAMAGE.
+ * ====================================================================
+ *
+ * This product includes cryptographic software written by Eric Young
+ * (eay@cryptsoft.com).  This product includes software written by Tim
+ * Hudson (tjh@cryptsoft.com).
+ *
+ */
+
+/*
+ * Copyright 2003 Sun Microsystems, Inc.  All rights reserved.
+ * Use is subject to license terms.
+ */
+
+#ifndef _P12LIB_H
+#define	_P12LIB_H
+
+
+#include <openssl/pkcs12.h>
+#include <openssl/pem.h>
+
+/*
+ * PKCS12 file routines borrowed from SNT's libwanboot.
+ */
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/* These declarations allow us to make stacks of EVP_PKEY objects */
+DECLARE_STACK_OF(EVP_PKEY)
+#define	sk_EVP_PKEY_new_null() SKM_sk_new_null(EVP_PKEY)
+#define	sk_EVP_PKEY_free(st) SKM_sk_free(EVP_PKEY, (st))
+#define	sk_EVP_PKEY_num(st) SKM_sk_num(EVP_PKEY, (st))
+#define	sk_EVP_PKEY_value(st, i) SKM_sk_value(EVP_PKEY, (st), (i))
+#define	sk_EVP_PKEY_push(st, val) SKM_sk_push(EVP_PKEY, (st), (val))
+#define	sk_EVP_PKEY_find(st, val) SKM_sk_find(EVP_PKEY, (st), (val))
+#define	sk_EVP_PKEY_delete(st, i) SKM_sk_delete(EVP_PKEY, (st), (i))
+#define	sk_EVP_PKEY_delete_ptr(st, ptr) SKM_sk_delete_ptr(EVP_PKEY, (st), (ptr))
+#define	sk_EVP_PKEY_insert(st, val, i) SKM_sk_insert(EVP_PKEY, (st), (val), (i))
+#define	sk_EVP_PKEY_pop_free(st, free_func) SKM_sk_pop_free(EVP_PKEY, (st), \
+	    (free_func))
+#define	sk_EVP_PKEY_pop(st) SKM_sk_pop(EVP_PKEY, (st))
+
+/* Error reporting routines required by OpenSSL */
+#define	SUNW_LIB_NAME	"SUNW_PKCS12"
+#define	SUNWerr(f, r)	ERR_SUNW_error((f), (r), __FILE__, __LINE__)
+
+/* Error codes for the SUNW functions. */
+/* OpenSSL prefers codes to start at 100 */
+
+/* Function codes. */
+typedef enum {
+	SUNW_F_USE_X509CERT = 100,
+	SUNW_F_USE_PKEY,
+	SUNW_F_USE_TASTORE,
+	SUNW_F_USE_CERTFILE,
+	SUNW_F_USE_KEYFILE,
+	SUNW_F_USE_TRUSTFILE,
+	SUNW_F_READ_FILE,
+	SUNW_F_DOPARSE,
+	SUNW_F_PKCS12_PARSE,
+	SUNW_F_PKCS12_CONTENTS,
+	SUNW_F_PARSE_ONE_BAG,
+	SUNW_F_PKCS12_CREATE,
+	SUNW_F_SPLIT_CERTS,
+	SUNW_F_FIND_LOCALKEYID,
+	SUNW_F_SET_LOCALKEYID,
+	SUNW_F_SET_FNAME,
+	SUNW_F_GET_LOCALKEYID,
+	SUNW_F_GET_PKEY_FNAME,
+	SUNW_F_APPEND_KEYS,
+	SUNW_F_PEM_CONTENTS,
+	SUNW_F_PEM_INFO,
+	SUNW_F_ASC2BMPSTRING,
+	SUNW_F_UTF82ASCSTR,
+	SUNW_F_FINDATTR,
+	SUNW_F_TYPE2ATTRIB,
+	SUNW_F_MOVE_CERTS,
+	SUNW_F_FIND_FNAME,
+	SUNW_F_PARSE_OUTER,
+	SUNW_F_CHECKFILE
+} sunw_err_func_t;
+
+/* Reason codes. */
+typedef enum {
+	SUNW_R_INVALID_ARG = 100,
+	SUNW_R_MEMORY_FAILURE,
+	SUNW_R_MAC_VERIFY_FAILURE,
+	SUNW_R_MAC_CREATE_FAILURE,
+	SUNW_R_BAD_FILETYPE,
+	SUNW_R_BAD_PKEY,
+	SUNW_R_BAD_PKEYTYPE,
+	SUNW_R_PKEY_READ_ERR,
+	SUNW_R_NO_TRUST_ANCHOR,
+	SUNW_R_READ_TRUST_ERR,
+	SUNW_R_ADD_TRUST_ERR,
+	SUNW_R_PKCS12_PARSE_ERR,
+	SUNW_R_PKCS12_CREATE_ERR,
+	SUNW_R_PARSE_BAG_ERR,
+	SUNW_R_MAKE_BAG_ERR,
+	SUNW_R_BAD_CERTTYPE,
+	SUNW_R_PARSE_CERT_ERR,
+	SUNW_R_BAD_LKID,
+	SUNW_R_SET_LKID_ERR,
+	SUNW_R_BAD_FNAME,
+	SUNW_R_SET_FNAME_ERR,
+	SUNW_R_BAD_TRUST,
+	SUNW_R_BAD_BAGTYPE,
+	SUNW_R_CERT_ERR,
+	SUNW_R_PKEY_ERR,
+	SUNW_R_READ_ERR,
+	SUNW_R_ADD_ATTR_ERR,
+	SUNW_R_STR_CONVERT_ERR,
+	SUNW_R_PKCS12_EMPTY_ERR,
+	SUNW_R_PASSWORD_ERR
+} sunw_err_reason_t;
+
+/*
+ * Type of checking to perform when calling sunw_check_cert_times
+ */
+typedef enum {
+	CHK_NOT_BEFORE = 1,	/* Check 'not before' date */
+	CHK_NOT_AFTER,		/* Check 'not after' date */
+	CHK_BOTH		/* Check both dates */
+} chk_actions_t;
+
+/*
+ * Return type for sunw_check_cert_times
+ */
+typedef enum {
+	CHKERR_TIME_OK = 0,	/* Current time meets requested checks */
+	CHKERR_TIME_BEFORE_BAD,	/* 'not before' field is invalid */
+	CHKERR_TIME_AFTER_BAD,	/* 'not after' field is invalid */
+	CHKERR_TIME_IS_BEFORE,	/* Current time is before 'not before' */
+	CHKERR_TIME_HAS_EXPIRED	/* Current time is after 'not after' */
+} chk_errs_t;
+
+/*
+ * This type indicates what to do with an attribute being returned.
+ */
+typedef enum {
+	GETDO_COPY = 1,		/* Simply return the value of the attribute */
+	GETDO_DEL		/* Delete the attribute at the same time. */
+} getdo_actions_t;
+
+/*
+ * For sunw_pkcs12_parse, the following are values for bits that indicate
+ * various types of searches/matching to do. Any of these values can be
+ * OR'd together. However, the order in which an attempt will be made
+ * to satisfy them is the order in which they are listed below. The
+ * exception is DO_NONE. It should not be OR'd with any other value.
+ */
+#define	DO_NONE		0x00	/* Don't even try to match */
+#define	DO_FIND_KEYID	0x01	/* 1st cert, key with matching localkeyid */
+#define	DO_FIND_FN	0x02	/* 1st cert, key with matching friendlyname */
+#define	DO_FIRST_PAIR	0x04	/* Return first matching cert/key pair found */
+#define	DO_LAST_PAIR	0x08	/* Return last matching cert/key pair found */
+#define	DO_UNMATCHING	0x10	/* Return first cert and/or key */
+
+/* Bits returned, which indicate what values were found. */
+#define	FOUND_PKEY	0x01	/* Found one or more private key */
+#define	FOUND_CERT	0x02	/* Found one or more client certificate */
+#define	FOUND_CA_CERTS	0x04	/* Added at least one cert to the CA list */
+#define	FOUND_XPKEY	0x08	/* Found at least one private key which does */
+				/* not match a certificate in the certs list */
+
+/* p12lib.c */
+PKCS12	*sunw_PKCS12_create(const char *, STACK_OF(EVP_PKEY) *,
+    STACK_OF(X509) *, STACK_OF(X509) *);
+
+int	sunw_split_certs(STACK_OF(EVP_PKEY) *, STACK_OF(X509) *,
+    STACK_OF(X509) **, STACK_OF(EVP_PKEY) **);
+
+void	sunw_evp_pkey_free(EVP_PKEY *);
+int	sunw_set_localkeyid(const char *, int, EVP_PKEY *, X509 *);
+int	sunw_get_pkey_localkeyid(getdo_actions_t, EVP_PKEY *, char **, int *);
+int	sunw_get_pkey_fname(getdo_actions_t, EVP_PKEY *, char **);
+int	sunw_find_localkeyid(char *, int, STACK_OF(EVP_PKEY) *,
+    STACK_OF(X509) *, EVP_PKEY **, X509 **);
+int	sunw_find_fname(char *, STACK_OF(EVP_PKEY) *, STACK_OF(X509) *,
+    EVP_PKEY **, X509 **);
+int	sunw_set_fname(const char *, EVP_PKEY *, X509 *);
+int	sunw_check_keys(X509 *, EVP_PKEY *);
+
+chk_errs_t	sunw_check_cert_times(chk_actions_t, X509 *);
+extern void	ERR_SUNW_error(int function, int reason, char *file, int line);
+extern void	ERR_load_SUNW_strings(void);
+int		sunw_PKCS12_contents(PKCS12 *, const char *,
+    STACK_OF(EVP_PKEY) **, STACK_OF(X509) **);
+int		sunw_get_cert_fname(getdo_actions_t, X509 *, char **);
+int		sunw_PEM_contents(FILE *, pem_password_cb, void *,
+    STACK_OF(EVP_PKEY) **, STACK_OF(X509) **);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* _P12LIB_H */
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/usr/src/lib/libpkg/common/pkgerr.c	Fri Jun 05 10:28:40 2009 -0400
@@ -0,0 +1,135 @@
+/*
+ * 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
+ * or http://www.opensolaris.org/os/licensing.
+ * 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 2009 Sun Microsystems, Inc.  All rights reserved.
+ * Use is subject to license terms.
+ */
+
+
+/*
+ * Module:	pkgerr.c
+ * Description:
+ *	Module for handling error messages that come from libpkg libraries.
+ */
+
+#include <stdio.h>
+#include <string.h>
+#include <sys/types.h>
+#include <locale.h>
+#include <libintl.h>
+#include <stdlib.h>
+#include <sys/varargs.h>
+#include "pkgerr.h"
+
+/* max length of any formatted error message */
+#define	MAX_ERRMSGLEN	1024
+
+/* private structures (not visible outside this file) */
+struct _pkg_err_struct {
+    int			nerrs;
+    char		**msgs;
+    PKG_ERR_CODE	*errs;
+};
+
+/* ---------------------- public functions ----------------------- */
+
+PKG_ERR
+*pkgerr_new()
+{
+	PKG_ERR	*newerr;
+
+	newerr = (PKG_ERR *)malloc(sizeof (PKG_ERR));
+	newerr->nerrs = 0;
+	newerr->msgs = NULL;
+	newerr->errs = NULL;
+	return (newerr);
+}
+
+void
+pkgerr_add(PKG_ERR *err, PKG_ERR_CODE code, char *fmt, ...)
+{
+	char		errmsgbuf[1024];
+	va_list		ap;
+
+	va_start(ap, fmt);
+	(void) vsnprintf(errmsgbuf, MAX_ERRMSGLEN, fmt, ap);
+	va_end(ap);
+
+	err->nerrs++;
+
+	err->msgs = (char **)realloc(err->msgs,
+	    err->nerrs * sizeof (char *));
+	err->errs = (PKG_ERR_CODE *)realloc(err->errs,
+	    err->nerrs * sizeof (PKG_ERR_CODE));
+	err->msgs[err->nerrs - 1] = strdup(errmsgbuf);
+	err->errs[err->nerrs - 1] = code;
+}
+
+void
+pkgerr_clear(PKG_ERR *err)
+{
+	int i;
+
+	for (i = 0; i < err->nerrs; i++) {
+		free(err->msgs[i]);
+	}
+
+	free(err->msgs);
+	free(err->errs);
+	err->msgs = NULL;
+	err->errs = NULL;
+	err->nerrs = 0;
+}
+
+int
+pkgerr_dump(PKG_ERR *err, FILE *fp)
+{
+	int i;
+
+	for (i = 0; i < err->nerrs; i++) {
+		(void) fprintf(fp, err->msgs[i]);
+	}
+	return (0);
+}
+
+int
+pkgerr_num(PKG_ERR *err)
+{
+	return (err->nerrs);
+}
+
+char
+*pkgerr_get(PKG_ERR *err, int pos)
+{
+	if (pos < 0 || pos > (err->nerrs - 1)) {
+		return (NULL);
+	}
+
+	return (err->msgs[pos]);
+}
+
+void
+pkgerr_free(PKG_ERR *err)
+{
+	pkgerr_clear(err);
+	free(err);
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/usr/src/lib/libpkg/common/pkgerr.h	Fri Jun 05 10:28:40 2009 -0400
@@ -0,0 +1,104 @@
+/*
+ * 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
+ * or http://www.opensolaris.org/os/licensing.
+ * 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 2003 Sun Microsystems, Inc.  All rights reserved.
+ * Use is subject to license terms.
+ */
+
+#ifndef _PKGERR_H
+#define	_PKGERR_H
+
+
+/*
+ * Module:	pkgerr.h
+ * Description:
+ *
+ *   Implements error routines to handle the creation,
+ *   management, and destruction of error objects, which
+ *   hold error messages and codes returned from libpkg
+ *   routines that support the objects defined herein.
+ */
+
+#include <stdio.h>
+
+#ifdef	__cplusplus
+extern "C" {
+#endif
+
+/*
+ * Public Definitions
+ */
+
+typedef enum {
+	PKGERR_OK = 0,
+	PKGERR_EXIST,
+	PKGERR_READ,
+	PKGERR_CORRUPT,
+	PKGERR_PARSE,
+	PKGERR_BADPASS,
+	PKGERR_BADALIAS,
+	PKGERR_INTERNAL,
+	PKGERR_UNSUP,
+	PKGERR_NOALIAS,
+	PKGERR_NOALIASMATCH,
+	PKGERR_MULTIPLE,
+	PKGERR_INCOMPLETE,
+	PKGERR_NOPRIVKEY,
+	PKGERR_NOPUBKEY,
+	PKGERR_NOCACERT,
+	PKGERR_NOMEM,
+	PKGERR_CHAIN,
+	PKGERR_LOCKED,
+	PKGERR_WRITE,
+	PKGERR_UNLOCK,
+	PKGERR_TIME,
+	PKGERR_DUPLICATE,
+	PKGERR_WEB,
+	PKGERR_VERIFY
+} PKG_ERR_CODE;
+
+/*
+ * Public Structures
+ */
+
+/* external reference to PKG_ERR object (contents private) */
+typedef PKG_ERR_CODE pkg_err_t;
+
+typedef struct _pkg_err_struct PKG_ERR;
+
+/*
+ * Public Methods
+ */
+
+PKG_ERR		*pkgerr_new();
+void		pkgerr_add(PKG_ERR *, PKG_ERR_CODE, char *, ...);
+void		pkgerr_clear(PKG_ERR *);
+int		pkgerr_dump(PKG_ERR *, FILE *);
+int		pkgerr_num(PKG_ERR *);
+char		*pkgerr_get(PKG_ERR *, int);
+void		pkgerr_free(PKG_ERR *);
+
+#ifdef	__cplusplus
+}
+#endif
+
+#endif /* _PKGERR_H */
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/usr/src/lib/libpkg/common/pkgexecl.c	Fri Jun 05 10:28:40 2009 -0400
@@ -0,0 +1,80 @@
+/*
+ * 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
+ * or http://www.opensolaris.org/os/licensing.
+ * 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 2004 Sun Microsystems, Inc. All rights reserved.
+ * Use is subject to license terms.
+ */
+
+/* Copyright (c) 1984, 1986, 1987, 1988, 1989 AT&T */
+/* All Rights Reserved */
+
+
+#include <stdio.h>
+#include <errno.h>
+#include <string.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <stdarg.h>
+#include <wait.h>
+#include <signal.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <pwd.h>
+#include <grp.h>
+#include <fcntl.h>
+#include "pkglocale.h"
+#include "pkglibmsgs.h"
+#include "pkglib.h"
+
+#define	MAXARGS	64
+
+/*VARARGS4*/
+int
+pkgexecl(char *filein, char *fileout, char *uname, char *gname, ...)
+{
+	char		*arg[MAXARGS+1];
+	char		*pt;
+	int		n;
+	va_list		ap;
+
+	/* construct arg[] array from varargs passed in */
+
+	va_start(ap, gname);
+
+	n = 0;
+	while ((pt = va_arg(ap, char *)) != NULL) {
+		if (n >= MAXARGS) {
+			va_end(ap);
+			progerr(pkg_gt(ERR_TOO_MANY_ARGS),
+				arg[0] ? arg[0] : "??");
+			return (-1);
+		}
+		arg[n++] = pt;
+	}
+
+	arg[n] = NULL;
+	va_end(ap);
+
+	/* return results of executing command based on arg[] list */
+
+	return (pkgexecv(filein, fileout, uname, gname, arg));
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/usr/src/lib/libpkg/common/pkgexecv.c	Fri Jun 05 10:28:40 2009 -0400
@@ -0,0 +1,445 @@
+/*
+ * 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
+ * or http://www.opensolaris.org/os/licensing.
+ * 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 2004 Sun Microsystems, Inc. All rights reserved.
+ * Use is subject to license terms.
+ */
+
+/* Copyright (c) 1984, 1986, 1987, 1988, 1989 AT&T */
+/* All Rights Reserved */
+
+
+#include <stdio.h>
+#include <errno.h>
+#include <string.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <signal.h>
+#include <wait.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+#include <pwd.h>
+#include <grp.h>
+#include "pkglib.h"
+#include "pkglibmsgs.h"
+#include "pkglocale.h"
+
+/* global environment inherited by this process */
+extern char	**environ;
+
+/* dstream.c */
+extern int	ds_curpartcnt;
+extern int	ds_close(int pkgendflg);
+
+/*
+ * global internal (private) variables
+ */
+
+/* received signal count - bumped with hooked signals are caught */
+
+static int	sig_received = 0;
+
+/*
+ * Name:	sig_trap
+ * Description:	hooked up to signal counts number of signals received
+ * Arguments:	a_signo - [RO, *RO] - (int)
+ *			Integer representing the signal received; see signal(3c)
+ * Returns:	<void>
+ */
+
+static void
+sig_trap(int a_signo)
+{
+	sig_received++;
+}
+
+/*
+ * Name:	pkgexecv
+ * Description:	Asynchronously execute a package command in a separate process
+ *		and return results - the subprocess MUST arm it's own SIGINT
+ *		and SIGHUP signals and must return a standard package command
+ *		exit code (see returns below)
+ *		Only another package command (such as pkginstall, pkgremove,
+ *		etc.) may be called via this interface. No files are closed
+ *		because open files are passed across to certain commands using
+ *		either implicit agreements between the two (yuk!) or by using
+ *		the '-p' option which passes a string of digits, some of which
+ *		represent open file descriptors passed through this interface!
+ * Arguments:	filein - [RO, *RO] - (char *)
+ *			Pointer to string representing the name of the file to
+ *			use for the package commands's stdin
+ *			== (char *)NULL or == "" - the current stdin
+ *			is used for the new package command process
+ *		fileout - [RO, *RO] - (char *)
+ *			Pointer to string representing the name of the file to
+ *			use for the package commands's stdout and stderr
+ *			== (char *)NULL or == "" - the current stdout/stderr
+ *			is used for the new package command process
+ *		uname - [RO, *RO] - (char *)
+ *			Pointer to string representing the user name to execute
+ *			the package command as - the user name is looked up
+ *			using the ncgrpw:cpwnam() interface
+ *			== (char *)NULL or == "" - the user name of the current
+ *			process is used for the new package command process
+ *		gname - [RO, *RO] - (char *)
+ *			Pointer to string representing the group name to execute
+ *			the package command as - the group name is looked up
+ *			using the ncgrpw:cgrnam() interface
+ *			== (char *)NULL or == "" - the group name of the current
+ *			process is used for the new package command process
+ *		arg - [RO, *RO] - (char **)
+ *			Pointer to array of character pointers representing the
+ *			arguments to pass to the package command - the array is
+ *			terminated with a pointer to (char *)NULL
+ * Returns:	int
+ *			== 99 - exec() of package command failed
+ *			== -1 - fork failed or other fatal error during
+ *				execution of the package command
+ *			otherwise - exit code from package command:
+ *			0 - successful
+ *			1 - package operation failed (fatal error)
+ *			2 - non-fatal error (warning)
+ *			3 - operation interrupted (including SIGINT/SIGHUP)
+ *			4 - admin settings prevented operation
+ *			5 - administration required and -n was specified
+ *			IN addition:
+ *			10 is added to the return code if reboot after the
+ *				installation of all packages is required
+ *			20 is added to the return code if immediate reboot
+ *				after installation of this package is required
+ */
+
+int
+pkgexecv(char *filein, char *fileout, char *uname, char *gname, char *arg[])
+{
+	int			exit_no;
+	int			n;
+	int			status;
+	pid_t			pid;
+	pid_t			waitstat;
+	struct group		*grp;
+	struct passwd		*pwp;
+	struct sigaction	nact;
+	struct sigaction	oact;
+	void			(*funcSighup)();
+	void			(*funcSigint)();
+
+	/* flush standard i/o before creating new process */
+
+	(void) fflush(stdout);
+	(void) fflush(stderr);
+
+	/*
+	 * hold SIGINT/SIGHUP signals and reset signal received counter;
+	 * after the vfork() the parent and child need to setup their respective
+	 * interrupt handling and release the hold on the signals
+	 */
+
+	(void) sighold(SIGINT);
+	(void) sighold(SIGHUP);
+
+	sig_received = 0;
+
+	/*
+	 * create new process to execute command in;
+	 * vfork() is being used to avoid duplicating the parents
+	 * memory space - this means that the child process may
+	 * not modify any of the parents memory including the
+	 * standard i/o descriptors - all the child can do is
+	 * adjust interrupts and open files as a prelude to a
+	 * call to exec().
+	 */
+
+	pid = vfork();
+
+	if (pid < 0) {
+		/*
+		 * *************************************************************
+		 * fork failed!
+		 * *************************************************************
+		 */
+
+		progerr(pkg_gt(ERR_FORK_FAILED), errno, strerror(errno));
+
+		/* release hold on signals */
+
+		(void) sigrelse(SIGHUP);
+		(void) sigrelse(SIGINT);
+
+		return (-1);
+	}
+
+	if (pid > 0) {
+		/*
+		 * *************************************************************
+		 * This is the forking (parent) process
+		 * *************************************************************
+		 */
+
+		/* close datastream if any portion read */
+
+		if (ds_curpartcnt >= 0) {
+			if (ds_close(0) != 0) {
+				/* kill child process */
+
+				(void) sigsend(P_PID, pid, SIGKILL);
+
+				/* release hold on signals */
+
+				(void) sigrelse(SIGHUP);
+				(void) sigrelse(SIGINT);
+
+				return (-1);
+			}
+		}
+
+		/*
+		 * setup signal handlers for SIGINT and SIGHUP and release hold
+		 */
+
+		/* hook SIGINT to sig_trap() */
+
+		nact.sa_handler = sig_trap;
+		nact.sa_flags = SA_RESTART;
+		(void) sigemptyset(&nact.sa_mask);
+
+		if (sigaction(SIGINT, &nact, &oact) < 0) {
+			funcSigint = SIG_DFL;
+		} else {
+			funcSigint = oact.sa_handler;
+		}
+
+		/* hook SIGHUP to sig_trap() */
+
+		nact.sa_handler = sig_trap;
+		nact.sa_flags = SA_RESTART;
+		(void) sigemptyset(&nact.sa_mask);
+
+		if (sigaction(SIGHUP, &nact, &oact) < 0) {
+			funcSighup = SIG_DFL;
+		} else {
+			funcSighup = oact.sa_handler;
+		}
+
+		/* release hold on signals */
+
+		(void) sigrelse(SIGHUP);
+		(void) sigrelse(SIGINT);
+
+		/*
+		 * wait for the process to exit, reap child exit status
+		 */
+
+		for (;;) {
+			status = 0;
+			waitstat = waitpid(pid, (int *)&status, 0);
+			if (waitstat < 0) {
+				/* waitpid returned error */
+				if (errno == EAGAIN) {
+					/* try again */
+					continue;
+				}
+				if (errno == EINTR) {
+					continue;
+				}
+				/* error from waitpid: bail */
+				break;
+			} else if (waitstat == pid) {
+				/* child exit status available */
+				break;
+			}
+		}
+
+		/*
+		 * reset signal handlers
+		 */
+
+		/* reset SIGINT */
+
+		nact.sa_handler = funcSigint;
+		nact.sa_flags = SA_RESTART;
+		(void) sigemptyset(&nact.sa_mask);
+
+		(void) sigaction(SIGINT, &nact, (struct sigaction *)NULL);
+
+		/* reset SIGHUP */
+
+		nact.sa_handler = funcSighup;
+		nact.sa_flags = SA_RESTART;
+		(void) sigemptyset(&nact.sa_mask);
+
+		(void) sigaction(SIGHUP, &nact, (struct sigaction *)NULL);
+
+		/* error if child process does not match */
+
+		if (waitstat != pid) {
+			progerr(pkg_gt(ERR_WAIT_FAILED), pid, waitstat, status,
+				errno, strerror(errno));
+			return (-1);
+		}
+
+		/*
+		 * determine final exit code:
+		 * - if signal received, then return interrupted (3)
+		 * - if child exit status is available, return exit child status
+		 * - otherwise return error (-1)
+		 */
+
+		if (sig_received != 0) {
+			exit_no = 3;	/* interrupted */
+		} else if (WIFEXITED(status)) {
+			exit_no = WEXITSTATUS(status);
+		} else {
+			exit_no = -1;	/* exec() or other process error */
+		}
+
+		return (exit_no);
+	}
+
+	/*
+	 * *********************************************************************
+	 * This is the forked (child) process
+	 * *********************************************************************
+	 */
+
+	/* reset all signals to default */
+
+	for (n = 0; n < NSIG; n++) {
+		(void) sigset(n, SIG_DFL);
+	}
+
+	/* release hold on signals held by parent before fork() */
+
+	(void) sigrelse(SIGHUP);
+	(void) sigrelse(SIGINT);
+
+	/*
+	 * The caller wants to have stdin connected to filein.
+	 */
+
+	if (filein && *filein) {
+		/*
+		 * If input is supposed to be connected to /dev/tty
+		 */
+		if (strncmp(filein, "/dev/tty", 8) == 0) {
+			/*
+			 * If stdin is connected to a tty device.
+			 */
+			if (isatty(STDIN_FILENO)) {
+				/*
+				 * Reopen it to /dev/tty.
+				 */
+				n = open(filein, O_RDONLY);
+				if (n >= 0) {
+					(void) dup2(n, STDIN_FILENO);
+				}
+			}
+		} else {
+			/*
+			 * If we did not want to be connected to /dev/tty, we
+			 * connect input to the requested file no questions.
+			 */
+			n = open(filein, O_RDONLY);
+			if (n >= 0) {
+				(void) dup2(n, STDIN_FILENO);
+			}
+		}
+	}
+
+	/*
+	 * The caller wants to have stdout and stderr connected to fileout.
+	 * If "fileout" is "/dev/tty" then reconnect stdout to "/dev/tty"
+	 * only if /dev/tty is not already associated with "a tty".
+	 */
+
+	if (fileout && *fileout) {
+		/*
+		 * If output is supposed to be connected to /dev/tty
+		 */
+		if (strncmp(fileout, "/dev/tty", 8) == 0) {
+			/*
+			 * If stdout is connected to a tty device.
+			 */
+			if (isatty(STDOUT_FILENO)) {
+				/*
+				 * Reopen it to /dev/tty if /dev/tty available.
+				 */
+				n = open(fileout, O_WRONLY);
+				if (n >= 0) {
+					/*
+					 * /dev/tty is available - close the
+					 * current standard output stream, and
+					 * reopen it on /dev/tty
+					 */
+					(void) dup2(n, STDOUT_FILENO);
+				}
+			}
+			/*
+			 * not connected to tty device - probably redirect to
+			 * file - preserve existing output device
+			 */
+		} else {
+			/*
+			 * If we did not want to be connected to /dev/tty, we
+			 * connect output to the requested file no questions.
+			 */
+			/* LINTED O_CREAT without O_EXCL specified in call to */
+			n = open(fileout, O_WRONLY|O_CREAT|O_APPEND, 0666);
+			if (n >= 0) {
+				(void) dup2(n, STDOUT_FILENO);
+			}
+		}
+
+		/*
+		 * Dup stderr from stdout.
+		 */
+
+		(void) dup2(STDOUT_FILENO, STDERR_FILENO);
+	}
+
+	/*
+	 * do NOT close all file descriptors except stdio
+	 * file descriptors are passed in to some subcommands
+	 * (see dstream:ds_getinfo() and dstream:ds_putinfo())
+	 */
+
+	/* set group/user i.d. if requested */
+
+	if (gname && *gname && (grp = cgrnam(gname)) != NULL) {
+		if (setgid(grp->gr_gid) == -1) {
+			progerr(pkg_gt(ERR_SETGID), grp->gr_gid);
+		}
+	}
+	if (uname && *uname && (pwp = cpwnam(uname)) != NULL) {
+		if (setuid(pwp->pw_uid) == -1) {
+			progerr(pkg_gt(ERR_SETUID), pwp->pw_uid);
+		}
+	}
+
+	/* execute target executable */
+
+	(void) execve(arg[0], arg, environ);
+	progerr(pkg_gt(ERR_EX_FAIL), arg[0], errno);
+	_exit(99);
+	/*NOTREACHED*/
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/usr/src/lib/libpkg/common/pkglib.h	Fri Jun 05 10:28:40 2009 -0400
@@ -0,0 +1,622 @@
+/*
+ * 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
+ * or http://www.opensolaris.org/os/licensing.
+ * 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 2009 Sun Microsystems, Inc.  All rights reserved.
+ * Use is subject to license terms.
+ */
+
+/* Copyright (c) 1984, 1986, 1987, 1988, 1989 AT&T */
+/* All Rights Reserved */
+
+#ifndef	_PKGLIB_H
+#define	_PKGLIB_H
+
+
+#ifdef	__cplusplus
+extern "C" {
+#endif
+
+#include <sys/types.h>
+#include <limits.h>
+#include <stdio.h>
+#include <pkgdev.h>
+#include <pkgstrct.h>
+#include <openssl/bio.h>
+#include <openssl/x509.h>
+#include <netdb.h>
+#include <boot_http.h>
+#include "pkgerr.h"
+#include "keystore.h"
+#include "cfext.h"
+
+/*
+ * Virtual File Protocol definitions
+ */
+
+/*
+ * flags associated with virtual file protocol operations; note that these flags
+ * may only occupy the low order 16 bits of the 32-bit unsigned flag.
+ */
+
+typedef unsigned long VFPFLAGS_T;
+
+#define	VFP_NONE	0x00000000	/* no special flags */
+#define	VFP_NEEDNOW	0x00000001	/* need memory now */
+#define	VFP_SEQUENTIAL	0x00000002	/* sequential access */
+#define	VFP_RANDOM	0x00000004	/* random access */
+#define	VFP_NOMMAP	0x00000008	/* do not use mmap to access file */
+#define	VFP_NOMALLOC	0x00000010	/* do not use malloc to buffer file */
+
+/* virtual file protocol object */
+
+typedef struct _vfp VFP_T;
+
+/* structure behind the virtual file protocol object */
+
+struct _vfp {
+	FILE		*_vfpFile;	/* -> opened FILE */
+	char		*_vfpCurr;	/* -> current byte to read/write */
+	char		*_vfpHighWater;	/* -> last byte modified */
+	char		*_vfpEnd;	/* -> last data byte */
+	char		*_vfpPath;	/* -> path associated with FILE */
+	char		*_vfpStart;	/* -> first data byte */
+	void		*_vfpExtra;	/* undefined */
+	size_t		_vfpSize;	/* size of mapped/allocated area */
+	size_t		_vfpMapSize;	/* # mapped bytes */
+	VFPFLAGS_T	_vfpFlags;	/* flags associated with vfp/data */
+	int		_vfpOverflow;	/* non-zero if buffer write overflow */
+	blkcnt_t	_vfpCkStBlocks;	/* checkpoint # blocks */
+	dev_t		_vfpCkDev;	/* checkpoint device i.d. */
+	ino_t		_vfpCkIno;	/* checkpoint inode # */
+	off_t		_vfpCkSize;	/* checkpoint size */
+	time_t		_vfpCkMtime;	/* checkpoint modification time */
+};
+
+/*
+ * get highest modified byte (length) contained in vfp
+ *
+ * determine number of bytes to write - it will be the highest of:
+ *  -- the current pointer into the file - this is updated whenever
+ *	the location of the file is changed by a single byte
+ *  -- the last "high water mark" - the last known location that
+ *	was written to the file - updated only when the location
+ *	of the file is directly changed - e.g. vfpSetCurrCharPtr,
+ *	vfpTruncate, vfpRewind.
+ * this reduces the "bookkeeping" that needs to be done to know
+ * how many bytes to write out to the file - typically a file is
+ * written sequentially so the current file pointer is sufficient
+ * to determine how many bytes to write out.
+ */
+
+#define	vfpGetModifiedLen(VFP)						\
+	(size_t)(((VFP)->_vfpHighWater > (VFP)->_vfpCurr) ?		\
+		(((ptrdiff_t)(VFP)->_vfpHighWater -			\
+				(ptrdiff_t)(VFP)->_vfpStart)) :		\
+		(((ptrdiff_t)(VFP)->_vfpCurr -				\
+				(ptrdiff_t)(VFP)->_vfpStart)))
+
+/*
+ * increment current pointer by specified delta
+ * if the delta exceeds the buffer size, set pointer to buffer end
+ */
+#define	vfpIncCurrPtrBy(VFP, INC)					\
+	{								\
+		((VFP)->_vfpCurr) += (INC);				\
+		if (((VFP)->_vfpCurr) > ((VFP)->_vfpEnd)) {		\
+			(VFP)->_vfpCurr = (VFP)->_vfpEnd;		\
+			(VFP)->_vfpOverflow = 1;			\
+		}							\
+		if ((VFP)->_vfpHighWater < (VFP)->_vfpCurr) {		\
+			(VFP)->_vfpHighWater = (VFP)->_vfpCurr;		\
+		}							\
+	}
+
+/* get the path associated with the vfp */
+#define	vfpGetPath(VFP)		((VFP)->_vfpPath)
+
+/* get a string from the vfp into a fixed size buffer */
+#define	vfpGets(VFP, PTR, LEN)						\
+	{								\
+		char	*XXpXX = (PTR);					\
+		size_t	XXlXX = (LEN);					\
+		while ((*(VFP)->_vfpCurr != '\0') &&			\
+				(*(VFP)->_vfpCurr != '\n')) {		\
+			if (XXlXX > 1) {				\
+				*XXpXX++ = *(VFP)->_vfpCurr;		\
+				XXlXX--;				\
+			}						\
+			(VFP)->_vfpCurr++;				\
+		}							\
+		*XXpXX++ = '\0';					\
+		if (*(VFP)->_vfpCurr != '\0') {				\
+			(VFP)->_vfpCurr++;				\
+		}							\
+	}
+
+/* get number of bytes remaining to read */
+#define	vfpGetBytesRemaining(VFP)	\
+	(((((VFP)->_vfpHighWater) <= ((VFP)->_vfpCurr))) ? 0 : 		\
+	((((ptrdiff_t)(VFP)->_vfpHighWater)-((ptrdiff_t)(VFP)->_vfpCurr))))
+
+/* get number of bytes remaining to write */
+#define	vfpGetBytesAvailable(VFP)	\
+	(((((VFP)->_vfpEnd) <= ((VFP)->_vfpCurr))) ? 0 : 		\
+	((((ptrdiff_t)(VFP)->_vfpEnd)-((ptrdiff_t)(VFP)->_vfpCurr))))
+
+/* put current character and increment to next */
+#define	vfpPutc(VFP, C)							\
+	{								\
+		(*(VFP)->_vfpCurr) = ((char)(C));			\
+		vfpIncCurrPtrBy((VFP), 1);				\
+	}
+
+/* put integer to current character and increment */
+#define	vfpPutInteger(VFP, NUMBER)	vfpPutFormat((VFP), "%d", (NUMBER))
+
+/* put long to current character and increment */
+#define	vfpPutLong(VFP, NUMBER)	vfpPutFormat((VFP), "%ld", (NUMBER))
+
+/* get current character and increment to next */
+#define	vfpGetc(VFP)		(*(VFP)->_vfpCurr++)
+
+/* get current character - do not increment */
+#define	vfpGetcNoInc(VFP)	(*(VFP)->_vfpCurr)
+
+/* get pointer to current character */
+#define	vfpGetCurrCharPtr(VFP)	((VFP)->_vfpCurr)
+
+/* increment current character pointer */
+#define	vfpIncCurrPtr(VFP)	vfpIncCurrPtrBy((VFP), 1)
+
+/* decrement current character pointer */
+#define	vfpDecCurrPtr(VFP)	((VFP)->_vfpCurr--)
+
+/* get pointer to first data byte in buffer */
+#define	vfpGetFirstCharPtr(VFP)	((VFP)->_vfpStart)
+
+/* get pointer to last data byte in buffer */
+#define	vfpGetLastCharPtr(VFP)	((VFP)->_vfpHighWater)
+
+/* set pointer to current character */
+#define	vfpSetCurrCharPtr(VFP, PTR)					\
+	if ((VFP)->_vfpCurr > (VFP)->_vfpHighWater) {			\
+		(VFP)->_vfpHighWater = (VFP)->_vfpCurr;			\
+	}								\
+	((VFP)->_vfpCurr = (PTR))
+
+/* set pointer to last data byte in buffer */
+#define	vfpSetLastCharPtr(VFP, PTR)					\
+	if ((PTR) >= (VFP)->_vfpStart) {				\
+		(VFP)->_vfpHighWater = (PTR);				\
+		if ((VFP)->_vfpCurr > (VFP)->_vfpHighWater) {		\
+			(VFP)->_vfpCurr = (VFP)->_vfpHighWater;		\
+		}							\
+	}
+
+/* seek to end of file - one past last data byte in file */
+#define	vfpSeekToEnd(VFP)	((VFP)->_vfpCurr = ((VFP)->_vfpHighWater)+1)
+
+/* get number of bytes between current char and specified char */
+#define	vfpGetCurrPtrDelta(VFP, P)	\
+	(((ptrdiff_t)(P))-((ptrdiff_t)(VFP)->_vfpCurr))
+
+/* put string to current character and increment */
+#define	vfpPuts(VFP, S)							\
+	{								\
+		size_t	xxLen;						\
+		size_t	xxResult;					\
+		xxLen = vfpGetBytesAvailable((VFP));			\
+		xxResult = strlcpy(((VFP)->_vfpCurr), (S), xxLen);	\
+		vfpIncCurrPtrBy((VFP), xxResult);			\
+	}
+
+/* put fixed number of bytes to current character and increment */
+#define	vfpPutBytes(VFP, PTR, LEN)					\
+	{								\
+		size_t	xxLen;						\
+		xxLen = vfpGetBytesAvailable((VFP));			\
+		if (xxLen > (LEN)) {					\
+			xxLen = (LEN);					\
+		} else {						\
+			(VFP)->_vfpOverflow = 1;			\
+		}							\
+		memcpy((VFP)->_vfpCurr, (PTR), (xxLen));		\
+		vfpIncCurrPtrBy((VFP), (xxLen));			\
+	}
+
+/* put format one arg to current character and increment */
+#define	vfpPutFormat(VFP, FORMAT, ARG)					\
+	{								\
+	char	xxTeMpXX[256];						\
+	(void) snprintf(xxTeMpXX, sizeof (xxTeMpXX), (FORMAT), (ARG));	\
+	vfpPuts((VFP), xxTeMpXX);					\
+	}
+
+struct dm_buf {
+	char *text_buffer;	/* start of allocated buffer */
+	int offset;		/* number of bytes into the text_buffer */
+	int allocation;		/* size of buffer in bytes */
+};
+
+/* This structure is used to hold a dynamically growing string */
+
+struct dstr {
+	char *pc;
+	int   len;
+	int   max;
+};
+
+/* setmapmode() defines */
+#define	MAPALL		0	/* resolve all variables */
+#define	MAPBUILD	1	/* map only build variables */
+#define	MAPINSTALL	2	/* map only install variables */
+#define	MAPNONE		3	/* map no variables */
+
+#define	NON_ABI_NAMELNGTH	33	/* 32 chars for name + 1 for NULL */
+
+#define	BLK_SIZE	512	/* size of logical block */
+
+/* max length for printed attributes */
+#define	ATTR_MAX	80
+
+/*
+ * These three defines indicate that the prototype file contains a '?'
+ * meaning do not specify this data in the pkgmap entry.
+ */
+#define	CURMODE		BADMODE		/* current mode has been specified */
+#define	CUROWNER	BADOWNER	/* ... same for owner ... */
+#define	CURGROUP	BADGROUP	/* ... and group. */
+
+#define	WILDCARD		BADMODE >> 1
+#define	DB_UNDEFINED_ENTRY	"?"
+
+#define	DEFAULT_MODE		0755
+#define	DEFAULT_MODE_FILE	0644
+#define	DEFAULT_OWNER	"root"
+#define	DEFAULT_GROUP	"other"
+
+#define	INST_RELEASE "var/sadm/system/admin/INST_RELEASE"
+
+#define	RANDOM			"/dev/urandom"
+#define	BLOCK			256
+
+#define	TERM_WIDTH		60
+#define	SMALL_DIVISOR		4
+#define	MED_DIVISOR		5
+#define	LARGE_DIVISOR		10
+#define	MED_DWNLD		(10 * 1024 * 1024) /* 10 MB */
+#define	LARGE_DWNLD		(5 * MED_DWNLD) /* 50 MB */
+
+#define	HTTP			"http://"
+#define	HTTPS			"https://"
+
+#define	PKGADD			"pkgadd"
+
+/* Settings for network admin defaults */
+
+#define	NET_TIMEOUT_DEFAULT 	60
+#define	NET_RETRIES_DEFAULT 	3
+#define	NET_TIMEOUT_MIN		1		/* 1 second */
+#define	NET_TIMEOUT_MAX		(5 * 60)	/* 5 minutes */
+#define	NET_RETRIES_MIN		1
+#define	NET_RETRIES_MAX		10
+#define	AUTH_NOCHECK		0
+#define	AUTH_QUIT		1
+
+/* package header magic tokens */
+#define	HDR_PREFIX	"# PaCkAgE DaTaStReAm"
+#define	HDR_SUFFIX	"# end of header"
+
+/* name of security files */
+#define	PKGSEC		"/var/sadm/security"
+#define	SIGNATURE_FILENAME	"signature"
+
+#define	GROUP	"/etc/group"
+#define	PASSWD	"/etc/passwd"
+
+/*
+ * The next three mean that no mode, owner or group was specified or that the
+ * one specified is invalid for some reason. Sometimes this is an error in
+ * which case it is generally converted to CUR* with a warning. Other times
+ * it means "look it up" by stating the existing file system object pointred
+ * to in the prototype file.
+ */
+#define	NOMODE		(BADMODE-1)
+#define	NOOWNER		"@"
+#define	NOGROUP		"@"
+
+/* string comparitor abbreviators */
+
+#define	ci_streq(a, b)		(strcasecmp((a), (b)) == 0)
+#define	ci_strneq(a, b, c)	(strncasecmp((a), (b), (c)) == 0)
+#define	streq(a, b)		(strcmp((a), (b)) == 0)
+#define	strneq(a, b, c)		(strncmp((a), (b), (c)) == 0)
+
+#ifdef	__STDC__
+
+extern FILE	*epopen(char *cmd, char *mode);
+extern char	**gpkglist(char *dir, char **pkg, char **catg);
+extern int	is_not_valid_length(char **category);
+extern int	is_not_valid_category(char **category, char *progname);
+extern int	is_same_CATEGORY(char **category, char *installed_category);
+extern char **get_categories(char *catg_arg);
+
+extern void	pkglist_cont(char *keyword);
+extern char	**pkgalias(char *pkg);
+extern char	*get_prog_name(void);
+extern char 	*set_prog_name(char *name);
+extern int	averify(int fix, char *ftype, char *path, struct ainfo *ainfo);
+extern int	ckparam(char *param, char *value);
+extern int	ckvolseq(char *dir, int part, int nparts);
+extern int	cverify(int fix, char *ftype, char *path, struct cinfo *cinfo,
+			int allow_checksum);
+extern unsigned long	compute_checksum(int *r_cksumerr, char *a_path);
+extern int	fverify(int fix, char *ftype, char *path, struct ainfo *ainfo,
+		    struct cinfo *cinfo);
+extern char	*getErrbufAddr(void);
+extern int	getErrbufSize(void);
+extern char	*getErrstr(void);
+extern void	setErrstr(char *errstr);
+extern int	devtype(char *alias, struct pkgdev *devp);
+extern int	ds_totread;	/* total number of parts read */
+extern int	ds_close(int pkgendflg);
+extern int	ds_findpkg(char *device, char *pkg);
+extern int	ds_getinfo(char *string);
+extern int	ds_getpkg(char *device, int n, char *dstdir);
+extern int	ds_ginit(char *device);
+extern boolean_t	ds_fd_open(void);
+extern int	ds_init(char *device, char **pkg, char *norewind);
+extern int	BIO_ds_dump_header(PKG_ERR *, BIO *);
+extern int	BIO_ds_dump(PKG_ERR *, char *, BIO *);
+extern int	BIO_dump_cmd(char *cmd, BIO *bio);
+extern int	ds_next(char *, char *);
+extern int	ds_readbuf(char *device);
+extern int	epclose(FILE *pp);
+extern int	esystem(char *cmd, int ifd, int ofd);
+extern int	e_ExecCmdArray(int *r_status, char **r_results,
+			char *a_inputFile, char *a_cmd, char **a_args);
+extern int	e_ExecCmdList(int *r_status, char **r_results,
+			char *a_inputFile, char *a_cmd, ...);
+extern int	gpkgmap(struct cfent *ept, FILE *fp);
+extern int	gpkgmapvfp(struct cfent *ept, VFP_T *fpv);
+extern void	setmapmode(int mode_no);
+extern int	isFdRemote(int a_fd);
+extern int	isFstypeRemote(char *a_fstype);
+extern int	isPathRemote(char *a_path);
+extern int	iscpio(char *path, int *iscomp);
+extern int	isdir(char *path);
+extern int	isfile(char *dir, char *file);
+extern int	fmkdir(char *a_path, int a_mode);
+extern int	pkgexecl(char *filein, char *fileout, char *uname, char *gname,
+			...);
+extern int	pkgexecv(char *filein, char *fileout, char *uname, char *gname,
+			char *arg[]);
+extern int	pkghead(char *device);
+extern int	pkgmount(struct pkgdev *devp, char *pkg, int part, int nparts,
+			int getvolflg);
+extern int	pkgtrans(char *device1, char *device2, char **pkg,
+			int options, keystore_handle_t, char *);
+extern int	pkgumount(struct pkgdev *devp);
+extern int	ppkgmap(struct cfent *ept, FILE *fp);
+extern int	putcfile(struct cfent *ept, FILE *fp);
+extern int	putcvfpfile(struct cfent *ept, VFP_T *vfp);
+extern int	rrmdir(char *path);
+extern void	set_memalloc_failure_func(void (*)(int));
+extern void	*xmalloc(size_t size);
+extern void	*xrealloc(void *ptr, size_t size);
+extern char	*xstrdup(char *str);
+extern void	set_passphrase_prompt(char *);
+extern void	set_passphrase_passarg(char *);
+extern int	pkg_passphrase_cb(char *, int, int, void *);
+
+extern int	srchcfile(struct cfent *ept, char *path, VFP_T *vfp,
+			VFP_T *vfpout);
+extern struct	group *cgrgid(gid_t gid);
+extern struct	group *cgrnam(char *nam);
+extern struct	passwd *cpwnam(char *nam);
+extern struct	passwd *cpwuid(uid_t uid);
+extern struct	group *clgrgid(gid_t gid);
+extern struct	group *clgrnam(char *nam);
+extern struct	passwd *clpwnam(char *nam);
+extern struct	passwd *clpwuid(uid_t uid);
+extern void	basepath(char *path, char *basedir, char *ir);
+extern void	canonize(char *file);
+extern void	canonize_slashes(char *file);
+extern void	checksum_off(void);
+extern void	checksum_on(void);
+extern void	cvtpath(char *path, char *copy);
+extern void	ds_order(char *list[]);
+extern void	ds_putinfo(char *buf);
+extern void	ds_skiptoend(char *device);
+extern void	ecleanup(void);
+/*PRINTFLIKE1*/
+extern void	logerr(char *fmt, ...);
+extern int	mappath(int flag, char *path);
+extern int	mapvar(int flag, char *varname);
+/*PRINTFLIKE1*/
+extern void	progerr(char *fmt, ...);
+extern void	pkgerr(PKG_ERR *);
+extern void	rpterr(void);
+extern void	tputcfent(struct cfent *ept, FILE *fp);
+extern void	set_nonABI_symlinks(void);
+extern int	nonABI_symlinks(void);
+extern void	disable_attribute_check(void);
+extern int	get_disable_attribute_check(void);
+
+/* security.c */
+extern void	sec_init(void);
+extern char	*get_subject_display_name(X509 *);
+extern char	*get_issuer_display_name(X509 *);
+extern char	*get_serial_num(X509 *);
+extern char	*get_fingerprint(X509 *, const EVP_MD *);
+extern int	get_cert_chain(PKG_ERR *, X509 *, STACK_OF(X509) *,
+    STACK_OF(X509) *, STACK_OF(X509) **);
+
+/* pkgstr.c */
+void		pkgstrConvertUllToTimeString_r(unsigned long long a_time,
+			char *a_buf, int a_bufLen);
+char		*pkgstrConvertPathToBasename(char *a_path);
+char		*pkgstrConvertPathToDirname(char *a_path);
+char		*pkgstrDup(char *a_str);
+char		*pkgstrLocatePathBasename(char *a_path);
+void		pkgstrScaleNumericString(char *a_buf, unsigned long long scale);
+void		pkgstrAddToken(char **a_old, char *a_new, char a_separator);
+boolean_t	pkgstrContainsToken(char *a_string, char *a_token,
+			char *a_separators);
+void		pkgstrExpandTokens(char **a_old, char *a_string,
+			char a_separator, char *a_separators);
+char		*pkgstrGetToken(char *r_sep, char *a_string, int a_index,
+			char *a_separators);
+void		pkgstrGetToken_r(char *r_sep, char *a_string, int a_index,
+			char *a_separators, char *a_buf, int a_bufLen);
+unsigned long	pkgstrNumTokens(char *a_string, char *a_separators);
+char		*pkgstrPrintf(char *a_format, ...);
+void		pkgstrPrintf_r(char *a_buf, int a_bufLen, char *a_format, ...);
+void		pkgstrRemoveToken(char **r_string, char *a_token,
+			char *a_separators, int a_index);
+void		pkgstrRemoveLeadingWhitespace(char **a_str);
+/* vfpops.c */
+extern int	vfpCheckpointFile(VFP_T **r_destVfp, VFP_T **a_vfp,
+			char *a_path);
+extern int	vfpCheckpointOpen(VFP_T **a_cvfp, VFP_T **r_vfp, char *a_path,
+			char *a_mode, VFPFLAGS_T a_flags);
+extern int	vfpClearModified(VFP_T *a_vfp);
+extern int	vfpClose(VFP_T **r_vfp);
+extern int	vfpGetModified(VFP_T *a_vfp);
+extern int	vfpOpen(VFP_T **r_vfp, char *a_path, char *a_mode,
+			VFPFLAGS_T a_flags);
+extern void	vfpRewind(VFP_T *a_vfp);
+extern ssize_t	vfpSafePwrite(int a_fildes, void *a_buf,
+			size_t a_nbyte, off_t a_offset);
+extern ssize_t	vfpSafeWrite(int a_fildes, void *a_buf, size_t a_nbyte);
+extern int	vfpSetFlags(VFP_T *a_vfp, VFPFLAGS_T a_flags);
+extern int	vfpSetModified(VFP_T *a_vfp);
+extern int	vfpSetSize(VFP_T *a_vfp, size_t a_size);
+extern void	vfpTruncate(VFP_T *a_vfp);
+extern int	vfpWriteToFile(VFP_T *a_vfp, char *a_path);
+
+/* handlelocalfs.c */
+boolean_t	enable_local_fs(void);
+boolean_t	restore_local_fs(void);
+
+#else	/* __STDC__ */
+
+extern FILE	*epopen();
+extern void	pkglist_cont();
+extern char	**gpkglist();
+extern char	**pkgalias();
+extern char	*get_prog_name();
+extern char 	*set_prog_name();
+extern int	averify();
+extern int	ckparam();
+extern int	ckvolseq();
+extern int	cverify();
+extern unsigned long	compute_checksum();
+extern int	fverify();
+extern char	*getErrbufAddr();
+extern int	getErrbufSize();
+extern char	*getErrstr();
+extern void	setErrstr();
+extern int	devtype();
+extern int	ds_close();
+extern int	ds_findpkg();
+extern int	ds_getinfo();
+extern int	ds_getpkg();
+extern int	ds_ginit();
+extern boolean_t	ds_fd_open();
+extern int	ds_init();
+extern int	ds_next();
+extern int	ds_readbuf();
+extern int	epclose();
+extern int	esystem();
+extern int	e_ExecCmdArray();
+extern int	e_ExecCmdList();
+extern int	gpkgmap();
+extern int	isFdRemote();
+extern int	isFstypeRemote();
+extern int	isPathRemote();
+extern int	iscpio();
+extern int	isdir();
+extern int	isfile();
+extern int	pkgexecl();
+extern int	pkgexecv();
+extern int	pkghead();
+extern int	pkgmount();
+extern int	pkgtrans();
+extern int	pkgumount();
+extern int	ppkgmap();
+extern int	putcfile();
+extern int	putcvfpfile();
+extern int	rrmdir();
+extern int	srchcfile();
+extern struct	group *cgrgid();
+extern struct	group *cgrnam();
+extern struct	passwd *cpwnam();
+extern struct	passwd *cpwuid();
+extern void	basepath();
+extern void	canonize();
+extern void	canonize_slashes();
+extern void	checksum_off();
+extern void	checksum_on();
+extern void	cvtpath();
+extern void	ds_order();
+extern void	ds_putinfo();
+extern void	ds_skiptoend();
+extern void	ecleanup();
+extern void	logerr();
+extern int	mappath();
+extern int	mapvar();
+extern void	progerr();
+extern void	rpterr();
+extern void	tputcfent();
+extern void	set_nonABI_symlinks();
+extern int	nonABI_symlinks();
+extern void	disable_attribute_check();
+extern int	get_disable_attribute_check();
+/* vfpops.c */
+extern int	vfpCheckpointFile();
+extern int	vfpCheckpointOpen();
+extern int	vfpClearModified();
+extern int	vfpClose();
+extern int	vfpGetModified();
+extern int	vfpOpen();
+extern void	vfpRewind();
+extern int	vfpSetFlags();
+extern int	vfpSetModified();
+extern int	vfpSetSize();
+extern void	vfpTruncate();
+extern int	vfpWriteToFile();
+
+/* handlelocalfs.c */
+boolean_t	enable_local_fs();
+boolean_t	restore_local_fs();
+
+/* gpkgmap.c */
+int		getmapmode(void);
+
+#endif	/* __STDC__ */
+
+#ifdef	__cplusplus
+}
+#endif
+
+#endif	/* _PKGLIB_H */
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/usr/src/lib/libpkg/common/pkglibmsgs.h	Fri Jun 05 10:28:40 2009 -0400
@@ -0,0 +1,431 @@
+/*
+ * 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
+ * or http://www.opensolaris.org/os/licensing.
+ * 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.
+ */
+
+#ifndef _PKGLIBMSGS_H
+#define	_PKGLIBMSGS_H
+
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/* srchcfile messages */
+#define	ERR_MISSING_NEWLINE	"missing newline at end of entry"
+#define	ERR_ILLEGAL_SEARCH_PATH	"illegal search path specified"
+#define	ERR_CANNOT_READ_MM_NUMS	"unable to read major/minor device numbers"
+#define	ERR_INCOMPLETE_ENTRY	"incomplete entry"
+#define	ERR_VOLUMENO_UNEXPECTED	"volume number not expected"
+#define	ERR_FTYPE_I_UNEXPECTED	"ftype <i> not expected"
+#define	ERR_CANNOT_READ_CLASS_TOKEN	"unable to read class token"
+#define	ERR_CANNOT_READ_PATHNAME_FLD	"unable to read pathname field"
+#define	ERR_UNKNOWN_FTYPE	"unknown ftype"
+#define	ERR_CANNOT_READ_LL_PATH	"unable to read local/link path"
+#define	ERR_INCOMPLETE_ENTRY	"incomplete entry"
+#define	ERR_NO_LINK_SOURCE_SPECIFIED	"no link source specified"
+#define	ERR_CANNOT_READ_MOG	"unable to read mode/owner/group"
+#define	ERR_CANNOT_READ_CONTENT_INFO	"unable to read content info"
+#define	ERR_PACKAGE_NAME_TOO_LONG	"package name too long"
+#define	ERR_NO_MEMORY	"no memory for package information"
+#define	ERR_BAD_ENTRY_END	"bad end of entry"
+#define	ERR_EXTRA_TOKENS	"extra token(s) on input line"
+
+/* pkgtrans messages */
+#define	MSG_TRANSFER	"Transferring <%s> package instance\n"
+#define	MSG_STORE_ACC	"Retrieving signature certificates from <%s>\n"
+#define	MSG_SIGNING	"Generating digital signature for signer <%s>\n"
+#define	MSG_RENAME 	"\t... instance renamed <%s> on destination\n"
+
+#define	ERR_TRANSFER	"unable to complete package transfer"
+#define	MSG_SEQUENCE	"- volume is out of sequence"
+#define	MSG_MEM		"- no memory"
+#define	MSG_CMDFAIL	"- process <%s> failed, exit code %d"
+#define	MSG_POPEN	"- popen of <%s> failed, errno=%d"
+#define	MSG_PCLOSE	"- pclose of <%s> failed, errno=%d"
+#define	MSG_BADDEV	"- invalid or unknown device <%s>"
+#define	MSG_GETVOL	"- unable to obtain package volume"
+#define	MSG_NOSIZE 	"- unable to obtain maximum part size from pkgmap"
+#define	MSG_CHDIR	"- unable to change directory to <%s>"
+#define	MSG_SYMLINK	"- unable to create symbolic link to <%s> from <%s>"
+#define	MSG_STATDIR	"- unable to stat <%s>"
+#define	MSG_CHOWNDIR	"- unable to chown <%s>"
+#define	MSG_CHMODDIR	"- unable to chmod <%s>"
+#define	MSG_FSTYP	"- unable to determine filesystem type for <%s>"
+#define	MSG_NOTEMP	"- unable to create or use temporary directory <%s>"
+#define	MSG_SAMEDEV	"- source and destination represent the same device"
+#define	MSG_NOTMPFIL	"- unable to create or use temporary file <%s>"
+#define	MSG_NOPKGMAP	"- unable to open pkgmap for <%s>"
+#define	MSG_BADPKGINFO	"- unable to determine contents of pkginfo file"
+#define	MSG_NOPKGS	"- no packages were selected from <%s>"
+#define	MSG_MKDIR	"- unable to make directory <%s>"
+#define	MSG_NOEXISTS	"- package instance <%s> does not exist on source " \
+			"device"
+#define	MSG_EXISTS	"- no permission to overwrite existing path <%s>"
+#define	MSG_DUPVERS	"- identical version of <%s> already exists on " \
+			"destination device"
+#define	MSG_TWODSTREAM	"- both source and destination devices cannot be a " \
+			"datastream"
+#define	MSG_OPEN	"- open of <%s> failed, errno=%d"
+#define	MSG_STATVFS	"- statvfs(%s) failed, errno=%d"
+
+/* security problems */
+#define	ERR_PARSE		"unable to parse keystore <%s>, invalid " \
+				"format or corrupt"
+#define	ERR_BADPASS		"Invalid password.  Password does not " \
+				"decrypt keystore"
+
+#define	MSG_PASSWD_FILE		"Password file <%s> cannot be read"
+#define	MSG_PASSWD_AGAIN	"For Verification"
+#define	MSG_PASSWD_NOMATCH	"Passwords do not match"
+#define	MSG_BADPASSARG		"Password retrieval method <%s> invalid"
+#define	MSG_NOPASS		"Cannot get passphrase using " \
+				"retrieval method <%s>"
+
+#define	ERR_MISMATCHPASS	"<%s> encrypted with different password " \
+				" than <%s>, keystore <%s> corrupt"
+
+#define	MSG_CHSIGDIR	"- unable to change directory to <%s/%s>"
+#define	MSG_MKSIGDIR	"- unable to make directory <%s/%s>"
+#define	ERR_CANTSIGN	"- destination device must be datastream in order to" \
+			" sign contents"
+#define	ERR_STORE	"unable to find or use store <%s> from application " \
+			"<%s>:<%s>"
+
+#define	ERR_NO_KEYSTORE	"unable to open keystore <%s> for reading"
+#define	ERR_NOT_REG	"<%s> is not a regular file"
+#define	ERR_KEYSTORE_CORRUPT	"Keystore file <%s> is corrupt or unparseable"
+#define	ERR_KEYSTORE_REPAIR	"unable to repair keystore <%s>"
+#define	ERR_KEYSTORE_LOCKED_READ	"unable to lock keystore file <%s> " \
+					"for reading, try again later"
+#define	ERR_KEYSTORE_LOCKED	"unable to lock keystore <%s> for exclusive " \
+				"access"
+#define	ERR_KEYSTORE_UNLOCK	"unable to unlock keystore <%s> for " \
+				"application <%s>"
+#define	ERR_KEYSTORE_WRITE	"unable to open keystore <%s> for writing"
+#define	ERR_KEYSTORE_REMOVE	"unable to delete keystore file <%s>"
+#define	ERR_KEYSTORE_READ	"unable to open keystore <%s> for reading"
+#define	ERR_KEYSTORE_OPEN	"unable to open keystore <%s>:<%s>"
+#define	ERR_KEYSTORE_FORM	"unable to form PKCS12 keystore file for " \
+				"writing to <%s>"
+
+#define	ERR_KEYSTORE_NOPUBCERTS	"unable to find any public key certificates " \
+				"in keystore file <%s>"
+
+#define	ERR_KEYSTORE_NOPRIVKEYS	"unable to find any private keys in keystore "\
+				"file <%s>"
+
+#define	ERR_KEYSTORE_NOCACERTS	"unable to find any trusted certificates in "\
+				"file <%s>"
+
+#define	ERR_KEYSTORE_NOTRUST	"unable to find any trusted certificates in "\
+				"keystore"
+
+#define	ERR_KEYSTORE_NOMATCH	"unable to find certificate and key pair " \
+				"with alias <%s> in keystore"
+
+#define	ERR_KEYSTORE_DUPLICATECERT	"Certificate with alias <%s> " \
+					"already exists in keystore"
+#define	ERR_KEYSTORE_DUPLICATEKEY	"Private key with alias <%s> already" \
+					" exists in keystore"
+#define	ERR_KEYSTORE_NO_ALIAS	"Keystore certificate <%s> has no recorded " \
+				"alias, must be deleted from keystore"
+#define	ERR_KEYSTORE_NOCERT	"No certificate with alias <%s> found in " \
+				"keystore <%s>"
+#define	ERR_KEYSTORE_NOCERTKEY	"No certificates or private keys with alias " \
+				"<%s> found in keystore <%s>"
+
+#define	ERR_KEYSTORE_INTERNAL	"Internal Error file %s line %d"
+
+#define	ERR_CURR_TIME	"Cannot determine current time from system"
+#define	ERR_CERT_TIME	"Certificate <%s> has expired or is not yet valid.\n" \
+			"Current time: <%s>\n  Certificate valid: <%s> - <%s>"
+#define	ERR_MISMATCHED_KEYS	"Private key does not match public key in " \
+			"certificate <%s>"
+#define	ERR_CERT_TIME_BAD	"Certificate has corrupt validity dates, " \
+				"cannot process"
+#define	ERR_TRUSTSTORE	"unable to find or use trusted certificate " \
+			"store <%s> from application <%s>:<%s>"
+
+#define	ERR_STORE_PW	"unable to read password from <%s>"
+
+#define	ERR_SEC		"unable to sign package contents using <%s> " \
+			"private key"
+
+#define	ERR_NOGEN	"unable to generate digital signature"
+
+#define	ERR_STORE_PW	"unable to read password from <%s>"
+#define	ERR_CORRUPTSIG  "Invalid or corrupt signature in datastream <%s>"
+#define	ERR_CORRUPTSIG_TYPE  "Wrong PKCS7 signature type in datastream <%s>"
+#define	ERR_CORRUPTSIG_DT   "Signature found but not detached in " \
+	"datastream <%s>"
+#define	ERR_KEYSTORE	"invalid or corrupt PKCS12 file <%s>."
+#define	ERR_KEYSTORE_NOCERTS "Store <%s> contains no certificates"
+#define	ERR_KEYSTORE_NOKEYS "Store <%s> contains no private keys"
+#define	ERR_SIG_INT "Internal error during signature verification."
+#define	MSG_VERIFY  "## Verifying signature for signer <%s>"
+#define	MSG_VERIFY_OK  "## Signature for signer <%s> verified."
+#define	ERR_VERIFY  "Signature verification failed."
+#define	ERR_VERIFY_SIG  "Signature verification failed while verifying " \
+			"certificate <subject=%s, issuer=%s>:<%s>."
+#define	ERR_VERIFY_ISSUER  "Could not find issuer certificate for signer <%s>"
+#define	ERR_OPENSIG	"Signature found in datastream but cannot be " \
+			" opened: <%s>"
+
+#define	ERR_SIGFOUND	"signature found in datastream <%s>, you must " \
+	"specify a keystore with -k"
+#define	ERR_DSINIT  "could not process datastream from <%s>"
+
+#define	MSG_KEYSTORE_AL	"Keystore Alias"
+#define	MSG_KEYSTORE_SN	"Serial Number"
+#define	MSG_KEYSTORE_FP	"Fingerprint"
+#define	MSG_KEYSTORE_CN	"Common Name"
+#define	MSG_KEYSTORE_IN "Issuer Common Name"
+#define	MSG_KEYSTORE_VD	"Validity Dates"
+#define	MSG_KEYSTORE_TY	"Certificate Type"
+#define	MSG_KEYSTORE_TRUSTED	"Trusted Certificate"
+#define	MSG_KEYSTORE_UNTRUSTED	"Signing Certificate"
+#define	MSG_KEYSTORE_UNKNOWN	"Unknown"
+
+/* parameter errors */
+#define	ERR_LEN		"length of parameter value <%s> exceeds limit"
+#define	ERR_ASCII	"parameter <%s> must be ascii"
+#define	ERR_ALNUM	"parameter <%s> must be alphanumeric"
+#define	ERR_CHAR	"parameter <%s> has incorrect first character"
+#define	ERR_UNDEF	"parameter <%s> cannot be null"
+
+/* volume sequence errors */
+#define	MSG_SEQ		"Volume is out of sequence."
+#define	MSG_CORRUPT	"Volume is corrupt or is not part of the appropriate " \
+			"package."
+#define	ERR_NOPKGMAP	"ERROR: unable to process <%s>"
+#define	ERR_BADPKGINFO	"ERROR: unable to process <%s>"
+
+/* datastream processing errors */
+#define	ERR_UNPACK	"attempt to process datastream failed"
+#define	ERR_DSTREAMSEQ	"datastream sequence corruption"
+#define	ERR_TRANSFER    "unable to complete package transfer"
+#define	MSG_CMDFAIL	"- process <%s> failed, exit code %d"
+#define	MSG_TOC		"- bad format in datastream table-of-contents"
+#define	MSG_EMPTY	"- datastream table-of-contents appears to be empty"
+#define	MSG_POPEN	"- popen of <%s> failed, errno=%d"
+#define	MSG_OPEN	"- open of <%s> failed, errno=%d"
+#define	MSG_PCLOSE	"- pclose of <%s> failed, errno=%d"
+#define	MSG_PKGNAME	"- invalid package name in datastream table-of-contents"
+#define	MSG_NOPKG	"- package <%s> not in datastream"
+#define	MSG_STATFS	"- unable to stat filesystem, errno=%d"
+#define	MSG_NOSPACE	"- not enough space, %d blocks required, %d available"
+
+/* pkglist errors */
+#define	ERR_MEMORY	"memory allocation failure, errno=%d"
+#define	ERR_NOPKG	"no package associated with <%s>"
+#define	HEADER		"The following packages are available:"
+#define	HELP		"Please enter the package instances you wish to " \
+			"process from the list provided (or 'all' to process " \
+			"all packages.)"
+
+#define	PROMPT		"Select package(s) you wish to process (or 'all' to " \
+			"process all packages)."
+/* pkgmap errors */
+#define	ERR_READLINK	"unable to read link specification."
+#define	ERR_NOVAR	"no value defined for%s variable <%s>."
+#define	ERR_OWNTOOLONG	"owner string is too long."
+#define	ERR_GRPTOOLONG	"group string is too long."
+#define	ERR_IMODE	"mode must not be parametric at install time."
+#define	ERR_BASEINVAL	"invalid base for mode."
+#define	ERR_MODELONG	"mode string is too long."
+#define	ERR_MODEALPHA	"mode is not numeric."
+#define	ERR_MODEBITS	"invalid bits set in mode."
+
+/* package mount errors and msgs */
+#define	ERR_FSTYP	"unable to determine fstype for <%s>"
+#define	ERR_NOTROOT	"You must be \"root\" when using mountable media."
+#define	MOUNT		"/sbin/mount"
+#define	UMOUNT		"/sbin/umount"
+#define	FSTYP		"/usr/sbin/fstyp"
+
+#define	LABEL0	"Insert %%v %d of %d for <%s> package into %%p."
+#define	LABEL1	"Insert %%v %d of %d into %%p."
+#define	LABEL2	"Insert %%v for <%s> package into %%p."
+#define	LABEL3	"Insert %%v into %%p."
+
+/* package verify errors */
+#define	MSG_WLDDEVNO	"NOTE: <%s> created as device (%d, %d)."
+
+#define	WRN_QV_SIZE	"WARNING: quick verify of <%s>; wrong size."
+#define	WRN_QV_MTIME	"WARNING: quick verify of <%s>; wrong mod time."
+
+#define	ERR_PKG_INTERNAL "Internal package library failure file %s line %d"
+#define	ERR_UNKNOWN	"unable to determine object type"
+#define	ERR_EXIST	"pathname does not exist"
+#define	ERR_FTYPE	"file type <%c> expected <%c> actual"
+#define	ERR_FTYPED	"<%s> is a door and is not being modified"
+#define	ERR_LINK	"pathname not properly linked to <%s>"
+#define	ERR_SLINK	"pathname not symbolically linked to <%s>"
+#define	ERR_MTIME	"modtime <%s> expected <%s> actual"
+#define	ERR_SIZE	"file size <%llu> expected <%llu> actual"
+#define	ERR_CKSUM	"file cksum <%ld> expected <%ld> actual"
+#define	ERR_NO_CKSUM	"unable to checksum, may need to re-run command as " \
+			"user \"root\""
+#define	ERR_MAJMIN	"major/minor device <%d, %d> expected <%d, %d> actual"
+#define	ERR_PERM	"permissions <%04o> expected <%04o> actual"
+#define	ERR_GROUP	"group name <%s> expected <%s> actual"
+#define	ERR_OWNER	"owner name <%s> expected <%s> actual"
+#define	ERR_MODFAIL	"unable to fix modification time"
+#define	ERR_LINKFAIL	"unable to create link to <%s>"
+#define	ERR_LINKISDIR	"<%s> is a directory, link() not performed"
+#define	ERR_SLINKFAIL	"unable to create symbolic link to <%s>"
+#define	ERR_DIRFAIL	"unable to create directory"
+#define	ERR_CDEVFAIL	"unable to create character-special device"
+#define	ERR_BDEVFAIL	"unable to create block-special device"
+#define	ERR_PIPEFAIL	"unable to create named pipe"
+#define	ERR_ATTRFAIL	"unable to fix attributes"
+#define	ERR_BADGRPID	"unable to determine group name for gid <%d>"
+#define	ERR_BADUSRID	"unable to determine owner name for uid <%d>"
+#define	ERR_BADGRPNM	"group name <%s> not found in group table(s)"
+#define	ERR_BADUSRNM	"owner name <%s> not found in passwd table(s)"
+#define	ERR_GETWD	"unable to determine current working directory"
+#define	ERR_CHDIR	"unable to change current working directory to <%s>"
+#define	ERR_RMDIR	"unable to remove existing directory at <%s>"
+
+/* others */
+#define	ERR_ISCPIO_OPEN		"iscpio(): open(%s) failed!"
+#define	ERR_ISCPIO_FSTAT	"iscpio(): fstat(%s) failed!"
+#define	ERR_ISCPIO_READ		"iscpio(): read(%s) failed!"
+#define	ERR_ISCPIO_NOCPIO	"iscpio(): <%s> is not a cpio archive!"
+
+#define	ERR_DUPFAIL	"%s: strdup(%s) failed.\n"
+#define	ERR_ADDFAIL	"%s: add_cache() failed.\n"
+#define	ERR_BADMEMB	"%s: %s in \"%s\" %s structure is invalid.\n"
+#define	ERR_NOGRP	"dup_gr_ent(): no group entry provided.\n"
+#define	ERR_NOPWD	"dup_pw_ent(): no passwd entry provided.\n"
+#define	ERR_NOINIT	"%s: init_cache() failed.\n"
+#define	ERR_MALLOC	"%s: malloc(%d) failed for %s.\n"
+
+#define	ERR_TOO_MANY_ARGS	"too many arguments passed to pkgexecl " \
+				"for command <%s>"
+#define	ERR_WAIT_FAILED	"wait for process %ld failed, pid <%ld> status " \
+			"<0x%08lx> errno <%d> (%s)"
+#define	ERR_FORK_FAILED	"fork() failed errno=%d (%s)"
+#define	ERR_FREOPEN	"freopen(%s, \"%s\", %s) failed, errno=%d (%s)"
+#define	ERR_FDOPEN	"fdopen(%d, \"%s\") failed, errno=%d (%s)"
+#define	ERR_CLOSE	"close(%d) failed, errno=%d"
+#define	ERR_SETGID	"setgid(%d) failed."
+#define	ERR_SETUID	"setuid(%d) failed."
+#define	ERR_EX_FAIL	"exec of %s failed, errno=%d"
+
+/* pkgweb errors */
+#define	MSG_DWNLD "\n## Downloading..."
+#define	ERR_DWNLD_FAILED "\n## After %d retries, unable to complete transfer"
+#define	MSG_DWNLD_TIMEOUT "\n## Timed out, retrying..."
+#define	MSG_DWNLD_CONNREF "\n## Connection to <%s> refused, retrying..."
+#define	MSG_DWNLD_HOSTDWN "\n## <%s> not responding, retrying..."
+#define	MSG_DWNLD_PART "\n## Found partially downloaded file <%s> of " \
+			"size <%ld> bytes.  To force a complete " \
+			"re-download, delete this file and try again"
+#define	MSG_DWNLD_PREV "\n## Using previously spooled package datastream <%s>"
+#define	MSG_DWNLD_CONT "\n## Continuing previously attempted download..."
+#define	MSG_DWNLD_COMPLETE "## Download Complete\n"
+
+#define	ERR_DWNLD_NO_CONT "unable to open partially downloaded file <%s> " \
+				"for appending"
+#define	ERR_BAD_PATH "unable to locate keystore."
+#define	ERR_EMPTYPATH "No valid path exists for the keystore file."
+#define	ERR_RETRIES "The number of server retries is not a valid " \
+	"value. Please specify a value within the range of %d - %d."
+#define	ERR_TIMEOUT "The network timeout value is not a valid " \
+	"value. Please specify a value within the range of %d - %d."
+#define	ERR_PARSE_URL "unable to parse the url <%s>."
+#define	ERR_MEM "unable to allocate memory."
+#define	ERR_HTTPS_PASSWD "unable set password for HTTPS connection."
+#define	ERR_HTTPS_CA "unable to set CA file for HTTPS connection."
+#define	ERR_HTTP "Failure occurred with http(s) negotiation: <%s>"
+#define	ERR_WRITE "Cannot write to file <%s> : <%s>"
+#define	ERR_READ "Cannot read from file <%s> : <%s>"
+#define	ERR_SVR_RESP "unable to establish a connection with the http(s) server."
+#define	ERR_INIT_CONN "unable to establish a connection with <%s>."
+#define	ERR_INIT_SESS "unable to intialize download session for <%s>."
+#define	ERR_INIT_CONN_PROXY "unable to establish a connection with <%s> " \
+	"using <%s> as the proxy"
+#define	ERR_CLOSE_CONN "unable to close the connection with <%s>."
+#define	ERR_NO_HEAD_VAL "HTTP Response did not include header <%s>."
+/* CSTYLED */
+#define	ERR_BAD_HEAD_VAL "HTTP Header value \"<%s>: <%s>\" unusable or " \
+			"unparseable."
+#define	ERR_BAD_CONTENT "The package <%s> attempting to be installed " \
+	"is illegal."
+#define	ERR_DWNLD "unable to download package datastream from <%s>."
+#define	ERR_OPEN_TMP "unable to open temporary file for writing."
+#define	ERR_WRITE_TMP "unable to write to temporary file."
+#define	ERR_DISK_SPACE "Not enough disk space is available to download " \
+	"package to\n%s. %llukb needed, %llukb available."
+#define	ERR_CERTS "unable to find a valid certificate in <%s>."
+#define	ERR_CERTCHAIN "unable to build certificate chain for subject <%s>:<%s>."
+#define	ERR_ILL_ENV "The environment variable <%s=%s> is illegal"
+#define	ERR_BAD_PROXY "Invalid proxy specification: <%s>"
+#define	ERR_TMPDIR "unable to find temporary directory <%s>"
+#define	ERR_MEM "unable to allocate memory."
+#define	ERR_NO_DWNLD_DIR "No download directory available."
+#define	MSG_OCSP_VERIFY "## Contacting OCSP Responder <%s> for " \
+			"certificate <%s> status"
+#define	MSG_OCSP_VERIFY_PROXY "## Contacting OCSP Responder <%s> through " \
+				"proxy <%s:%d> for certificate <%s> status"
+#define	ERR_OCSP_PARSE "OCSP Responder URL <%s> invalid or unparseable"
+#define	ERR_OCSP_RESP_PARSE "OCSP Response <%s> unparseable or empty"
+#define	ERR_OCSP_RESP_NOTOK "OCSP Request failed.  Expected status " \
+			"<%d>, got <%d>, Reason=<%s>"
+#define	WRN_OCSP_RESP_NONCE "WARNING: Invalid or no nonce found in " \
+			"OCSP response."
+#define	ERR_OCSP_RESP_TYPE "OCSP response message type invalid: <%s>, " \
+			"expecting <%s>"
+#define	ERR_OCSP_CONNECT "Cannot connect to OCSP Responder <%s> port <%d>"
+#define	ERR_OCSP_SEND "Cannot send OCSP request to OCSP Responder <%s>"
+#define	ERR_OCSP_READ "Cannot read OCSP response from OCSP Responder <%s>"
+#define	ERR_OCSP_RESPONDER "OCSP Responder cannot process OCSP Request"
+#define	ERR_OCSP_UNSUP "Unsupported OCSP Option <%s>"
+#define	ERR_OCSP_VERIFY_NOTIME "Cannot access system time() to determine " \
+				"OCSP Response validity"
+#define	ERR_OCSP_VERIFY_SIG "OCSP Response, signed by <%s>, cannot be " \
+			"verified: <%s>"
+#define	ERR_OCSP_VERIFY_FAIL "unable to validate response from OCSP " \
+			"Responder <%s>"
+#define	ERR_OCSP_VERIFY_NO_STATUS "OCSP Responder did not supply validity " \
+				"of certificate <%s> "
+#define	ERR_OCSP_VERIFY_VALIDITY_NOTBEFORE "OCSP Response is only valid " \
+			"after <%s>.  Current time is <%s>."
+#define	ERR_OCSP_VERIFY_VALIDITY "OCSP Response is only valid from <%s> " \
+			"to <%s>.  Current time is <%s>."
+#define	ERR_OCSP_VERIFY_STATUS "OCSP Responder indicates certificate <%s> " \
+			"status is <%s>"
+#define	ERR_OCSP_VERIFY "OCSP Responder rejected certificate, or did not " \
+			"recognize"
+#define	ERR_OCSP_NO_URI "No OCSP Responder URL"
+
+#define	MSG_BASE_USED   "Using <%s> as the package base directory."
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* _PKGLIBMSGS_H */
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/usr/src/lib/libpkg/common/pkglocale.h	Fri Jun 05 10:28:40 2009 -0400
@@ -0,0 +1,51 @@
+/*
+ * 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
+ * or http://www.opensolaris.org/os/licensing.
+ * 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 2003 Sun Microsystems, Inc.  All rights reserved.
+ * Use is subject to license terms.
+ */
+
+#ifndef _PKGLOCALE_H
+#define	_PKGLOCALE_H
+
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#include <locale.h>
+#include <libintl.h>
+
+#if !defined(TEXT_DOMAIN)
+#define	TEXT_DOMAIN	"SYS_TEST"
+#endif
+#ifdef lint
+#define	pkg_gt(x)	x
+#else	/* !lint */
+#define	pkg_gt(x)	dgettext(TEXT_DOMAIN, x)
+#endif	/* lint */
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* _PKGLOCALE_H */
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/usr/src/lib/libpkg/common/pkgmount.c	Fri Jun 05 10:28:40 2009 -0400
@@ -0,0 +1,167 @@
+/*
+ * 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
+ * or http://www.opensolaris.org/os/licensing.
+ * 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 2009 Sun Microsystems, Inc.  All rights reserved.
+ * Use is subject to license terms.
+ */
+
+/* Copyright (c) 1984, 1986, 1987, 1988, 1989 AT&T */
+/* All Rights Reserved */
+
+
+
+#include <stdio.h>
+#include <errno.h>
+#include <string.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <pkgdev.h>
+#include <pkginfo.h>
+#include <sys/types.h>
+#include <devmgmt.h>
+#include <sys/mount.h>
+#include "pkglib.h"
+#include "pkglibmsgs.h"
+#include "pkglocale.h"
+
+extern void	quit(int retcode); 	/* Expected to be declared by caller! */
+/* libadm.a */
+extern int	getvol(char *device, char *label, int options, char *prompt);
+
+#define	CMDSIZ	256
+
+int
+pkgmount(struct pkgdev *devp, char *pkg, int part, int nparts, int getvolflg)
+{
+	int	n;
+	char	*pt, prompt[64], cmd[CMDSIZ];
+	FILE	*pp;
+
+	if (getuid()) {
+		progerr(pkg_gt(ERR_NOTROOT));
+		return (99);
+	}
+
+	if (part && nparts) {
+		if (pkg) {
+			(void) sprintf(prompt, pkg_gt(LABEL0), part,
+			    nparts, pkg);
+		} else {
+			(void) sprintf(prompt, pkg_gt(LABEL1), part,
+			    nparts);
+		}
+	} else if (pkg)
+		(void) sprintf(prompt, pkg_gt(LABEL2), pkg);
+	else
+		(void) sprintf(prompt, pkg_gt(LABEL3));
+
+	n = 0;
+	for (;;) {
+		if (!getvolflg && n)
+			/*
+			 * Return to caller if not prompting
+			 * and error was encountered.
+			 */
+			return (-1);
+		if (getvolflg && (n = getvol(devp->bdevice, NULL,
+		    (devp->rdonly ? 0 : DM_FORMFS|DM_WLABEL), prompt))) {
+			if (n == 3)
+				return (3);
+			if (n == 2)
+				progerr(pkg_gt("unknown device <%s>"),
+				    devp->bdevice);
+			else
+				progerr(
+				    pkg_gt("unable to obtain package volume"));
+			return (99);
+		}
+
+		if (devp->fstyp == NULL) {
+			(void) sprintf(cmd, "%s %s", FSTYP, devp->bdevice);
+			if ((pp = epopen(cmd, "r")) == NULL) {
+				rpterr();
+				logerr(pkg_gt(ERR_FSTYP), devp->bdevice);
+				n = -1;
+				continue;
+			}
+			cmd[0] = '\0';
+			if (fgets(cmd, CMDSIZ, pp) == NULL) {
+				logerr(pkg_gt(ERR_FSTYP), devp->bdevice);
+				(void) pclose(pp);
+				n = -1;
+				continue;
+			}
+			if (epclose(pp)) {
+				rpterr();
+				logerr(pkg_gt(ERR_FSTYP), devp->bdevice);
+				n = -1;
+				continue;
+			}
+			if (pt = strpbrk(cmd, " \t\n"))
+				*pt = '\0';
+			if (cmd[0] == '\0') {
+				logerr(pkg_gt(ERR_FSTYP), devp->bdevice);
+				n = -1;
+				continue;
+			}
+			devp->fstyp = strdup(cmd);
+		}
+
+		if (devp->rdonly) {
+			n = pkgexecl(NULL, NULL, NULL, NULL, MOUNT, "-r", "-F",
+			    devp->fstyp, devp->bdevice, devp->mount, NULL);
+		} else {
+			n = pkgexecl(NULL, NULL, NULL, NULL, MOUNT, "-F",
+			    devp->fstyp, devp->bdevice, devp->mount, NULL);
+		}
+		if (n) {
+			progerr(pkg_gt("mount of %s failed"), devp->bdevice);
+			continue;
+		}
+		devp->mntflg++;
+		break;
+	}
+	return (0);
+}
+
+int
+pkgumount(struct pkgdev *devp)
+{
+	int	n = 1;
+	int	retry = 10;
+
+	if (!devp->mntflg)
+		return (0);
+
+	while (n != 0 && retry-- > 0) {
+		n = pkgexecl(NULL, NULL, NULL, NULL, UMOUNT, devp->bdevice,
+		    NULL);
+		if (n != 0) {
+			progerr(pkg_gt("retrying umount of %s"),
+			    devp->bdevice);
+			sleep(5);
+		}
+	}
+	if (n == 0)
+		devp->mntflg = 0;
+	return (n);
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/usr/src/lib/libpkg/common/pkgstr.c	Fri Jun 05 10:28:40 2009 -0400
@@ -0,0 +1,1132 @@
+/*
+ * 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
+ * or http://www.opensolaris.org/os/licensing.
+ * 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 2009 Sun Microsystems, Inc. All rights reserved.
+ * Use is subject to license terms.
+ */
+
+
+/*
+ * Module:	pkgstr.c
+ * Synopsis:	general string services
+ * Taxonomy:	project private
+ * Debug Flag:	str
+ * Description:
+ *
+ *   This module implements general string utility services
+ *
+ * Public Methods:
+ *
+ *   pkgstrAddToken - Add a token to a string
+ *   pkgstrContainsToken - Determine if a string contains a specified token
+ *   pkgstrConvertPathToBasename - Return copy of base name in path string
+ *   pkgstrConvertPathToDirname - Return copy of directory name in path string
+ *   pkgstrConvertUllToTimeString_r - convert unsigned long long to time string
+ *   pkgstrExpandTokens - Expand tokens from string appending tokens to another
+ *   pkgstrGetToken - Get a token from a string
+ *   pkgstrGetToken_r - Get a token from a string into a fixed buffer
+ *   pkgstrLocatePathBasename - Locate position of base name in path string
+ *   pkgstrNumTokens - Determine number of tokens in string
+ *   pkgstrPrintf - Create a string from a printf style format and arguments
+ *   pkgstrPrintf_r - Create a string from a printf style format and arguments
+ *			into a fixed buffer
+ *   pkgstrRemoveToken - Remove a token from a string
+ *   pkgstrRemoveLeadingWhitespace - remove leading whitespace from string
+ *   pkgstrScaleNumericString - Convert unsigned long long to human
+ *	readable form
+ */
+
+/*
+ * Unix Includes
+ */
+
+#define	__EXTENSIONS__
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <libintl.h>
+#include <limits.h>
+#include <sys/types.h>
+#include <assert.h>
+#include <errno.h>
+#include <libintl.h>
+#include <ctype.h>
+#include <unistd.h>
+#include <strings.h>
+#include <stdarg.h>
+
+/*
+ * pkglib Includes
+ */
+
+#include "pkglib.h"
+#include "pkgstrct.h"
+#include "libintl.h"
+#include "pkglocale.h"
+
+/*
+ * External definitions
+ */
+
+/*
+ * Public methods
+ */
+
+/*
+ * Name:	pkgstrRemoveLeadingWhitespace
+ * Synopsis:	Remove leading whitespace from string
+ * Description:	Remove all leading whitespace characters from a string
+ * Arguments:	a_str - [RO, *RW] - (char **)
+ *			Pointer to handle to string (in allocated storage) to
+ *			remove all leading whitespace from
+ * Returns:	void
+ *			The input string is modified as follows:
+ *			== (char *)NULL:
+ *				- input string was (char *)NULL
+ *				- input string is all whitespace
+ *			!= (char *)NULL:
+ *				- copy of input string with leading
+ *				  whitespace removed
+ * CAUTION:	The input string must be allocated space (via mem* or
+ *		pkgstr* methods) - it must not be a static or inline
+ *		character string
+ * NOTE:	The input string a_str will be freed with 'free'
+ *		if it is all whitespace, or if it contains any leading
+ *		whitespace characters
+ * NOTE:    	Any string returned is placed in new storage for the
+ *		calling method. The caller must use 'free' to dispose
+ *		of the storage once the string is no longer needed.
+ * Errors:	If the string cannot be created, the process exits
+ */
+
+void
+pkgstrRemoveLeadingWhitespace(char **a_str)
+{
+	char	*o_str;
+
+	/* entry assertions */
+
+	assert(a_str != (char **)NULL);
+
+	/* if string is null, just return */
+
+	if (*a_str == (char *)NULL) {
+		return;
+	}
+	o_str = *a_str;
+
+	/* if string is empty, deallocate and return NULL */
+
+	if (*o_str == '\0') {
+		/* free string - handle is reset to NULL by free */
+		free(*a_str);
+		*a_str = (char *)NULL;
+		return;
+	}
+
+	/* if first character is not a space, just return */
+
+	if (!isspace(*o_str)) {
+		return;
+	}
+
+	/* advance past all space characters */
+
+	while ((*o_str != '\0') && (isspace(*o_str))) {
+		o_str++;
+	}
+
+	/* if string was all space characters, deallocate and return NULL */
+
+	if (*o_str == '\0') {
+		/* free string - *a_str is reset to NULL by free */
+		free(*a_str);
+		*a_str = (char *)NULL;
+		return;
+	}
+
+	/* have non-space/null byte, return dup, deallocate original */
+
+	o_str = strdup(o_str);
+	assert(o_str != (char *)NULL);
+	if (o_str != (char *)NULL) {
+		free(*a_str);
+		*a_str = o_str;
+	}
+}
+
+unsigned long
+pkgstrNumTokens(char *a_string, char *a_separators)
+{
+	int	index;
+
+	if (a_string == (char *)NULL) {
+		return (0);
+	}
+
+	if (*a_string == '\0') {
+		return (0);
+	}
+
+	for (index = 0 ; ; index ++) {
+		char *p;
+
+		p = pkgstrGetToken((char *)NULL, a_string, index, a_separators);
+		if (p == (char *)NULL) {
+			return (index);
+		}
+		free(p);
+	}
+}
+
+/*
+ * Name:	pkgstrPrintf_r
+ * Synopsis:	Create string from printf style format and arguments
+ * Description:	Call to convert a printf style format and arguments into a
+ *		string of characters placed in allocated storage
+ * Arguments:	a_buf - [RO, *RW] - (char *)
+ *			- Pointer to buffer used as storage space for the
+ *			  returned string created
+ *		a_bufLen - [RO, *RO] - (int)
+ *			- Size of 'a_buf' in bytes - a maximum of 'a_bufLen-1'
+ *			  bytes will be placed in 'a_buf' - the returned
+ *			  string is always null terminated
+ *		a_format - [RO, RO*] (char *)
+ *			printf-style format for string to be formatted
+ *		VARG_LIST - [RO] (?)
+ *			arguments as appropriate to 'format' specified
+ * Returns:	void
+ */
+
+/*PRINTFLIKE3*/
+void
+pkgstrPrintf_r(char *a_buf, int a_bufLen, char *a_format, ...)
+{
+	va_list		ap;
+	size_t		vres = 0;
+
+	/* entry assertions */
+
+	assert(a_format != (char *)NULL);
+	assert(*a_format != '\0');
+	assert(a_buf != (char *)NULL);
+	assert(a_bufLen > 1);
+
+	/* generate the results of the printf conversion */
+
+	va_start(ap, a_format);
+	vres = vsnprintf(a_buf, a_bufLen-1, a_format, ap);
+	va_end(ap);
+
+	assert(vres > 0);
+	assert(vres < a_bufLen);
+
+	a_buf[a_bufLen-1] = '\0';
+}
+
+/*
+ * Name:	pkgstrPrintf
+ * Synopsis:	Create string from printf style format and arguments
+ * Description:	Call to convert a printf style format and arguments into a
+ *		string of characters placed in allocated storage
+ * Arguments:	format - [RO, RO*] (char *)
+ *			printf-style format for string to be formatted
+ *		VARG_LIST - [RO] (?)
+ *			arguments as appropriate to 'format' specified
+ * Returns:	char *
+ *			A string representing the printf conversion results
+ * NOTE:    	Any string returned is placed in new storage for the
+ *		calling method. The caller must use 'free' to dispose
+ *		of the storage once the string is no longer needed.
+ * Errors:	If the string cannot be created, the process exits
+ */
+
+/*PRINTFLIKE1*/
+char *
+pkgstrPrintf(char *a_format, ...)
+{
+	va_list		ap;
+	size_t		vres = 0;
+	char		bfr[1];
+	char		*rstr = (char *)NULL;
+
+	/* entry assertions */
+
+	assert(a_format != (char *)NULL);
+	assert(*a_format != '\0');
+
+	/* determine size of the message in bytes */
+
+	va_start(ap, a_format);
+	vres = vsnprintf(bfr, 1, a_format, ap);
+	va_end(ap);
+
+	assert(vres > 0);
+	assert(vres < LINE_MAX);
+
+	/* allocate storage to hold the message */
+
+	rstr = (char *)calloc(1, vres+2);
+	assert(rstr != (char *)NULL);
+	if (rstr == (char *)NULL) {
+		return ((char *)NULL);
+	}
+
+	/* generate the results of the printf conversion */
+
+	va_start(ap, a_format);
+	vres = vsnprintf(rstr, vres+1, a_format, ap);
+	va_end(ap);
+
+	assert(vres > 0);
+	assert(vres < LINE_MAX);
+	assert(*rstr != '\0');
+
+	/* return the results */
+
+	return (rstr);
+}
+
+/*
+ * Name:	pkgstrExpandTokens
+ * Synopsis:	Expand tokens from string appending tokens to another
+ * Description:	Given a string and a list of one or more separators,
+ *		expand each token from the string and append those tokens
+ *		to a string that is in allocated space - create new string
+ *		if no string to append to exists.
+ * Arguments:	a_old - [RO, *RW] - (char **)
+ *			- Pointer to handle to string to append token to
+ *			  == (char *)NULL - new string is created
+ *		a_separator - [RO, *RO] - (char *)
+ *			- separator to end tokens returned
+ *		a_separators - [RO, *RO] - (char *)
+ *			- String containing one or more characters that
+ *			  can separate one "token" from a_string from another
+ * Returns:	void
+ * NOTE:    	Any token string returned is placed in new storage for the
+ *		calling method. The caller must use 'free' to dispose
+ *		of the storage once the token string is no longer needed.
+ */
+
+void
+pkgstrExpandTokens(char **a_old, char *a_string, char a_separator,
+	char *a_separators)
+{
+	int		i;
+	char		sep[2] = {'\0', '\0'};
+
+	/* convert single separator character into character string */
+
+	sep[0] = a_separator;
+
+	/*
+	 * iterate extracting tokens from the source string and adding
+	 * those tokens to the target string when the tokens are not
+	 * already present in the target string
+	 */
+
+	for (i = 0; ; i++) {
+		char	*p;
+
+		/* extract the next matching token from the source string */
+
+		p = pkgstrGetToken((char *)NULL, a_string, i, a_separators);
+
+		/* return if no token is available */
+
+		if (p == (char *)NULL) {
+			return;
+		}
+
+		/*
+		 * obtained token from source string: if the token is not
+		 * in the target string, add the token to the target string
+		 */
+
+		if (pkgstrContainsToken(*a_old, p, sep) == B_FALSE) {
+			pkgstrAddToken(a_old, p, *sep);
+		}
+
+		/* free up temporary storage used by token from source string */
+
+		free(p);
+	}
+	/*NOTREACHED*/
+}
+
+
+/*
+ * Name:	pkgstrGetToken
+ * Synopsis:	Get a separator delimited token from a string
+ * Description:	Given a string and a list of one or more separators,
+ *		return the position specified token (sequence of one or
+ *		more characters that do not include any of the separators)
+ * Arguments:	r_sep - [*RW] - (char *)
+ *			- separator that ended the token returned
+ *			- NOTE: this is a pointer to a "char", e.g.:
+ *				- char a;
+ *				- pkgstrGetToken(&a, ...)
+ *		a_string - [RO, *RO] - (char *)
+ *			- pointer to string to extract token from
+ *		a_index - [RO, *RO] - (int)
+ *			- Index of token to return; '0' is first matching
+ *			  token, '1' is second matching token, etc.
+ *		a_separators - [RO, *RO] - (char *)
+ *			- String containing one or more characters that
+ *			  can separate one "token" from another
+ * Returns:	char *
+ *			== (char *)NULL - no token matching criteria found
+ *			!= (char *)NULL - token matching criteria
+ * NOTE:    	Any token string returned is placed in new storage for the
+ *		calling method. The caller must use 'free' to dispose
+ *		of the storage once the token string is no longer needed.
+ */
+
+char *
+pkgstrGetToken(char *r_sep, char *a_string, int a_index, char *a_separators)
+{
+	char	*p;
+	char	*q;
+	char	*lasts;
+
+	/* entry assertions */
+
+	assert(a_string != (char *)NULL);
+	assert(a_index >= 0);
+	assert(a_separators != (char *)NULL);
+	assert(*a_separators != '\0');
+
+	/* if returned separator requested, reset to null until token found */
+
+	if (r_sep != (char *)NULL) {
+		*r_sep = '\0';
+	}
+
+	/* duplicate original string before breaking down into tokens */
+
+	p = strdup(a_string);
+	assert(p != (char *)NULL);
+	if (p == (char *)NULL) {
+		return ((char *)NULL);
+	}
+	lasts = p;
+
+	/* scan for separators and return 'index'th token found */
+
+	while (q = strtok_r((char *)NULL, a_separators, &lasts)) {
+		/* retrieve separator if requested */
+
+		if (r_sep != (char *)NULL) {
+			char	*x;
+
+			x = strpbrk(a_string, a_separators);
+			if (x) {
+				*r_sep = *x;
+			}
+		}
+
+		/* if this is the 'index'th token requested return it */
+
+		if (a_index-- == 0) {
+			char	*tmp;
+
+			/* duplicate token into its own storage */
+
+			tmp = strdup(q);
+			assert(tmp != (char *)NULL);
+			if (tmp == (char *)NULL) {
+				return ((char *)NULL);
+			}
+
+			/* free up copy of original input string */
+
+			free(p);
+
+			/* return token found */
+
+			return (tmp);
+		}
+	}
+
+	/*
+	 * token not found
+	 */
+
+	/* free up copy of original input string */
+
+	free(p);
+
+	/* return NULL pointer (token not found) */
+
+	return ((char *)NULL);
+}
+
+/*
+ * Name:	pkgstrGetToken
+ * Synopsis:	Get separator delimited token from a string into a fixed buffer
+ * Description:	Given a string and a list of one or more separators,
+ *		return the position specified token (sequence of one or
+ *		more characters that do not include any of the separators)
+ *		into a specified buffer of a fixed maximum size
+ * Arguments:	r_sep - [*RW] - (char *)
+ *			- separator that ended the token returned
+ *			- NOTE: this is a pointer to a "char", e.g.:
+ *				- char a;
+ *				- pkgstrGetToken(&a, ...)
+ *		a_string - [RO, *RO] - (char *)
+ *			- pointer to string to extract token from
+ *		a_index - [RO, *RO] - (int)
+ *			- Index of token to return; '0' is first matching
+ *			  token, '1' is second matching token, etc.
+ *		a_separators - [RO, *RO] - (char *)
+ *			- String containing one or more characters that
+ *			  can separate one "token" from another
+ *		a_buf - [RO, *RW] - (char *)
+ *			- Pointer to buffer used as storage space for the
+ *			  returned token - the returned token is always
+ *			  null terminated
+ *			  a_buf[0] == '\0' - no token meeting criteria found
+ *			  a_buf[0] != '\0' - token meeting criteria returned
+ *		a_bufLen - [RO, *RO] - (int)
+ *			- Size of 'a_buf' in bytes - a maximum of 'a_bufLen-1'
+ *			  bytes will be placed in 'a_buf' - the returned
+ *			  token is always null terminated
+ * Returns:	void
+ */
+
+void
+pkgstrGetToken_r(char *r_sep, char *a_string, int a_index,
+	char *a_separators, char *a_buf, int a_bufLen)
+{
+	char	*p;
+	char	*q;
+	char	*lasts;
+
+	/* entry assertions */
+
+	assert(a_string != (char *)NULL);
+	assert(a_index >= 0);
+	assert(a_separators != (char *)NULL);
+	assert(*a_separators != '\0');
+	assert(a_buf != (char *)NULL);
+	assert(a_bufLen > 0);
+
+	/* reset returned separator */
+
+	if (r_sep != (char *)NULL) {
+		*r_sep = '\0';
+	}
+
+	/* zero out contents of return buffer */
+
+	bzero(a_buf, a_bufLen);
+
+	/* duplicate original string before breaking down into tokens */
+
+	p = strdup(a_string);
+	assert(p != (char *)NULL);
+	if (p == (char *)NULL) {
+		return;
+	}
+	lasts = p;
+
+	/* scan for separators and return 'index'th token found */
+
+	while (q = strtok_r((char *)NULL, a_separators, &lasts)) {
+		/* retrieve separator if requested */
+
+		if (r_sep != (char *)NULL) {
+			char	*x;
+			x = strpbrk(a_string, a_separators);
+			if (x) {
+				*r_sep = *x;
+			}
+		}
+
+		/* if this is the 'index'th token requested return it */
+
+		if (a_index-- == 0) {
+			/* copy as many characters as possible to return buf */
+
+			(void) strncpy(a_buf, q, a_bufLen-1);
+			break;
+		}
+	}
+
+	/* free up copy of original input string */
+
+	free(p);
+}
+
+/*
+ * Name:	pkgstrAddToken
+ * Synopsis:	Add a token to a string
+ * Description:	Append a token (sequence of one or more characters) to a
+ *		string that is in allocated space - create new string if
+ *		no string to append to exists
+ * Arguments:	a_old - [RO, *RW] - (char **)
+ *			- Pointer to handle to string to append token to
+ *			  == (char *)NULL - new string is created
+ *		a_new - [RO, *RO] - (char *)
+ *			- Pointer to string representing token to append
+ *			  to the end of the "a_old" string
+ *			  == (char *)NULL - no action is performed
+ *			  a_new[0] == '\0' - no action is performed
+ *		a_separator - [RO, *RO] - (char)
+ *			- One character placed between the old (existing)
+ *			  string and the new token to be added IF the old
+ *			  string exists and is not empty (zero length)
+ * Returns:	void
+ * CAUTION:	The old (existing) string must be allocated space (via lu_mem*
+ *		or pkgstr* methods) - it must not be a static or inline
+ *		character string
+ * NOTE:	The old (existing) string may be freed with 'free'
+ *		if a token is appended to it
+ * NOTE:    	Any string returned in 'a_old' is placed in new storage for the
+ *		calling method. The caller must use 'free' to dispose
+ *		of the storage once the token string is no longer needed.
+ */
+
+void
+pkgstrAddToken(char **a_old, char *a_new, char a_separator)
+{
+	/* entry assertions */
+
+	assert(a_old != (char **)NULL);
+	assert(a_separator != '\0');
+
+	/* if token to add is null, just return */
+
+	if (a_new == (char *)NULL) {
+		return;
+	}
+
+	/* if token to add is empty (zero length), just return */
+
+	if (*a_new == '\0') {
+		return;
+	}
+
+	/* make sure that new token does not contain the separator */
+
+	assert(strchr(a_new, (int)a_separator) == (char *)NULL);
+
+	/* if old string is empty (zero length), deallocate */
+
+	if ((*a_old != (char *)NULL) && ((*a_old)[0] == '\0')) {
+		/* *a_old is set to NULL by free */
+		free(*a_old);
+		*a_old = (char *)NULL;
+	}
+
+	/* if old string is exists, append separator and token */
+
+	if (*a_old != (char *)NULL) {
+		char *p;
+		p = pkgstrPrintf("%s%c%s", *a_old, a_separator, a_new);
+		free(*a_old);
+		*a_old = p;
+		return;
+	}
+
+	/* old string does not exist - return duplicate of token */
+
+	assert(*a_old == (char *)NULL);
+	*a_old = strdup(a_new);
+	assert(*a_old != (char *)NULL);
+}
+
+/*
+ * Name:	pkgstrContainsToken
+ * Synopsis:	Does a given string contain a specified substring
+ * Description:	Determine if a given substring exists in a larger string
+ * Arguments:	a_string - [RO, *RO] - (char *)
+ *			Pointer to string to look for substring in
+ *		a_token - [RO, *RO] - (char *)
+ *			Pointer to substring to look for in larger string
+ * Results:	boolean_t
+ *			B_TRUE - substring exists in larger string
+ *			B_FALSE - substring does NOT exist in larger string
+ * NOTE:	The substring must match on a "token" basis; that is, the
+ *		substring must exist in the larger string delineated with
+ *		either spaces or tabs to match.
+ */
+
+boolean_t
+pkgstrContainsToken(char *a_string, char *a_token, char *a_separators)
+{
+	char	*lasts;
+	char	*current;
+	char	*p;
+
+	/* entry assertions */
+
+	assert(a_separators != (char *)NULL);
+	assert(*a_separators != '\0');
+
+	/* if token is not supplied, return false */
+
+	if (a_token == (char *)NULL) {
+		return (B_FALSE);
+	}
+
+	/* if no string provided, return false */
+
+	if (a_string == (char *)NULL) {
+		return (B_FALSE);
+	}
+
+	/* if string empty (zero length), return false */
+
+	if (*a_string == '\0') {
+		return (B_FALSE);
+	}
+
+	/* duplicate larger string because strtok_r changes it */
+
+	p = strdup(a_string);
+	assert(p != (char *)NULL);
+	if (p == (char *)NULL) {
+		return (B_FALSE);
+	}
+
+	lasts = p;
+
+	/* scan each token looking for a match */
+
+	while ((current = strtok_r((char *)NULL, a_separators, &lasts)) !=
+			(char *)NULL) {
+		if (streq(current, a_token)) {
+			free(p);
+			return (B_TRUE);
+		}
+	}
+
+	/* free up temporary storage */
+
+	free(p);
+
+	/* not found */
+
+	return (B_FALSE);
+}
+
+/*
+ * Name:	pkgstrRemoveToken
+ * Synopsis:	Remove a token from a string
+ * Description:	Remove a token (sequence of one or more characters) from a
+ *		string that is in allocated space
+ * Arguments:	r_string - [RO, *RW] - (char **)
+ *			- Pointer to handle to string to remove token from
+ *		a_token - [RO, *RO] - (char *)
+ *			Pointer to token (substring) to look for and remove
+ *			from r_string provided
+ *		a_separators - [RO, *RO] - (char *)
+ *			- String containing one or more characters that
+ *			  separate one "token" from another in r_string
+ *		a_index - [RO, *RO] - (int)
+ *			- Index of token to remove; '0' is first matching
+ *			  token, '1' is second matching token, etc.
+ * Returns:	void
+ * CAUTION:	The input string must be allocated space (via lu_mem* or
+ *		pkgstr* methods) - it must not be a static or inline
+ *		character string
+ * NOTE:	The input string r_string will be freed with 'free'
+ *		if the token to be removed is found
+ * NOTE:    	Any token string returned is placed in new storage for the
+ *		calling method. The caller must use 'free' to dispose
+ *		of the storage once the token string is no longer needed.
+ * Errors:	If the new token string cannot be created, the process exits
+ */
+
+void
+pkgstrRemoveToken(char **r_string, char *a_token, char *a_separators,
+	int a_index)
+{
+	char	*a_string;
+	char	*copyString;
+	char	sep = 0;
+	int	copyLength;
+	int	i;
+
+	/* entry assertions */
+
+	assert(r_string != (char **)NULL);
+	assert(a_token != (char *)NULL);
+	assert(*a_token != '\0');
+	assert(a_separators != (char *)NULL);
+	assert(*a_separators != '\0');
+
+	/* simple case: input string is null; return empty string */
+
+	a_string = *r_string;
+	if (*a_string == '\0') {
+		return;
+	}
+
+	/* simple case: token == input string; return empty string */
+
+	if (streq(a_string, a_token)) {
+		/* deallocate input string; free sets *r_string to NULL */
+
+		free(*r_string);
+		*r_string = (char *)NULL;
+		return;
+	}
+
+	/* simple case: token not in input string: return */
+
+	if (!pkgstrContainsToken(a_string, a_token, a_separators)) {
+		return;
+	}
+
+	/*
+	 * Pick apart the old string building the new one as we go along
+	 * removing the first occurance of the token provided
+	 */
+
+	copyLength = (strlen(a_string)-strlen(a_token))+2;
+	copyString = calloc(1, copyLength);
+	assert(copyString != (char *)NULL);
+	if (copyString == (char *)NULL) {
+		return;
+	}
+
+	for (i = 0; ; i++) {
+		char	*p;
+
+		p = pkgstrGetToken(&sep, a_string, i, a_separators);
+		if (p == (char *)NULL) {
+			break;
+		}
+
+		if (streq(p, a_token) && (a_index-- == 0)) {
+			continue;
+		}
+
+		if (*copyString) {
+			assert(sep != '\0');
+			(void) strncat(copyString, &sep, 1);
+		}
+
+		(void) strcat(copyString, p);
+	}
+
+	free(*r_string);
+	assert(*copyString);
+	*r_string = copyString;
+}
+
+/*
+ * Name:	pkgstrScaleNumericString
+ * Synopsis:	Convert unsigned long long to human readable form
+ * Description:	Convert a string containing an unsigned long long representation
+ *		and convert it into a human readable numeric string. The number
+ *		is scaled down until it is small enough to be in a good human
+ *		readable format i.e. in the range 0 thru scale-1.
+ * Arguments:	a_buf - [RO, *RW] - (char *)
+ *			Pointer to buffer containing string representation
+ *			of unsigned long long to convert
+ *		scale - [RO, *RO] - (unsigned long long)
+ *			Value to scale the number into
+ * Returns:	a_buf - contains human readable scaled representation of
+ *			original value contained in the buffer
+ * Note:	The value "(unsigned long long)-1" is a special case and
+ *		is always converted to "-1".
+ * Errors:	If the string cannot be created, the process exits
+ */
+
+void
+pkgstrScaleNumericString(char *a_buf, unsigned long long scale)
+{
+static char		*M = " KMGTPE"; /* Measurement: */
+					/* kilo, mega, giga, tera, peta, exa */
+
+	unsigned long long number = 0;	/* convert this number */
+	unsigned long long save = 0;
+	char	*uom = M;    /* unit of measurement, initially ' ' (=M[0]) */
+
+	/* entry assertions */
+
+	assert(scale > (unsigned long long)0);
+	assert(scale <=  (unsigned long long)1048576);
+
+	/*
+	 * Get the number - if no number of empty number, just return
+	 */
+
+	if (a_buf == (char *)NULL) {
+		return;
+	}
+
+	if (*a_buf == '\0') {
+		(void) strcpy(a_buf, "0");
+		return;
+	}
+
+	/* convert out the number from the input buffer */
+
+	number = strtoull(a_buf, (char **)NULL, 10);
+
+	/* if conversion error, return "-1" */
+
+	if ((long long)number == (long long)-1) {
+		(void) strcpy(a_buf, "-1");
+		return;
+	}
+
+	/*
+	 * Now have number as a count of scale units.
+	 * Stop scaling when we reached exa-bytes, then something is
+	 * probably wrong with our number (it is improbably large)
+	 */
+
+	while ((number >= scale) && (*uom != 'E')) {
+		uom++; /* next unit of measurement */
+		save = number;
+		number = (number + (scale / 2)) / scale;
+	}
+
+	/* check if we should output a decimal place after the point */
+
+	if (save && ((save / scale) < 10)) {
+		/* sprintf() will round for us */
+		float fnum = (float)save / scale;
+		(void) sprintf(a_buf, "%4.1f%c", fnum, *uom);
+	} else {
+		(void) sprintf(a_buf, "%4llu%c", number, *uom);
+	}
+}
+
+/*
+ * Name:	pkgstrLocatePathBasename
+ * Synopsis:	Locate position of base name in path string
+ * Description:	Locate the base name (last path item) in a path and
+ *		return a pointer to the first byte of the base name
+ *		within the given path
+ * Arguments:	a_path - [RO, *RO] - (char *)
+ *			- Pointer to string representing path to scan
+ * Returns:	char *
+ *			- Pointer into string of first byte of path base name
+ *			- == (char *)NULL - input path is (char *)NULL
+ */
+
+char *
+pkgstrLocatePathBasename(char *a_path)
+{
+	char	*p;
+
+	/* if path is NULL, return NULL */
+
+	if (!a_path) {
+		return (a_path);
+	}
+
+	/* locate last occurance of '/' in path */
+
+	p = strrchr(a_path, '/');
+	if (p != (char *)NULL) {
+		/* base name located - return -> first byte */
+		return (p+1);
+	}
+
+	/* no occurance of '/' - entry path must be basename */
+
+	return (a_path);
+}
+
+/*
+ * Name:	pkgstrConvertPathToBasename
+ * Synopsis:	Return copy of base name in path string
+ * Description:	Locate the base name (last path item) in a path and
+ *		return a copy of the base name in allocated storage
+ * Arguments:	a_path - [RO, *RO] - (char *)
+ *			- Pointer to string representing path to scan
+ * Returns:	char *
+ *			- String containing path base name
+ *			- == (char *)NULL - input path is (char *)NULL
+ * NOTE:    	Any string returned is placed in new storage for the
+ *		calling method. The caller must use 'free' to dispose
+ *		of the storage once the string is no longer needed.
+ * Errors:	If the string cannot be created, the process exits
+ */
+
+char *
+pkgstrConvertPathToBasename(char *a_path)
+{
+	char	*p;
+
+	/* if path is NULL, return NULL */
+
+	if (a_path == (char *)NULL) {
+		return ((char *)NULL);
+	}
+
+	/* if path is empty (zero length), return NULL */
+
+	if (*a_path == '\0') {
+		return ((char *)NULL);
+	}
+
+	/* locate last occurance of '/' in path */
+
+	p = strrchr(a_path, '/');
+	if (p == (char *)NULL) {
+		/* no occurance of '/' - entry path must be basename */
+
+		return (strdup(a_path));
+	}
+
+	/* base name located - return string from -> first byte */
+
+	return (strdup(p+1));
+}
+
+/*
+ * Name:	pkgstrConvertPathToDirname
+ * Synopsis:	Return copy of directory in path string
+ * Description:	Locate the directory name (everything but last path item) in a
+ *		path and return a copy of the dir name in allocated storage
+ * Arguments:	a_path - [RO, *RO] - (char *)
+ *			- Pointer to string representing path to scan
+ * Returns:	char *
+ *			- String containing path directory name
+ *			- == (char *)NULL - input path is (char *)NULL,
+ *			  or a_path is empty (*a_path == '\0'), or the
+ *			  a_path has no directory name in it.
+ * NOTE:    	Any string returned is placed in new storage for the
+ *		calling method. The caller must use 'free' to dispose
+ *		of the storage once the string is no longer needed.
+ * Errors:	If the string cannot be created, the process exits
+ */
+
+char *
+pkgstrConvertPathToDirname(char *a_path)
+{
+	char	*p;
+	char	*retPath;
+
+	/* if path is NULL, return NULL */
+
+	if (a_path == (char *)NULL) {
+		return ((char *)NULL);
+	}
+
+	/* if path is empty (zero length), return NULL */
+
+	if (*a_path == '\0') {
+		return ((char *)NULL);
+	}
+
+	/* locate last occurance of '/' in path */
+
+	p = strrchr(a_path, '/');
+	if (p == (char *)NULL) {
+		/* no occurance of '/' - entire path must be basename */
+
+		return ((char *)NULL);
+	}
+
+	/* duplicate original path */
+
+	retPath = strdup(a_path);
+	assert(retPath != (char *)NULL);
+	if (retPath == (char *)NULL) {
+		return ((char *)NULL);
+	}
+
+	/* remove all trailing '/'s from copy of path */
+
+	for (p = strrchr(retPath, '/');	(p > retPath) && (*p == '/'); p--) {
+		*p = '\0';
+	}
+
+	/* if entire path was '/'s, return null string - no directory present */
+
+	if (*retPath == '\0') {
+		free(retPath);
+		return ((char *)NULL);
+	}
+
+	/* path has at least one non-'/' in it - return -> directory portion */
+
+	return (retPath);
+}
+
+/*
+ * Name:	pkgstrConvertUllToTimeString_r
+ * Synopsis:	Convert an unsigned long long into a "time string"
+ * Description:	Given an unsigned long long, return a "time string" which is a
+ *		conversion of the unsigned long long interpreted as a number of
+ *		nanoseconds into a "hour:minute:second.ns" ascii string
+ * Arguments:	a_time - [RO, *RO] - (unsigned long long)n
+ *			- value to convert
+ *		a_buf - [RO, *RW] - (char *)
+ *			- Pointer to buffer used as storage space for the
+ *			  returned string
+ *		a_bufLen - [RO, *RO] - (int)
+ *			- Size of 'a_buf' in bytes - a maximum of 'a_bufLen-1'
+ *			  bytes will be placed in 'a_buf'
+ * Returns:	char *
+ *			- String containing converted value
+ * NOTE:    	Any string returned is placed in new storage for the
+ *		calling method. The caller must use 'free' to dispose
+ *		of the storage once the string is no longer needed.
+ * Errors:	If the string cannot be created, the process exits
+ */
+
+void
+pkgstrConvertUllToTimeString_r(unsigned long long a_time,
+	char *a_buf, int a_bufLen)
+{
+	unsigned long long	seconds;
+	unsigned long long	minutes;
+	unsigned long long	hours;
+	unsigned long long	ns;
+
+	/* entry assertions */
+
+	assert(a_buf != (char *)NULL);
+	assert(a_bufLen > 0);
+
+	/* if time is 0, return immediate result */
+
+	if (a_time == 0) {
+		pkgstrPrintf_r(a_buf, a_bufLen, "%s", "0:00:00.000000000");
+		return;
+	}
+
+	/* break out individual time components */
+
+	ns = a_time % 1000000000ll;	/* nanoseconds left over from seconds */
+	seconds = a_time / 1000000000ll;	/* total seconds */
+	minutes = seconds / 60ll;	/* total minutes */
+	seconds = seconds % 60ll;	/* seconds left over from minutes */
+	hours = minutes / 60ll;		/* total hours */
+	minutes = minutes % 60ll;	/* minutes left over from hours */
+
+	/* return a converted string */
+
+	pkgstrPrintf_r(a_buf, a_bufLen, "%llu:%02llu:%02llu.%09llu",
+						hours, minutes, seconds, ns);
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/usr/src/lib/libpkg/common/pkgtrans.c	Fri Jun 05 10:28:40 2009 -0400
@@ -0,0 +1,1973 @@
+/*
+ * 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
+ * or http://www.opensolaris.org/os/licensing.
+ * 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 2009 Sun Microsystems, Inc.  All rights reserved.
+ * Use is subject to license terms.
+ */
+
+/* Copyright (c) 1984, 1986, 1987, 1988, 1989 AT&T */
+/* All Rights Reserved */
+
+
+
+#include <stdio.h>
+#include <errno.h>
+#include <stdarg.h>
+#include <limits.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <fcntl.h>
+#include <ctype.h>
+#include <string.h>
+#include <sys/types.h>
+#include <sys/param.h>
+#include <sys/stat.h>
+#include <sys/statvfs.h>
+#include <sys/sysmacros.h>
+#include <dirent.h>
+#include <signal.h>
+#include <devmgmt.h>
+#include <openssl/pkcs12.h>
+#include <openssl/x509.h>
+#include <openssl/pkcs7.h>
+#include <openssl/err.h>
+#include <openssl/pem.h>
+#include "pkginfo.h"
+#include "pkgstrct.h"
+#include "pkgtrans.h"
+#include "pkgdev.h"
+#include "pkglib.h"
+#include "pkglibmsgs.h"
+#include "keystore.h"
+#include "pkglocale.h"
+#include "pkgerr.h"
+
+extern char	*pkgdir; 		/* pkgparam.c */
+
+/* libadm.a */
+extern char	*devattr(char *device, char *attribute);
+extern char	*fpkginst(char *pkg, ...);
+extern int	fpkginfo(struct pkginfo *info, char *pkginst);
+extern int	getvol(char *device, char *label, int options, char *prompt);
+extern int	_getvol(char *device, char *label, int options, char *prompt,
+			char *norewind);
+
+/* dstream.c */
+extern int	ds_ginit(char *device);
+extern int	ds_close(int pkgendflg);
+
+#define	CPIOPROC	"/usr/bin/cpio"
+
+#define	CMDSIZE	512	/* command block size */
+
+#define	BLK_SIZE	512		/* size of logical block */
+
+#define	ENTRY_MAX	256 /* max size of entry for cpio cmd or header */
+
+#define	PKGINFO	"pkginfo"
+#define	PKGMAP	"pkgmap"
+#define	MAP_STAT_SIZE	60	/* 1st line of pkgmap (3 numbers & a : */
+
+#define	INSTALL	"install"
+#define	RELOC	"reloc"
+#define	ROOT	"root"
+#define	ARCHIVE	"archive"
+
+static struct	pkgdev srcdev, dstdev;
+static char	*tmpdir;
+static char	*tmppath;
+static char	*tmpsymdir = NULL;
+static char	dstinst[NON_ABI_NAMELNGTH];
+static char 	*ids_name, *ods_name;
+static int	ds_volcnt;
+static int	ds_volno;
+static int	compressedsize, has_comp_size;
+
+static void	(*sigintHandler)();
+static void	(*sighupHandler)();
+static void	cleanup(void);
+static void	sigtrap(int signo);
+static int	rd_map_size(FILE *fp, int *npts, int *maxpsz, int *cmpsize);
+
+static int	cat_and_count(struct dm_buf *, char *);
+
+static int	ckoverwrite(char *dir, char *inst, int options);
+static int	pkgxfer(char *srcinst, int options);
+static int	wdsheader(struct dm_buf *, char *src, char *device,
+    char **pkg, PKCS7 *);
+static struct dm_buf	*genheader(char *, char *, char **);
+
+static int	dump_hdr_and_pkgs(BIO *, struct dm_buf *, char **);
+
+extern int	ds_fd;	/* open file descriptor for data stream WHERE? */
+
+static char *root_names[] = {
+	"root",
+	"root.cpio",
+	"root.Z",
+	"root.cpio.Z",
+	0
+};
+
+static char *reloc_names[] = {
+	"reloc",
+	"reloc.cpio",
+	"reloc.Z",
+	"reloc.cpio.Z",
+	0
+};
+
+static int	signal_received = 0;
+
+char	**xpkg; 	/* array of transferred packages */
+int	nxpkg;
+
+static	char *allpkg[] = {
+	"all",
+	NULL
+};
+
+static struct dm_buf hdrbuf;
+static char *pinput, *nextpinput;
+
+int
+pkghead(char *device)
+{
+	char	*pt;
+	int	n;
+
+	cleanup();
+
+
+	if (device == NULL)
+		return (0);
+	else if ((device[0] == '/') && !isdir(device)) {
+		pkgdir = device;
+		return (0);
+	} else if ((pt = devattr(device, "pathname")) != NULL && !isdir(pt)) {
+		pkgdir = pt;
+		return (0);
+	}
+
+	/* check for datastream */
+	if (n = pkgtrans(device, (char *)0, allpkg, PT_SILENT|PT_INFO_ONLY,
+	    NULL, NULL)) {
+		cleanup();
+		return (n);
+	}
+		/* pkgtrans has set pkgdir */
+	return (0);
+}
+
+static char *
+mgets(char *buf, int size)
+{
+	nextpinput = strchr(pinput, '\n');
+	if (nextpinput == NULL)
+		return (0);
+	*nextpinput = '\0';
+	if ((int)strlen(pinput) > size)
+		return (0);
+	(void) strncpy(buf, pinput, strlen(pinput));
+	buf[strlen(pinput)] = '\0';
+	pinput = nextpinput + 1;
+	return (buf);
+}
+/*
+ * Here we construct the package size summaries for the headers. The
+ * pkgmap file associated with fp must be rewound to the beginning of the
+ * file. Note that we read three values from pkgmap first line in order
+ * to get the *actual* size if this package is compressed.
+ * This returns
+ *	0 : error
+ *	2 : not a compressed package
+ *	3 : compressed package
+ * and sets has_comp_size to indicate whether or not this is a compressed
+ * package.
+ */
+static int
+rd_map_size(FILE *fp, int *npts, int *maxpsz, int *cmpsize)
+{
+	int n;
+	char line_buffer[MAP_STAT_SIZE];
+
+	/* First read the null terminated first line */
+	if (fgets(line_buffer, MAP_STAT_SIZE, fp) == NULL) {
+		progerr(pkg_gt(ERR_TRANSFER));
+		logerr(pkg_gt(MSG_NOSIZE));
+		(void) fclose(fp);
+		ecleanup();
+		return (0);
+	}
+
+	n = sscanf(line_buffer, ": %d %d %d", npts, maxpsz, cmpsize);
+
+	if (n == 3)		/* A valid compressed package entry */
+		has_comp_size = 1;
+	else if (n == 2)	/* A valid standard package entry */
+		has_comp_size = 0;
+	else {			/* invalid entry */
+		progerr(pkg_gt(ERR_TRANSFER));
+		logerr(pkg_gt(MSG_NOSIZE));
+		(void) fclose(fp);
+		ecleanup();
+		return (0);
+	}
+
+	return (n);
+}
+
+/* will return 0, 1, 3, or 99 */
+static int
+_pkgtrans(char *device1, char *device2, char **pkg, int options,
+    keystore_handle_t keystore, char *keystore_alias)
+{
+	BIO			*p7_bio = NULL;
+	EVP_PKEY		*privkey = NULL;
+	PKCS7			*sec_pkcs7 = NULL;
+	PKCS7_SIGNER_INFO	*sec_signerinfo = NULL;
+	PKG_ERR			*err;
+	STACK_OF(X509)		*cacerts = NULL;
+	STACK_OF(X509)		*clcerts = NULL;
+	STACK_OF(X509)		*sec_chain = NULL;
+	X509			*pubcert = NULL;
+	boolean_t		making_sig = B_FALSE;
+	char			*src, *dst;
+	int			errflg, i, n;
+	struct			dm_buf *hdr;
+
+	making_sig = (keystore != NULL) ? B_TRUE : B_FALSE;
+
+	if (making_sig) {
+
+		/* new error object */
+		err = pkgerr_new();
+
+		/* find matching cert and key */
+		if (find_key_cert_pair(err, keystore,
+		    keystore_alias, &privkey, &pubcert) != 0) {
+			pkgerr(err);
+			pkgerr_free(err);
+			return (1);
+		}
+
+		/* get CA certificates */
+		if (find_ca_certs(err, keystore, &cacerts) != 0) {
+			pkgerr(err);
+			pkgerr_free(err);
+			return (1);
+		}
+
+		/* get CL (aka "chain") certificates */
+		if (find_cl_certs(err, keystore, &clcerts) != 0) {
+			pkgerr(err);
+			pkgerr_free(err);
+			return (1);
+		}
+
+		/* initialize PKCS7 object to be filled in later */
+		sec_pkcs7 = PKCS7_new();
+		PKCS7_set_type(sec_pkcs7, NID_pkcs7_signed);
+		sec_signerinfo = PKCS7_add_signature(sec_pkcs7,
+		    pubcert, privkey, EVP_sha1());
+
+		if (sec_signerinfo == NULL) {
+			progerr(gettext(ERR_SEC), keystore_alias);
+			ERR_print_errors_fp(stderr);
+			pkgerr_free(err);
+			return (1);
+		}
+
+		/* add signer cert into signature */
+		PKCS7_add_certificate(sec_pkcs7, pubcert);
+
+		/* attempt to resolve cert chain starting at the signer cert */
+		if (get_cert_chain(err, pubcert, clcerts, cacerts,
+		    &sec_chain) != 0) {
+			pkgerr(err);
+			pkgerr_free(err);
+			return (1);
+		}
+
+		/*
+		 * add the verification chain of certs into the signature.
+		 * The first cert is the user cert, which we don't need,
+		 * since it's baked in already, so skip it
+		 */
+		for (i = 1; i < sk_X509_num(sec_chain); i++) {
+			PKCS7_add_certificate(sec_pkcs7,
+			    sk_X509_value(sec_chain, i));
+		}
+
+		pkgerr_free(err);
+		err = NULL;
+	}
+
+	if (signal_received > 0) {
+		return (1);
+	}
+
+	/* transfer spool to appropriate device */
+	if (devtype(device1, &srcdev)) {
+		progerr(pkg_gt(ERR_TRANSFER));
+		logerr(pkg_gt(MSG_BADDEV), device1);
+		return (1);
+	}
+	srcdev.rdonly++;
+
+	/* check for datastream */
+	ids_name = NULL;
+	if (srcdev.bdevice) {
+		if (n = _getvol(srcdev.bdevice, NULL, NULL,
+		    pkg_gt("Insert %v into %p."), srcdev.norewind)) {
+			cleanup();
+			if (n == 3)
+				return (3);
+			progerr(pkg_gt(ERR_TRANSFER));
+			logerr(pkg_gt(MSG_GETVOL));
+			return (1);
+		}
+		if (ds_readbuf(srcdev.cdevice))
+			ids_name = srcdev.cdevice;
+	}
+
+	if (srcdev.cdevice && !srcdev.bdevice)
+		ids_name = srcdev.cdevice;
+	else if (srcdev.pathname) {
+		ids_name = srcdev.pathname;
+		if (access(ids_name, 0) == -1) {
+			progerr(ERR_TRANSFER);
+			logerr(pkg_gt(MSG_GETVOL));
+			return (1);
+		}
+	}
+
+	if (!ids_name && device2 == (char *)0) {
+		if (n = pkgmount(&srcdev, NULL, 1, 0, 0)) {
+			cleanup();
+			return (n);
+		}
+		if (srcdev.mount && *srcdev.mount)
+			pkgdir = strdup(srcdev.mount);
+		return (0);
+	}
+
+	if (ids_name && device2 == (char *)0) {
+		tmppath = tmpnam(NULL);
+		tmppath = strdup(tmppath);
+		if (tmppath == NULL) {
+			progerr(pkg_gt(ERR_TRANSFER));
+			logerr(pkg_gt(MSG_MEM));
+			return (1);
+		}
+		if (mkdir(tmppath, 0755)) {
+			progerr(pkg_gt(ERR_TRANSFER));
+			logerr(pkg_gt(MSG_MKDIR), tmppath);
+			return (1);
+		}
+		device2 = tmppath;
+	}
+
+	if (devtype(device2, &dstdev)) {
+		progerr(pkg_gt(ERR_TRANSFER));
+		logerr(pkg_gt(MSG_BADDEV), device2);
+		return (1);
+	}
+
+	if ((srcdev.cdevice && dstdev.cdevice) &&
+	    strcmp(srcdev.cdevice, dstdev.cdevice) == 0) {
+		progerr(pkg_gt(ERR_TRANSFER));
+		logerr(pkg_gt(MSG_SAMEDEV));
+		return (1);
+	}
+
+	ods_name = NULL;
+	if (dstdev.cdevice && !dstdev.bdevice || dstdev.pathname)
+		options |= PT_ODTSTREAM;
+
+	if (options & PT_ODTSTREAM) {
+		if (!((ods_name = dstdev.cdevice) != NULL ||
+		    (ods_name = dstdev.pathname) != NULL)) {
+			progerr(pkg_gt(ERR_TRANSFER));
+			logerr(pkg_gt(MSG_BADDEV), device2);
+			return (1);
+		}
+		if (ids_name) {
+			progerr(pkg_gt(ERR_TRANSFER));
+			logerr(pkg_gt(MSG_TWODSTREAM));
+			return (1);
+		}
+	} else {
+		/*
+		 * output device isn't a stream.  If we're making a signed
+		 * package, then fail, since we can't make signed,
+		 * non-stream pkgs
+		 */
+		if (making_sig) {
+			progerr(pkg_gt(ERR_TRANSFER));
+			logerr(pkg_gt(ERR_CANTSIGN));
+			return (1);
+		}
+	}
+
+	if ((srcdev.dirname && dstdev.dirname) &&
+	    strcmp(srcdev.dirname, dstdev.dirname) == 0) {
+		progerr(pkg_gt(ERR_TRANSFER));
+		logerr(pkg_gt(MSG_SAMEDEV));
+		return (1);
+	}
+
+	if ((srcdev.pathname && dstdev.pathname) &&
+	    strcmp(srcdev.pathname, dstdev.pathname) == 0) {
+		progerr(pkg_gt(ERR_TRANSFER));
+		logerr(pkg_gt(MSG_SAMEDEV));
+		return (1);
+	}
+
+	if (signal_received > 0) {
+		return (1);
+	}
+
+	if (ids_name) {
+		if (srcdev.cdevice && !srcdev.bdevice &&
+		(n = _getvol(srcdev.cdevice, NULL, NULL, NULL,
+		    srcdev.norewind))) {
+			cleanup();
+			if (n == 3)
+				return (3);
+			progerr(pkg_gt(ERR_TRANSFER));
+			logerr(pkg_gt(MSG_GETVOL));
+			return (1);
+		}
+		if (srcdev.dirname = tmpnam(NULL))
+			tmpdir = srcdev.dirname = strdup(srcdev.dirname);
+
+		if ((srcdev.dirname == NULL) || mkdir(srcdev.dirname, 0755) ||
+		    chdir(srcdev.dirname)) {
+			progerr(pkg_gt(ERR_TRANSFER));
+			logerr(pkg_gt(MSG_NOTEMP), srcdev.dirname);
+			cleanup();
+			return (1);
+		}
+		if (ds_init(ids_name, pkg, srcdev.norewind)) {
+			cleanup();
+			return (1);
+		}
+	} else if (srcdev.mount) {
+		if (n = pkgmount(&srcdev, NULL, 1, 0, 0)) {
+			cleanup();
+			return (n);
+		}
+	}
+
+	src = srcdev.dirname;
+	dst = dstdev.dirname;
+
+	if (chdir(src)) {
+		progerr(pkg_gt(ERR_TRANSFER));
+		logerr(pkg_gt(MSG_CHDIR), src);
+		cleanup();
+		return (1);
+	}
+
+	if (signal_received > 0) {
+		return (1);
+	}
+
+	xpkg = pkg = gpkglist(src, pkg, NULL);
+	if (!pkg) {
+		progerr(pkg_gt(ERR_TRANSFER));
+		logerr(pkg_gt(MSG_NOPKGS), src);
+		cleanup();
+		return (1);
+	}
+
+	for (nxpkg = 0; pkg[nxpkg]; /* void */) {
+		nxpkg++; /* count */
+	}
+
+	if (ids_name) {
+		ds_order(pkg); /* order requests */
+	}
+
+	if (signal_received > 0) {
+		return (1);
+	}
+
+	if (options & PT_ODTSTREAM) {
+		char line[128];
+
+		if (!dstdev.pathname &&
+		    (n = _getvol(ods_name, NULL, DM_FORMAT, NULL,
+		    dstdev.norewind))) {
+			cleanup();
+			if (n == 3)
+				return (3);
+			progerr(pkg_gt(ERR_TRANSFER));
+			logerr(pkg_gt(MSG_GETVOL));
+			return (1);
+		}
+		if ((hdr = genheader(src, ods_name, pkg)) == NULL) {
+			cleanup();
+			return (1);
+		}
+		if (making_sig) {
+			/* start up signature data stream */
+			PKCS7_content_new(sec_pkcs7, NID_pkcs7_data);
+			PKCS7_set_detached(sec_pkcs7, 1);
+			p7_bio = PKCS7_dataInit(sec_pkcs7, NULL);
+
+			/*
+			 * Here we generate all the data that will go into
+			 * the package, and send it through the signature
+			 * generator, essentially calculating the signature
+			 * of the entire package so we can place it in the
+			 * header.  Otherwise we'd have to place it at the end
+			 * of the pkg, which would break the ABI
+			 */
+			if (!(options & PT_SILENT)) {
+				(void) fprintf(stderr, pkg_gt(MSG_SIGNING),
+				    get_subject_display_name(pubcert));
+			}
+			if (dump_hdr_and_pkgs(p7_bio, hdr, pkg) != 0) {
+			    progerr(gettext(ERR_NOGEN));
+			    logerr(pkg_gt(MSG_GETVOL));
+			    cleanup();
+			    return (1);
+
+			}
+
+			BIO_flush(p7_bio);
+
+			/*
+			 * now generate PKCS7 signature
+			 */
+			if (!PKCS7_dataFinal(sec_pkcs7, p7_bio)) {
+			    progerr(gettext(ERR_NOGEN));
+			    logerr(pkg_gt(MSG_GETVOL));
+			    cleanup();
+			    return (1);
+			}
+
+			BIO_free(p7_bio);
+		}
+
+		/* write out header to stream, which includes signature */
+		if (wdsheader(hdr, src, ods_name, pkg, sec_pkcs7)) {
+			cleanup();
+			return (1);
+		}
+
+		if (sec_pkcs7 != NULL) {
+			/* nuke in-memory signature for safety */
+			PKCS7_free(sec_pkcs7);
+			sec_pkcs7 = NULL;
+		}
+
+		ds_volno = 1; /* number of volumes in datastream */
+		pinput = hdrbuf.text_buffer;
+		/* skip past first line in header */
+		(void) mgets(line, 128);
+	}
+
+	if (signal_received > 0) {
+		return (1);
+	}
+
+	errflg = 0;
+
+	for (i = 0; pkg[i]; i++) {
+
+		if (signal_received > 0) {
+			return (1);
+		}
+
+		if (!(options & PT_ODTSTREAM) && dstdev.mount) {
+			if (n = pkgmount(&dstdev, NULL, 0, 0, 1)) {
+				cleanup();
+				return (n);
+			}
+		}
+		if (errflg = pkgxfer(pkg[i], options)) {
+			pkg[i] = NULL;
+			if ((options & PT_ODTSTREAM) || (errflg != 2))
+				break;
+		} else if (strcmp(dstinst, pkg[i]))
+			pkg[i] = strdup(dstinst);
+	}
+
+	if (!(options & PT_ODTSTREAM) && dst) {
+		pkgdir = strdup(dst);
+	}
+
+	/*
+	 * No cleanup of temporary directories created in this
+	 * function is done here. The calling function must do
+	 * the cleanup.
+	 */
+
+	return (signal_received > 0 ? 1 : errflg);
+}
+
+int
+pkgtrans(char *device1, char *device2, char **pkg, int options,
+    keystore_handle_t keystore, char *keystore_alias)
+{
+	int			r;
+	struct sigaction	nact;
+	struct sigaction	oact;
+
+	/*
+	 * setup signal handlers for SIGINT and SIGHUP and release hold
+	 */
+
+	/* hold SIGINT/SIGHUP interrupts */
+
+	(void) sighold(SIGHUP);
+	(void) sighold(SIGINT);
+
+	/* hook SIGINT to sigtrap */
+
+	nact.sa_handler = sigtrap;
+	nact.sa_flags = SA_RESTART;
+	(void) sigemptyset(&nact.sa_mask);
+
+	if (sigaction(SIGINT, &nact, &oact) < 0) {
+		sigintHandler = SIG_DFL;
+	} else {
+		sigintHandler = oact.sa_handler;
+	}
+
+	/* hook SIGHUP to sigtrap */
+
+	nact.sa_handler = sigtrap;
+	nact.sa_flags = SA_RESTART;
+	(void) sigemptyset(&nact.sa_mask);
+
+	if (sigaction(SIGHUP, &nact, &oact) < 0) {
+		sighupHandler = SIG_DFL;
+	} else {
+		sighupHandler = oact.sa_handler;
+	}
+
+	/* reset signal received count */
+
+	signal_received = 0;
+
+	/* release hold on signals */
+
+	(void) sigrelse(SIGHUP);
+	(void) sigrelse(SIGINT);
+
+	/*
+	 * perform the package translation
+	 */
+
+	r = _pkgtrans(device1, device2, pkg, options, keystore, keystore_alias);
+
+	/*
+	 * reset signal handlers
+	 */
+
+	/* hold SIGINT/SIGHUP interrupts */
+
+	(void) sighold(SIGHUP);
+	(void) sighold(SIGINT);
+
+	/* reset SIGINT */
+
+	nact.sa_handler = sigintHandler;
+	nact.sa_flags = SA_RESTART;
+	(void) sigemptyset(&nact.sa_mask);
+
+	(void) sigaction(SIGINT, &nact, (struct sigaction *)NULL);
+
+	/* reset SIGHUP */
+
+	nact.sa_handler = sighupHandler;
+	nact.sa_flags = SA_RESTART;
+	(void) sigemptyset(&nact.sa_mask);
+
+	(void) sigaction(SIGHUP, &nact, (struct sigaction *)NULL);
+
+	/* if signal received and pkgtrans returned error, call cleanup */
+
+	if (signal_received > 0) {
+		if (r != 0) {
+			cleanup();
+		}
+		(void) kill(getpid(), SIGINT);
+	}
+
+	/* release hold on signals */
+
+	(void) sigrelse(SIGHUP);
+	(void) sigrelse(SIGINT);
+
+	return (r);
+}
+
+/*
+ * This function concatenates append to the text described in the buf_ctrl
+ * structure. This code modifies data in this structure and handles all
+ * allocation issues. It returns '0' if everything was successful and '1'
+ * if not.
+ */
+static int
+cat_and_count(struct dm_buf *buf_ctrl, char *append)
+{
+
+	/* keep allocating until we have enough room to hold string */
+	while ((buf_ctrl->offset + (int)strlen(append))
+	    >= buf_ctrl->allocation) {
+		/* reallocate (and maybe move) text buffer */
+		if ((buf_ctrl->text_buffer =
+		    (char *)realloc(buf_ctrl->text_buffer,
+		    buf_ctrl->allocation + BLK_SIZE)) == NULL) {
+			progerr(pkg_gt(ERR_TRANSFER));
+			logerr(pkg_gt(MSG_MEM));
+			free(buf_ctrl->text_buffer);
+			return (1);
+		}
+
+		/* clear the new memory */
+		(void) memset(buf_ctrl->text_buffer +
+		    buf_ctrl->allocation, '\0', BLK_SIZE);
+
+		/* adjust total allocation */
+		buf_ctrl->allocation += BLK_SIZE;
+	}
+
+	/* append new string to end of buffer */
+	while (*append) {
+		*(buf_ctrl->text_buffer + buf_ctrl->offset) = *append++;
+		(buf_ctrl->offset)++;
+	}
+
+	return (0);
+}
+
+static struct dm_buf *
+genheader(char *src, char *device, char **pkg)
+{
+
+	FILE	*fp;
+	char	path[MAXPATHLEN], tmp_entry[ENTRY_MAX];
+	int	i, n, nparts, maxpsize;
+	int	partcnt, totsize;
+	struct stat statbuf;
+
+	if ((hdrbuf.text_buffer = (char *)malloc(BLK_SIZE)) == NULL) {
+		progerr(pkg_gt(ERR_TRANSFER));
+		logerr(pkg_gt(MSG_MEM));
+		return (NULL);
+	}
+
+	/* clear the new memory */
+	(void) memset(hdrbuf.text_buffer, '\0', BLK_SIZE);
+
+	/* set up the buffer control structure for the header */
+	hdrbuf.offset = 0;
+	hdrbuf.allocation = BLK_SIZE;
+
+	(void) cat_and_count(&hdrbuf, HDR_PREFIX);
+	(void) cat_and_count(&hdrbuf, "\n");
+
+	nparts = maxpsize = 0;
+
+	totsize = 0;
+	for (i = 0; pkg[i]; i++)  {
+		(void) snprintf(path, MAXPATHLEN, "%s/%s/%s",
+		    src, pkg[i], PKGINFO);
+		if (stat(path, &statbuf) < 0) {
+			progerr(pkg_gt(ERR_TRANSFER));
+			logerr(pkg_gt(MSG_BADPKGINFO));
+			ecleanup();
+			return (NULL);
+		}
+		totsize += statbuf.st_size/BLK_SIZE + 1;
+	}
+
+	/*
+	 * totsize contains number of blocks used by the pkginfo files
+	 */
+	totsize += i/4 + 1;
+	if (dstdev.capacity && totsize > dstdev.capacity) {
+		progerr(pkg_gt(ERR_TRANSFER));
+		logerr(pkg_gt(MSG_NOSPACE), totsize, dstdev.capacity);
+		ecleanup();
+		return (NULL);
+	}
+
+	ds_volcnt = 1;
+	for (i = 0; pkg[i]; i++) {
+		partcnt = 0;
+		(void) snprintf(path, MAXPATHLEN, "%s/%s/%s",
+		    src, pkg[i], PKGMAP);
+		if ((fp = fopen(path, "r")) == NULL) {
+			progerr(pkg_gt(ERR_TRANSFER));
+			logerr(pkg_gt(MSG_NOPKGMAP), pkg[i]);
+			ecleanup();
+			return (NULL);
+		}
+
+		/* Evaluate the first entry in pkgmap */
+		n = rd_map_size(fp, &nparts, &maxpsize, &compressedsize);
+
+		if (n == 3)	/* It's a compressed package */
+			/* The header needs the *real* size */
+			maxpsize = compressedsize;
+		else if (n == 0)	/* pkgmap is corrupt */
+			return (NULL);
+
+		if (dstdev.capacity && maxpsize > dstdev.capacity) {
+			progerr(pkg_gt(ERR_TRANSFER));
+			logerr(pkg_gt(MSG_NOSPACE));
+			(void) fclose(fp);
+			ecleanup();
+			return (NULL);
+		}
+
+		/* add pkg name, number of parts and the max part size */
+		if (snprintf(tmp_entry, ENTRY_MAX, "%s %d %d",
+				pkg[i], nparts, maxpsize) >= ENTRY_MAX) {
+			progerr(pkg_gt(ERR_TRANSFER));
+			logerr(pkg_gt(ERR_MEM));
+			(void) fclose(fp);
+			ecleanup();
+			return (NULL);
+		}
+		if (cat_and_count(&hdrbuf, tmp_entry)) {
+			progerr(pkg_gt(ERR_TRANSFER));
+			logerr(pkg_gt(MSG_MEM));
+			(void) fclose(fp);
+			ecleanup();
+			return (NULL);
+		}
+
+		totsize += nparts * maxpsize;
+		if (dstdev.capacity && dstdev.capacity < totsize) {
+			int lastpartcnt = 0;
+#if 0
+			if (i != 0) {
+				progerr(pkg_gt(ERR_TRANSFER));
+				logerr(pkg_gt(MSG_NOSPACE));
+				(void) fclose(fp);
+				ecleanup();
+				return (NULL);
+			}
+#endif	/* 0 */
+
+			if (totsize)
+				totsize -= nparts * maxpsize;
+			while (partcnt < nparts) {
+				while (totsize <= dstdev.capacity &&
+				    partcnt <= nparts) {
+					totsize +=  maxpsize;
+					partcnt++;
+				}
+				/* partcnt == 0 means skip to next volume */
+				if (partcnt)
+					partcnt--;
+				(void) snprintf(tmp_entry, ENTRY_MAX,
+				    " %d", partcnt - lastpartcnt);
+				if (cat_and_count(&hdrbuf, tmp_entry)) {
+					progerr(pkg_gt(ERR_TRANSFER));
+					logerr(pkg_gt(MSG_MEM));
+					(void) fclose(fp);
+					ecleanup();
+					return (NULL);
+				}
+				ds_volcnt++;
+				totsize = 0;
+				lastpartcnt = partcnt;
+			}
+			/* first parts/volume number does not count */
+			ds_volcnt--;
+		}
+
+		if (cat_and_count(&hdrbuf, "\n")) {
+			progerr(pkg_gt(ERR_TRANSFER));
+			logerr(pkg_gt(MSG_MEM));
+			(void) fclose(fp);
+			ecleanup();
+			return (NULL);
+		}
+
+		(void) fclose(fp);
+	}
+
+	if (cat_and_count(&hdrbuf, HDR_SUFFIX) ||
+	    cat_and_count(&hdrbuf, "\n")) {
+		progerr(pkg_gt(ERR_TRANSFER));
+		logerr(pkg_gt(MSG_MEM));
+		(void) fclose(fp);
+		ecleanup();
+		return (NULL);
+	}
+	return (&hdrbuf);
+}
+
+static int
+wdsheader(struct dm_buf *hdr, char *src, char *device, char **pkg, PKCS7 *sig)
+{
+	FILE	*fp;
+	char	path[PATH_MAX], tmp_entry[ENTRY_MAX],
+	    tmp_file[L_tmpnam+1];
+	char	srcpath[PATH_MAX];
+	int	i, n;
+	int	list_fd;
+	int	block_cnt;
+	int 	len;
+	char	cwd[MAXPATHLEN + 1];
+	boolean_t	making_sig = B_FALSE;
+
+	making_sig = (sig != NULL) ? B_TRUE : B_FALSE;
+
+	(void) ds_close(0);
+	if (dstdev.pathname)
+		ds_fd = creat(device, 0644);
+	else
+		ds_fd = open(device, 1);
+
+	if (ds_fd < 0) {
+		progerr(pkg_gt(ERR_TRANSFER));
+		logerr(pkg_gt(MSG_OPEN), device, errno);
+		return (1);
+	}
+
+	if (ds_ginit(device) < 0) {
+		progerr(pkg_gt(ERR_TRANSFER));
+		logerr(pkg_gt(MSG_OPEN), device, errno);
+		(void) ds_close(0);
+		return (1);
+	}
+
+	/*
+	 * The loop below assures compatibility with tapes that don't
+	 * have a block size (e.g.: Exabyte) by forcing EOR at the end
+	 * of each 512 bytes.
+	 */
+	for (block_cnt = 0; block_cnt < hdr->allocation;
+		block_cnt += BLK_SIZE) {
+		write(ds_fd, (hdr->text_buffer + block_cnt), BLK_SIZE);
+	}
+
+	/*
+	 * write the first cpio() archive to the datastream
+	 * which should contain the pkginfo & pkgmap files
+	 * for all packages
+	 */
+	(void) tmpnam(tmp_file);	/* temporary file name */
+	if ((list_fd = open(tmp_file, O_RDWR | O_CREAT)) == -1) {
+		progerr(pkg_gt(ERR_TRANSFER));
+		logerr(pkg_gt(MSG_NOTMPFIL));
+		return (1);
+	}
+
+	/*
+	 * Create a cpio-compatible list of the requisite files in
+	 * the temporary file.
+	 */
+	if (!making_sig) {
+		for (i = 0; pkg[i]; i++) {
+			register ssize_t entry_size;
+
+			/*
+			 * Copy pkginfo and pkgmap filenames into the
+			 * temporary string allowing for the first line
+			 * as a special case.
+			 */
+			entry_size = sprintf(tmp_entry,
+			    (i == 0) ? "%s/%s\n%s/%s" : "\n%s/%s\n%s/%s",
+			    pkg[i], PKGINFO, pkg[i], PKGMAP);
+
+			if (write(list_fd, tmp_entry,
+			    entry_size) != entry_size) {
+				progerr(pkg_gt(ERR_TRANSFER));
+				logerr(pkg_gt(MSG_NOTMPFIL));
+				(void) close(list_fd);
+				ecleanup();
+				return (1);
+			}
+		}
+
+	} else {
+		register ssize_t entry_size;
+
+		/*
+		 * if we're making a signature, we must make a
+		 * temporary area full of symlinks to the requisite
+		 * files, plus an extra entry for the signature, so
+		 * that cpio will put all files and signature in the
+		 * same archive in a single invocation of cpio.
+		 */
+		tmpsymdir = xstrdup(tmpnam(NULL));
+
+		if (mkdir(tmpsymdir,  S_IRWXU)) {
+			progerr(pkg_gt(ERR_TRANSFER));
+			logerr(pkg_gt(MSG_MKDIR), tmpsymdir);
+			return (1);
+		}
+
+		/* generate the signature */
+		if (((len = snprintf(path, PATH_MAX, "%s/%s",
+		    tmpsymdir, SIGNATURE_FILENAME)) >= PATH_MAX) ||
+		    len < 0) {
+			progerr(pkg_gt(ERR_TRANSFER));
+			logerr(pkg_gt(MSG_NOTMPFIL), tmpsymdir);
+			cleanup();
+			return (1);
+		}
+
+		if ((fp = fopen(path, "w")) == NULL) {
+			progerr(pkg_gt(ERR_TRANSFER));
+			logerr(pkg_gt(MSG_NOTMPFIL), path);
+			cleanup();
+			return (1);
+		}
+		PEM_write_PKCS7(fp, sig);
+		(void) fclose(fp);
+
+		for (i = 0; pkg[i]; i++) {
+			sprintf(path, "%s/%s", tmpsymdir, pkg[i]);
+			if (mkdir(path, 0755)) {
+				progerr(pkg_gt(ERR_TRANSFER));
+				logerr(pkg_gt(MSG_MKDIR), path);
+				cleanup();
+				return (1);
+			}
+			sprintf(path, "%s/%s/%s", tmpsymdir,
+			    pkg[i], PKGINFO);
+			sprintf(srcpath, "%s/%s/%s", src, pkg[i], PKGINFO);
+			if (symlink(srcpath, path) != 0) {
+				progerr(pkg_gt(ERR_TRANSFER));
+				logerr(pkg_gt(MSG_SYMLINK), path, srcpath);
+				cleanup();
+				return (1);
+			}
+
+			sprintf(path, "%s/%s/%s", tmpsymdir,
+			    pkg[i], PKGMAP);
+			sprintf(srcpath, "%s/%s/%s", src, pkg[i], PKGMAP);
+			if (symlink(srcpath, path) != 0) {
+				progerr(pkg_gt(ERR_TRANSFER));
+				logerr(pkg_gt(MSG_SYMLINK), path, srcpath);
+				cleanup();
+				return (1);
+			}
+
+			/*
+			 * Copy pkginfo and pkgmap filenames into the
+			 * temporary string allowing for the first line
+			 * as a special case.
+			 */
+			entry_size = sprintf(tmp_entry,
+			    (i == 0) ? "%s/%s\n%s/%s" : "\n%s/%s\n%s/%s",
+			    pkg[i], PKGINFO, pkg[i], PKGMAP);
+
+			if (write(list_fd, tmp_entry,
+			    entry_size) != entry_size) {
+				progerr(pkg_gt(ERR_TRANSFER));
+				logerr(pkg_gt(MSG_NOTMPFIL));
+				(void) close(list_fd);
+				ecleanup();
+				cleanup();
+				return (1);
+			}
+		}
+
+		/* add signature to list of files */
+		entry_size = sprintf(tmp_entry, "\n%s", SIGNATURE_FILENAME);
+		if (write(list_fd, tmp_entry, entry_size) != entry_size) {
+			progerr(pkg_gt(ERR_TRANSFER));
+			logerr(pkg_gt(MSG_NOTMPFIL), tmp_file);
+			(void) close(list_fd);
+			ecleanup();
+			cleanup();
+			return (1);
+		}
+	}
+
+	(void) lseek(list_fd, 0, SEEK_SET);
+
+	if (!making_sig) {
+#ifndef SUNOS41
+		(void) sprintf(tmp_entry, "%s -ocD -C %d",
+		    CPIOPROC, (int)BLK_SIZE);
+#else
+		(void) sprintf(tmp_entry, "%s -oc -C %d",
+		    CPIOPROC, (int)BLK_SIZE);
+#endif
+	} else {
+		/*
+		 * when making a signature, we must make sure to follow
+		 * symlinks during the cpio so that we don't archive
+		 * the links themselves
+		 */
+#ifndef SUNOS41
+		(void) sprintf(tmp_entry, "%s -ocDL -C %d",
+		    CPIOPROC, (int)BLK_SIZE);
+#else
+		(void) sprintf(tmp_entry, "%s -ocL -C %d",
+		    CPIOPROC, (int)BLK_SIZE);
+#endif
+	}
+
+	if (making_sig) {
+		/* save cwd and change to symlink dir for cpio invocation */
+		if (getcwd(cwd, MAXPATHLEN + 1) == NULL) {
+			logerr(pkg_gt(ERR_GETWD));
+			progerr(pkg_gt(ERR_TRANSFER));
+			cleanup();
+			return (1);
+		}
+
+		if (chdir(tmpsymdir)) {
+			progerr(pkg_gt(ERR_TRANSFER));
+			logerr(pkg_gt(MSG_CHDIR), tmpsymdir);
+			cleanup();
+			return (1);
+		}
+	}
+
+	if (n = esystem(tmp_entry, list_fd, ds_fd)) {
+		rpterr();
+		progerr(pkg_gt(ERR_TRANSFER));
+		logerr(pkg_gt(MSG_CMDFAIL), tmp_entry, n);
+		(void) close(list_fd);
+		(void) unlink(tmp_file);
+		cleanup();
+		return (1);
+	}
+
+	(void) close(list_fd);
+	(void) unlink(tmp_file);
+
+	if (making_sig) {
+		/* change to back to src dir for subsequent operations */
+		if (chdir(cwd)) {
+			progerr(pkg_gt(ERR_TRANSFER));
+			logerr(pkg_gt(MSG_CHDIR), cwd);
+			cleanup();
+			return (1);
+		}
+	}
+	return (0);
+}
+
+static int
+ckoverwrite(char *dir, char *inst, int options)
+{
+	char	path[PATH_MAX];
+
+	(void) sprintf(path, "%s/%s", dir, inst);
+	if (access(path, 0) == 0) {
+		if (options & PT_OVERWRITE)
+			return (rrmdir(path));
+		progerr(pkg_gt(ERR_TRANSFER));
+		logerr(pkg_gt(MSG_EXISTS), path);
+		return (1);
+	}
+	return (0);
+}
+
+static int
+pkgxfer(char *srcinst, int options)
+{
+	int	r;
+	struct pkginfo info;
+	FILE	*fp, *pp;
+	char	*pt, *src, *dst;
+	char	dstdir[PATH_MAX],
+		temp[PATH_MAX],
+		srcdir[PATH_MAX],
+		cmd[CMDSIZE],
+		pkgname[NON_ABI_NAMELNGTH];
+	int	i, n, part, nparts, maxpartsize, curpartcnt, iscomp;
+	char	volnos[128], tmpvol[128];
+	struct	statvfs64 svfsb;
+	longlong_t free_blocks;
+	struct	stat	srcstat;
+
+	info.pkginst = NULL; /* required initialization */
+
+	/*
+	 * when this routine is entered, the first part of
+	 * the package to transfer is already available in
+	 * the directory indicated by 'src' --- unless the
+	 * source device is a datstream, in which case only
+	 * the pkginfo and pkgmap files are available in 'src'
+	 */
+	src = srcdev.dirname;
+	dst = dstdev.dirname;
+
+	if (!(options & PT_SILENT))
+		(void) fprintf(stderr, pkg_gt(MSG_TRANSFER), srcinst);
+	(void) strcpy(dstinst, srcinst);
+
+	if (!(options & PT_ODTSTREAM)) {
+		/* destination is a (possibly mounted) directory */
+		(void) sprintf(dstdir, "%s/%s", dst, dstinst);
+
+		/*
+		 * need to check destination directory to assure
+		 * that we will not be duplicating a package which
+		 * already resides there (though we are allowed to
+		 * overwrite the same version)
+		 */
+		pkgdir = src;
+		if (fpkginfo(&info, srcinst)) {
+			progerr(pkg_gt(ERR_TRANSFER));
+			logerr(pkg_gt(MSG_NOEXISTS), srcinst);
+			(void) fpkginfo(&info, NULL);
+			return (1);
+		}
+		pkgdir = dst;
+
+		(void) strcpy(temp, srcinst);
+		if (pt = strchr(temp, '.'))
+			*pt = '\0';
+		(void) strcat(temp, ".*");
+
+		if (pt = fpkginst(temp, info.arch, info.version)) {
+			/*
+			 * the same instance already exists, although
+			 * its pkgid might be different
+			 */
+			if (options & PT_OVERWRITE) {
+				(void) strcpy(dstinst, pt);
+				(void) sprintf(dstdir, "%s/%s", dst, dstinst);
+			} else {
+				progerr(pkg_gt(ERR_TRANSFER));
+				logerr(pkg_gt(MSG_DUPVERS), srcinst);
+				(void) fpkginfo(&info, NULL);
+				(void) fpkginst(NULL);
+				return (2);
+			}
+		} else if (options & PT_RENAME) {
+			/*
+			 * find next available instance by appending numbers
+			 * to the package abbreviation until the instance
+			 * does not exist in the destination directory
+			 */
+			if (pt = strchr(temp, '.'))
+				*pt = '\0';
+			for (i = 2; (access(dstdir, 0) == 0); i++) {
+				(void) sprintf(dstinst, "%s.%d", temp, i);
+				(void) sprintf(dstdir, "%s/%s", dst, dstinst);
+			}
+		} else if (options & PT_OVERWRITE) {
+			/*
+			 * we're allowed to overwrite, but there seems
+			 * to be no valid package to overwrite, and we are
+			 * not allowed to rename the destination, so act
+			 * as if we weren't given permission to overwrite
+			 * --- this keeps us from removing a destination
+			 * instance which is named the same as the source
+			 * instance, but really reflects a different pkg!
+			 */
+			options &= (~PT_OVERWRITE);
+		}
+		(void) fpkginfo(&info, NULL);
+		(void) fpkginst(NULL);
+
+		if (ckoverwrite(dst, dstinst, options))
+			return (2);
+
+		if (isdir(dstdir) && mkdir(dstdir, 0755)) {
+			progerr(pkg_gt(ERR_TRANSFER));
+			logerr(pkg_gt(MSG_MKDIR), dstdir);
+			return (1);
+		}
+
+		(void) sprintf(srcdir, "%s/%s", src, srcinst);
+		if (stat(srcdir, &srcstat) != -1) {
+			if (chmod(dstdir, (srcstat.st_mode & S_IAMB)) == -1) {
+				progerr(pkg_gt(ERR_TRANSFER));
+				logerr(pkg_gt(MSG_CHMODDIR), dstdir);
+				return (1);
+			}
+		} else {
+			progerr(pkg_gt(ERR_TRANSFER));
+			logerr(pkg_gt(MSG_STATDIR), srcdir);
+			return (1);
+		}
+	}
+
+	if (!(options & PT_SILENT) && strcmp(dstinst, srcinst))
+		(void) fprintf(stderr, pkg_gt(MSG_RENAME), dstinst);
+
+	(void) sprintf(srcdir, "%s/%s", src, srcinst);
+	if (chdir(srcdir)) {
+		progerr(pkg_gt(ERR_TRANSFER));
+		logerr(pkg_gt(MSG_CHDIR), srcdir);
+		return (1);
+	}
+
+	if (ids_name) {	/* unpack the datatstream into a directory */
+		/*
+		 * transfer pkginfo & pkgmap first
+		 */
+		(void) sprintf(cmd, "%s -pudm %s", CPIOPROC, dstdir);
+		if ((pp = epopen(cmd, "w")) == NULL) {
+			rpterr();
+			progerr(pkg_gt(ERR_TRANSFER));
+			logerr(pkg_gt(MSG_POPEN), cmd, errno);
+			return (1);
+		}
+		(void) fprintf(pp, "%s\n%s\n", PKGINFO, PKGMAP);
+
+		sighold(SIGINT);
+		sighold(SIGHUP);
+		r = epclose(pp);
+		sigrelse(SIGINT);
+		sigrelse(SIGHUP);
+
+		if (r != 0) {
+			rpterr();
+			progerr(pkg_gt(ERR_TRANSFER));
+			logerr(pkg_gt(MSG_PCLOSE), cmd, errno);
+			return (1);
+		}
+
+		if (options & PT_INFO_ONLY)
+			return (0); /* don't transfer objects */
+
+		if (chdir(dstdir)) {
+			progerr(pkg_gt(ERR_TRANSFER));
+			logerr(pkg_gt(MSG_CHDIR), dstdir);
+			return (1);
+		}
+
+		/*
+		 * for each part of the package, use cpio() to
+		 * unpack the archive into the destination directory
+		 */
+		nparts = ds_findpkg(srcdev.cdevice, srcinst);
+		if (nparts < 0) {
+			progerr(pkg_gt(ERR_TRANSFER));
+			return (1);
+		}
+		for (part = 1; part <= nparts; /* void */) {
+			if (ds_getpkg(srcdev.cdevice, part, dstdir)) {
+				progerr(pkg_gt(ERR_TRANSFER));
+				return (1);
+			}
+			part++;
+			if (dstdev.mount) {
+				(void) chdir("/");
+				if (pkgumount(&dstdev))
+					return (1);
+				if (part <= nparts) {
+					if (n = pkgmount(&dstdev, NULL, part+1,
+					    nparts, 1))
+						return (n);
+					if (ckoverwrite(dst, dstinst, options))
+						return (1);
+					if (isdir(dstdir) &&
+					    mkdir(dstdir, 0755)) {
+						progerr(
+						    pkg_gt(ERR_TRANSFER));
+						logerr(pkg_gt(MSG_MKDIR),
+						    dstdir);
+						return (1);
+					}
+					/*
+					 * since volume is removable, each part
+					 * must contain a duplicate of the
+					 * pkginfo file to properly identify the
+					 * volume
+					 */
+					if (chdir(srcdir)) {
+						progerr(
+						    pkg_gt(ERR_TRANSFER));
+						logerr(pkg_gt(MSG_CHDIR),
+						    srcdir);
+						return (1);
+					}
+					if ((pp = epopen(cmd, "w")) == NULL) {
+						rpterr();
+						progerr(
+						    pkg_gt(ERR_TRANSFER));
+						logerr(pkg_gt(MSG_POPEN),
+						    cmd, errno);
+						return (1);
+					}
+					(void) fprintf(pp, "pkginfo");
+
+					sighold(SIGINT);
+					sighold(SIGHUP);
+					r = epclose(pp);
+					sigrelse(SIGINT);
+					sigrelse(SIGHUP);
+
+					if (r != 0) {
+						rpterr();
+						progerr(
+						    pkg_gt(ERR_TRANSFER));
+						logerr(pkg_gt(MSG_PCLOSE),
+						    cmd, errno);
+						return (1);
+					}
+					if (chdir(dstdir)) {
+						progerr(
+						    pkg_gt(ERR_TRANSFER));
+						logerr(pkg_gt(MSG_CHDIR),
+						    dstdir);
+						return (1);
+					}
+				}
+			}
+		}
+		return (0);
+	}
+
+	if ((fp = fopen(PKGMAP, "r")) == NULL) {
+		progerr(pkg_gt(ERR_TRANSFER));
+		logerr(pkg_gt(MSG_NOPKGMAP), srcinst);
+		return (1);
+	}
+
+	nparts = 1;
+	if (!rd_map_size(fp, &nparts, &maxpartsize, &compressedsize))
+		return (1);
+	else
+		(void) fclose(fp);
+
+	if (srcdev.mount) {
+		if (ckvolseq(srcdir, 1, nparts)) {
+			progerr(pkg_gt(ERR_TRANSFER));
+			logerr(pkg_gt(MSG_SEQUENCE));
+			return (1);
+		}
+	}
+
+	/* write each part of this package */
+	if (options & PT_ODTSTREAM) {
+		char line[128];
+		(void) mgets(line, 128);
+		curpartcnt = -1;
+		if (sscanf(line, "%s %d %d %[ 0-9]", &pkgname, &nparts,
+		    &maxpartsize, volnos) == 4) {
+			sscanf(volnos, "%d %[ 0-9]", &curpartcnt, tmpvol);
+			strcpy(volnos, tmpvol);
+		}
+	}
+
+	for (part = 1; part <= nparts; /* void */) {
+		if (curpartcnt == 0 && (options & PT_ODTSTREAM)) {
+			char prompt[128];
+			int index;
+			ds_volno++;
+			(void) ds_close(0);
+			(void) sprintf(prompt,
+			    pkg_gt("Insert %%v %d of %d into %%p"),
+			    ds_volno, ds_volcnt);
+			if (n = getvol(ods_name, NULL, DM_FORMAT, prompt))
+				return (n);
+			if ((ds_fd = open(dstdev.cdevice, O_WRONLY)) < 0) {
+				progerr(pkg_gt(ERR_TRANSFER));
+				logerr(pkg_gt(MSG_OPEN), dstdev.cdevice,
+				    errno);
+				return (1);
+			}
+			if (ds_ginit(dstdev.cdevice) < 0) {
+				progerr(pkg_gt(ERR_TRANSFER));
+				logerr(pkg_gt(MSG_OPEN), dstdev.cdevice,
+				    errno);
+				(void) ds_close(0);
+				return (1);
+			}
+
+			(void) sscanf(volnos, "%d %[ 0-9]", &index, tmpvol);
+			(void) strcpy(volnos, tmpvol);
+			curpartcnt += index;
+		}
+
+		if (options & PT_INFO_ONLY)
+			nparts = 0;
+
+		if (part == 1) {
+			(void) sprintf(cmd, "find %s %s", PKGINFO, PKGMAP);
+			if (nparts && (isdir(INSTALL) == 0)) {
+				(void) strcat(cmd, " ");
+				(void) strcat(cmd, INSTALL);
+			}
+		} else
+			(void) sprintf(cmd, "find %s", PKGINFO);
+
+		if (nparts > 1) {
+			(void) sprintf(temp, "%s.%d", RELOC, part);
+			if (iscpio(temp, &iscomp) || isdir(temp) == 0) {
+				(void) strcat(cmd, " ");
+				(void) strcat(cmd, temp);
+			}
+			(void) sprintf(temp, "%s.%d", ROOT, part);
+			if (iscpio(temp, &iscomp) || isdir(temp) == 0) {
+				(void) strcat(cmd, " ");
+				(void) strcat(cmd, temp);
+			}
+			(void) sprintf(temp, "%s.%d", ARCHIVE, part);
+			if (isdir(temp) == 0) {
+				(void) strcat(cmd, " ");
+				(void) strcat(cmd, temp);
+			}
+		} else if (nparts) {
+			for (i = 0; reloc_names[i] != NULL; i++) {
+				if (iscpio(reloc_names[i], &iscomp) ||
+				    isdir(reloc_names[i]) == 0) {
+					(void) strcat(cmd, " ");
+					(void) strcat(cmd, reloc_names[i]);
+				}
+			}
+			for (i = 0; root_names[i] != NULL; i++) {
+				if (iscpio(root_names[i], &iscomp) ||
+				    isdir(root_names[i]) == 0) {
+					(void) strcat(cmd, " ");
+					(void) strcat(cmd, root_names[i]);
+				}
+			}
+			if (isdir(ARCHIVE) == 0) {
+				(void) strcat(cmd, " ");
+				(void) strcat(cmd, ARCHIVE);
+			}
+		}
+		if (options & PT_ODTSTREAM) {
+#ifndef SUNOS41
+			(void) sprintf(cmd+strlen(cmd),
+			    " -print | %s -ocD -C %d",
+#else
+			(void) sprintf(cmd+strlen(cmd),
+			    " -print | %s -oc -C %d",
+#endif
+				CPIOPROC, (int)BLK_SIZE);
+		} else {
+			if (statvfs64(dstdir, &svfsb) == -1) {
+				progerr(pkg_gt(ERR_TRANSFER));
+				logerr(pkg_gt(MSG_STATVFS), dstdir, errno);
+				return (1);
+			}
+
+			free_blocks = (((long)svfsb.f_frsize > 0) ?
+			    howmany(svfsb.f_frsize, DEV_BSIZE) :
+			    howmany(svfsb.f_bsize, DEV_BSIZE)) * svfsb.f_bavail;
+
+			if ((has_comp_size ? compressedsize : maxpartsize) >
+			    free_blocks) {
+				progerr(pkg_gt(ERR_TRANSFER));
+				logerr(pkg_gt(MSG_NOSPACE));
+				return (1);
+			}
+			(void) sprintf(cmd+strlen(cmd), " -print | %s -pdum %s",
+				CPIOPROC, dstdir);
+		}
+
+		n = esystem(cmd, -1, (options & PT_ODTSTREAM) ? ds_fd : -1);
+		if (n) {
+			rpterr();
+			progerr(pkg_gt(ERR_TRANSFER));
+			logerr(pkg_gt(MSG_CMDFAIL), cmd, n);
+			return (1);
+		}
+
+		part++;
+		if (srcdev.mount && (nparts > 1)) {
+			/* unmount current source volume */
+			(void) chdir("/");
+			if (pkgumount(&srcdev))
+				return (1);
+			/* loop until volume is mounted successfully */
+			while (part <= nparts) {
+				/* read only */
+				n = pkgmount(&srcdev, NULL, part, nparts, 1);
+				if (n)
+					return (n);
+				if (chdir(srcdir)) {
+					progerr(pkg_gt(ERR_TRANSFER));
+					logerr(pkg_gt(MSG_CORRUPT), srcdir);
+					(void) chdir("/");
+					pkgumount(&srcdev);
+					continue;
+				}
+				if (ckvolseq(srcdir, part, nparts)) {
+					(void) chdir("/");
+					pkgumount(&srcdev);
+					continue;
+				}
+				break;
+			}
+		}
+		if (!(options & PT_ODTSTREAM) && dstdev.mount) {
+			/* unmount current volume */
+			if (pkgumount(&dstdev))
+				return (1);
+			/* loop until next volume is mounted successfully */
+			while (part <= nparts) {
+				/* writable */
+				n = pkgmount(&dstdev, NULL, part, nparts, 1);
+				if (n)
+					return (n);
+				if (ckoverwrite(dst, dstinst, options))
+					continue;
+				if (isdir(dstdir) && mkdir(dstdir, 0755)) {
+					progerr(pkg_gt(ERR_TRANSFER));
+					logerr(pkg_gt(MSG_MKDIR), dstdir);
+					continue;
+				}
+				break;
+			}
+		}
+
+		if ((options & PT_ODTSTREAM) && part <= nparts) {
+			if (curpartcnt >= 0 && part > curpartcnt) {
+				char prompt[128];
+				int index;
+				ds_volno++;
+				if (ds_close(0))
+					return (1);
+				(void) sprintf(prompt,
+				    pkg_gt("Insert %%v %d of %d into %%p"),
+				    ds_volno, ds_volcnt);
+				if (n = getvol(ods_name, NULL, DM_FORMAT,
+				    prompt))
+					return (n);
+				if ((ds_fd = open(dstdev.cdevice, 1)) < 0) {
+					progerr(pkg_gt(ERR_TRANSFER));
+					logerr(pkg_gt(MSG_OPEN),
+					    dstdev.cdevice, errno);
+					return (1);
+				}
+				if (ds_ginit(dstdev.cdevice) < 0) {
+					progerr(pkg_gt(ERR_TRANSFER));
+					logerr(pkg_gt(MSG_OPEN),
+					    dstdev.cdevice, errno);
+					(void) ds_close(0);
+					return (1);
+				}
+
+				(void) sscanf(volnos, "%d %[ 0-9]", &index,
+				    tmpvol);
+				(void) strcpy(volnos, tmpvol);
+				curpartcnt += index;
+			}
+		}
+
+	}
+	return (0);
+}
+
+/*
+ * Name:		pkgdump
+ * Description:	Dump a cpio archive of a package's contents to a BIO.
+ *
+ * Arguments:	srcinst - Name of package, which resides on the
+ *		device pointed to by the static 'srcdev' variable,
+ *		to dump.
+ *		bio - BIO object to dump data to
+ *
+ * Returns :   	0 - success
+ *		nonzero - failure.  errors printed to screen.
+ */
+static int
+pkgdump(char *srcinst, BIO *bio)
+{
+	FILE	*fp;
+	char	*src;
+	char	temp[MAXPATHLEN],
+		srcdir[MAXPATHLEN],
+		cmd[CMDSIZE];
+	int	i, n, part, nparts, maxpartsize, iscomp;
+
+	/*
+	 * when this routine is entered, the entire package
+	 * is already available at 'src' - including the
+	 * pkginfo/pkgmap files and the objects as well.
+	 */
+
+	/* read the pkgmap to get it's size information */
+	if ((fp = fopen(PKGMAP, "r")) == NULL) {
+		progerr(pkg_gt(ERR_TRANSFER));
+		logerr(pkg_gt(MSG_NOPKGMAP), srcinst);
+		return (1);
+	}
+
+	nparts = 1;
+	if (!rd_map_size(fp, &nparts, &maxpartsize, &compressedsize))
+		return (1);
+	else
+		(void) fclose(fp);
+
+	/* make sure the first volume is available */
+	if (srcdev.mount) {
+		src = srcdev.dirname;
+		(void) snprintf(srcdir, MAXPATHLEN, "%s/%s", src, srcinst);
+		if (ckvolseq(srcdir, 1, nparts)) {
+			progerr(pkg_gt(ERR_TRANSFER));
+			logerr(pkg_gt(MSG_SEQUENCE));
+			return (1);
+		}
+	}
+
+	/*
+	 * form cpio command that will output the contents of all of
+	 * this package's parts
+	 */
+	for (part = 1; part <= nparts; /* void */) {
+
+		if (part == 1) {
+			(void) snprintf(cmd, CMDSIZE, "find %s %s",
+			    PKGINFO, PKGMAP);
+			if (nparts && (isdir(INSTALL) == 0)) {
+				(void) strcat(cmd, " ");
+				(void) strcat(cmd, INSTALL);
+			}
+		} else
+			(void) snprintf(cmd, CMDSIZE, "find %s", PKGINFO);
+
+		if (nparts > 1) {
+			(void) snprintf(temp, MAXPATHLEN, "%s.%d", RELOC, part);
+			if (iscpio(temp, &iscomp) || isdir(temp) == 0) {
+				(void) strlcat(cmd, " ", CMDSIZE);
+				(void) strlcat(cmd, temp, CMDSIZE);
+			}
+			(void) snprintf(temp, MAXPATHLEN, "%s.%d", ROOT, part);
+			if (iscpio(temp, &iscomp) || isdir(temp) == 0) {
+				(void) strlcat(cmd, " ", CMDSIZE);
+				(void) strlcat(cmd, temp, CMDSIZE);
+			}
+			(void) snprintf(temp, MAXPATHLEN, "%s.%d",
+			    ARCHIVE, part);
+			if (isdir(temp) == 0) {
+				(void) strlcat(cmd, " ", CMDSIZE);
+				(void) strlcat(cmd, temp, CMDSIZE);
+			}
+		} else if (nparts) {
+			for (i = 0; reloc_names[i] != NULL; i++) {
+				if (iscpio(reloc_names[i], &iscomp) ||
+				    isdir(reloc_names[i]) == 0) {
+					(void) strlcat(cmd, " ", CMDSIZE);
+					(void) strlcat(cmd, reloc_names[i],
+					    CMDSIZE);
+				}
+			}
+			for (i = 0; root_names[i] != NULL; i++) {
+				if (iscpio(root_names[i], &iscomp) ||
+				    isdir(root_names[i]) == 0) {
+					(void) strlcat(cmd, " ", CMDSIZE);
+					(void) strlcat(cmd, root_names[i],
+					    CMDSIZE);
+				}
+			}
+			if (isdir(ARCHIVE) == 0) {
+				(void) strlcat(cmd, " ", CMDSIZE);
+				(void) strlcat(cmd, ARCHIVE, CMDSIZE);
+			}
+		}
+
+#ifndef SUNOS41
+		(void) sprintf(cmd+strlen(cmd),
+		    " -print | %s -ocD -C %d",
+#else
+		    (void) sprintf(cmd+strlen(cmd),
+			" -print | %s -oc -C %d",
+#endif
+			CPIOPROC, (int)BLK_SIZE);
+		/*
+		 * execute the command, dumping all standard output
+		 * to the BIO.
+		 */
+		n = BIO_dump_cmd(cmd, bio);
+		if (n != 0) {
+			rpterr();
+			progerr(pkg_gt(ERR_TRANSFER));
+			logerr(pkg_gt(MSG_CMDFAIL), cmd, n);
+			return (1);
+		}
+
+		part++;
+	}
+	return (0);
+}
+
+static void
+sigtrap(int signo)
+{
+	signal_received++;
+}
+
+static void
+cleanup(void)
+{
+	chdir("/");
+	if (tmpdir) {
+		rrmdir(tmpdir);
+		free(tmpdir);
+		tmpdir = NULL;
+	}
+
+	if (tmppath) {
+		/* remove any previous tmppath stuff */
+		rrmdir(tmppath);
+		free(tmppath);
+		tmppath = NULL;
+	}
+
+	if (tmpsymdir) {
+		/* remove temp symbolic links made for signed pkg */
+		rrmdir(tmpsymdir);
+		free(tmpsymdir);
+		tmpsymdir = NULL;
+	}
+
+	if (srcdev.mount && !ids_name)
+		pkgumount(&srcdev);
+	if (dstdev.mount && !ods_name)
+		pkgumount(&dstdev);
+	(void) ds_close(1);
+}
+
+/*
+ * Name:		dump_hdr_and_pkgs
+ * Description:	Dumps datastream header and each package's contents
+ *		to the supplied BIO
+ *
+ * Arguments:	bio - BIO object to dump data to
+ *		hdr - Header for the datastream being dumped
+ *		pkglist - NULL-terminated list of packages
+ *		to dump.  The location of the packages are stored
+ *		in the static 'srcdev' variable.
+ *
+ * Returns :   	0 - success
+ *		nonzero - failure.  errors printed to screen.
+ */
+static int
+dump_hdr_and_pkgs(BIO *bio, struct dm_buf *hdr, char **pkglist)
+{
+	int	block_cnt, i;
+	char	srcdir[MAXPATHLEN];
+	char	cwd[MAXPATHLEN + 1];
+	char	*src;
+
+	/* write out the header to the signature stream */
+	for (block_cnt = 0; block_cnt < hdr->allocation;
+		block_cnt += BLK_SIZE) {
+		BIO_write(bio, (hdr->text_buffer + block_cnt), BLK_SIZE);
+	}
+
+	/* save current directory */
+	if (getcwd(cwd, MAXPATHLEN + 1) == NULL) {
+		logerr(pkg_gt(ERR_GETWD));
+		progerr(pkg_gt(ERR_TRANSFER));
+		return (1);
+	}
+
+	/* now write out each package's contents */
+	for (i = 0; pkglist[i]; i++) {
+		/*
+		 * change to the source dir, so we can find and dump
+		 * the package(s) bits into the BIO
+		 *
+		 */
+		src = srcdev.dirname;
+
+		/* change to the package source directory */
+		(void) snprintf(srcdir, MAXPATHLEN, "%s/%s", src, pkglist[i]);
+		if (chdir(srcdir)) {
+			progerr(pkg_gt(ERR_TRANSFER));
+			logerr(pkg_gt(MSG_CHDIR), srcdir);
+			return (1);
+		}
+
+		if (pkgdump(pkglist[i], bio)) {
+			pkglist[i] = NULL;
+			return (1);
+		}
+	}
+
+	/* change back to directory we were in upon entering this routine */
+	if (chdir(cwd)) {
+		progerr(pkg_gt(ERR_TRANSFER));
+		logerr(pkg_gt(MSG_CHDIR), cwd);
+		return (1);
+	}
+
+	return (0);
+}
+
+/*
+ * Name:		BIO_dump_cmd
+ * Description:	Dump the output of invoking a command
+ *		to a BIO.
+ *
+ * Arguments:	cmd - Command to invoke
+ *		bio - BIO to dump output of command to
+ *		only 'stdout' is dumped.
+ * Returns :   	0 - success
+ *		nonzero - failure.  errors printed to screen.
+ */
+int
+BIO_dump_cmd(char *cmd, BIO *bio)
+{
+	char	buf[BLK_SIZE];
+	FILE	*fp;
+	int	rc;
+
+	/* start up the process */
+	if ((fp = epopen(cmd, "r")) == NULL) {
+		rpterr();
+		return (1);
+	}
+
+	/* read output in chunks, transfer to BIO */
+	while (fread(buf, BLK_SIZE, 1, fp) == 1) {
+		if (BIO_write(bio, buf, BLK_SIZE) != BLK_SIZE) {
+			sighold(SIGINT);
+			sighold(SIGHUP);
+			(void) epclose(fp);
+			sigrelse(SIGINT);
+			sigrelse(SIGHUP);
+			rpterr();
+			return (1);
+		}
+	}
+
+	/* done with stream, make sure no errors were encountered */
+	if (ferror(fp)) {
+		(void) epclose(fp);
+		rpterr();
+		return (1);
+	}
+
+	/* done, close stream, report any errors */
+	sighold(SIGINT);
+	sighold(SIGHUP);
+	rc = epclose(fp);
+	sigrelse(SIGINT);
+	sigrelse(SIGHUP);
+	if (rc != 0) {
+		rpterr();
+		return (1);
+	}
+
+	return (rc);
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/usr/src/lib/libpkg/common/pkgweb.c	Fri Jun 05 10:28:40 2009 -0400
@@ -0,0 +1,3238 @@
+/*
+ * 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
+ * or http://www.opensolaris.org/os/licensing.
+ * 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 2009 Sun Microsystems, Inc. All rights reserved.
+ * Use is subject to license terms.
+ */
+
+/* Copyright (c) 1984, 1986, 1987, 1988, 1989 AT&T */
+/* All Rights Reserved */
+
+
+#include <stdio.h>
+#include <limits.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <string.h>
+#include <pkglocs.h>
+#include <locale.h>
+#include <libintl.h>
+#include <libgen.h>
+#include <signal.h>
+#include <sys/stat.h>
+#include <sys/statvfs.h>
+#include <sys/types.h>
+#include <fcntl.h>
+#include <dirent.h>
+#include <boot_http.h>
+#include <errno.h>
+#include <ctype.h>
+#include <openssl/pkcs7.h>
+#include <openssl/ocsp.h>
+#include <openssl/pkcs12.h>
+#include <openssl/err.h>
+#include <openssl/x509.h>
+#include <openssl/pem.h>
+#include <openssl/evp.h>
+#include <openssl/rand.h>
+#include <openssl/x509v3.h>
+#include "pkglib.h"
+#include "pkglibmsgs.h"
+#include "pkglocale.h"
+#include "keystore.h"
+#include "pkgweb.h"
+#include "pkgerr.h"
+#include "p12lib.h"
+
+/* fixed format when making an OCSP request */
+#define	OCSP_REQUEST_FORMAT \
+	"POST %s HTTP/1.0\r\n" \
+	"Content-Type: application/ocsp-request\r\n" \
+	"Content-Length: %d\r\n\r\n"
+
+/*
+ * no security is afforded by using this phrase to "encrypt" CA certificates,
+ * but it might aid in debugging and has to be non-null
+ */
+#define	WEB_CA_PHRASE		"schizophrenic"
+
+/* This one needs the ': ' at the end */
+#define	CONTENT_TYPE_HDR	"Content-Type"
+#define	CONTENT_DISPOSITION_HDR	"Content-Disposition"
+#define	CONTENT_OCSP_RESP	"application/ocsp-response"
+#define	CONTENT_LENGTH_HDR	"Content-Length"
+#define	LAST_MODIFIED_HDR	"Last-Modified"
+#define	OCSP_BUFSIZ	1024
+
+/*
+ * default amount of time that is allowed for error when checking
+ * OCSP response validity.
+ * For example, if this is set to 5 minutes, then if a response
+ * is issued that is valid from 12:00 to 1:00, then we will
+ * accept it if the local time is between 11:55 and 1:05.
+ * This takes care of not-quite-synchronized server and client clocks.
+ */
+#define	OCSP_VALIDITY_PERIOD	(5 * 60)
+
+/* this value is defined by getpassphrase(3c) manpage */
+#define	MAX_PHRASELEN		257
+
+/* Max length of "enter password again" prompt message */
+#define	MAX_VERIFY_MSGLEN	1024
+
+/* local prototypes */
+static boolean_t remove_dwnld_file(char *);
+static boolean_t get_ENV_proxyport(PKG_ERR *, ushort_t *);
+static boolean_t make_link(char *, char *);
+static WebStatus web_send_request(PKG_ERR *, int, int, int);
+static boolean_t web_eval_headers(PKG_ERR *);
+static WebStatus web_get_file(PKG_ERR *, char *, int, char **);
+static boolean_t ck_dwnld_dir_space(PKG_ERR *, char *, ulong_t);
+static WebStatus web_connect(PKG_ERR *);
+static boolean_t web_setup(PKG_ERR *);
+static boolean_t check_dwnld_dir(PKG_ERR *, char *);
+static boolean_t parse_url_proxy(PKG_ERR *, char *, char *, ushort_t);
+static boolean_t web_disconnect(void);
+static char *get_unique_filename(char *, char *);
+static boolean_t get_ENV_proxy(PKG_ERR *, char **);
+static char *condense_lastmodified(char *);
+static int web_verify(int, X509_STORE_CTX *);
+static int get_issuer(X509 **issuer, X509_STORE_CTX *ctx, X509 *x);
+static boolean_t get_ocsp_uri(X509 *, char **);
+static OCSPStatus ocsp_verify(PKG_ERR *, X509 *, X509 *, char *, url_hport_t *,
+    STACK_OF(X509) *);
+static char	*get_time_string(ASN1_GENERALIZEDTIME *);
+static char	*write_ca_file(PKG_ERR *, char *, STACK_OF(X509) *, char *);
+static boolean_t _get_random_info(void *, int);
+static boolean_t	init_session(void);
+static void	progress_setup(int, ulong_t);
+static void	progress_report(int, ulong_t);
+static void	progress_finish(int);
+static char	*replace_token(char *, char, char);
+static void	dequote(char *);
+static void	trim(char *);
+
+
+/*
+ * structure used to hold data passed back to the
+ * X509 verify callback routine in validate_signature()
+ */
+typedef struct {
+	url_hport_t	*proxy;
+	PKG_ERR		*err;
+	STACK_OF(X509)	*cas;
+} verify_cb_data_t;
+
+/* Progress bar variables */
+static ulong_t const_increment, const_divider, completed, const_completed;
+
+/* current network backoff wait period */
+static int cur_backoff = 0;
+
+/* download session context handle */
+static WEB_SESSION *ps;
+
+static int	webpkg_install = 0;
+static char	*prompt = NULL;
+static char	*passarg = NULL;
+
+
+/* ~~~~~~~~~~~~~~ Public Functions ~~~~~~~~~~~~~~~~~~~ */
+
+/*
+ * Name:		set_prompt
+ * Description:	Specifies the prompt to use with the pkglib
+ *		passphrase callback routine.
+ *
+ * Arguments:	newprompt - The prompt to display
+ *
+ * Returns :	NONE
+ */
+void
+set_passphrase_prompt(char *newprompt)
+{
+	prompt = newprompt;
+}
+
+/*
+ * Name:		set_passarg
+ * Description:	Specifies the passphrase retrieval method
+ *		 to use with the pkglib
+ *		passphrase callback routine.
+ *
+ * Arguments:	newpassarg - The new password retrieval arg
+ *
+ * Returns :	NONE
+ */
+void
+set_passphrase_passarg(char *newpassarg)
+{
+	passarg = newpassarg;
+}
+
+/*
+ * Name:		get_proxy_port
+ * Description:	Resolves proxy specification
+ *
+ * Arguments:	err - where to record any errors.
+ *     		proxy - Location to store result - if *proxy is not
+ *		null, then it will be validated, but not changed
+ *
+ * Returns :	B_TRUE - success, B_FALSE otherwise
+ *		on success, *proxy and *port are set to either
+ *		the user-supplied proxy and port, or the
+ *		ones found in the environment variables
+ *		HTTPPROXY and/or HTTPROXYPORT
+ */
+boolean_t
+get_proxy_port(PKG_ERR *err, char **proxy, ushort_t *port)
+{
+	if (*proxy != NULL) {
+		if (!path_valid(*proxy)) {
+			/* bad proxy supplied */
+			pkgerr_add(err, PKGERR_WEB,
+			    gettext(ERR_BAD_PROXY), *proxy);
+			return (B_FALSE);
+		}
+		if (!get_ENV_proxyport(err, port)) {
+			/* env set, but bad */
+			return (B_FALSE);
+		}
+	} else {
+		if (!get_ENV_proxy(err, proxy)) {
+			/* environment variable set, but bad */
+			return (B_FALSE);
+		}
+		if ((*proxy != NULL) && !path_valid(*proxy)) {
+			/* env variable set, but bad */
+			pkgerr_add(err, PKGERR_WEB,
+			    gettext(ERR_BAD_PROXY), *proxy);
+			return (B_FALSE);
+		}
+		if (!get_ENV_proxyport(err, port)) {
+			/* env variable set, but bad */
+			return (B_FALSE);
+		}
+	}
+	return (B_TRUE);
+}
+
+/*
+ * Name:		path_valid
+ * Description:	Checks a string for being a valid path
+ *
+ * Arguments:	path - path to validate
+ *
+ * Returns :	B_TRUE - success, B_FALSE otherwise.
+ *		B_FALSE means path was null, too long (>PATH_MAX),
+ *		or too short (<1)
+ */
+boolean_t
+path_valid(char *path)
+{
+	if (path == NULL) {
+		return (B_FALSE);
+	} else if (strlen(path) > PATH_MAX) {
+		return (B_FALSE);
+	} else if (strlen(path) >= 1) {
+		return (B_TRUE);
+	} else {
+		/* path < 1 */
+		return (B_FALSE);
+	}
+}
+
+/*
+ * Name:		web_cleanup
+ * Description:	Deletes temp files, closes, frees memory taken
+ *		by 'ps' static structure
+ *
+ * Arguments:	none
+ *
+ * Returns :	none
+ */
+void
+web_cleanup(void)
+{
+	PKG_ERR *err;
+
+	if (ps == NULL)
+		return;
+
+	err = pkgerr_new();
+
+	if (ps->keystore) {
+		(void) close_keystore(err, ps->keystore, NULL);
+	}
+
+	ps->keystore = NULL;
+
+	pkgerr_free(err);
+
+	if (ps->uniqfile) {
+		(void) remove_dwnld_file(ps->uniqfile);
+		free(ps->uniqfile);
+		ps->uniqfile = NULL;
+	}
+	if (ps->link) {
+		(void) remove_dwnld_file(ps->link);
+		free(ps->link);
+		ps->link = NULL;
+	}
+	if (ps->dwnld_dir) {
+	    (void) rmdir(ps->dwnld_dir);
+	    ps->dwnld_dir = NULL;
+	}
+	if (ps->errstr) {
+	    free(ps->errstr);
+	    ps->errstr = NULL;
+	}
+
+	if (ps->content) {
+	    free(ps->content);
+	    ps->content = NULL;
+	}
+
+	if (ps->resp) {
+		http_free_respinfo(ps->resp);
+		ps->resp = NULL;
+	}
+
+	if (ps) {
+	    free(ps);
+	    ps = NULL;
+	}
+}
+
+/*
+ * Name:		web_session_control
+ * Description:	Downloads an arbitrary URL and saves to disk.
+ *
+ * Arguments:	err - where to record any errors.
+ *     		url - URL pointing to content to download - can be
+ *			http:// or https://
+ *		dwnld_dir - Directory to download into
+ *		keystore - keystore to use for accessing trusted
+ *			certs when downloading using SSL
+ *		proxy - HTTP proxy to use, or NULL for no proxy
+ *		proxy_port - HTTP proxy port to use, ignored
+ *			if proxy is NULL
+ *		passarg - method to retrieve password
+ *		retries - # of times to retry download before
+ *			giving up
+ *		timeout - how long to wait before retrying,
+ *			when download is interrupted
+ *		nointeract - if non-zero, do not output
+ *			download progress to screen
+ *
+ * Returns :	B_TRUE - success, B_FALSE otherwise
+ */
+boolean_t
+web_session_control(PKG_ERR *err, char *url, char *dwnld_dir,
+    keystore_handle_t keystore, char *proxy, ushort_t proxy_port,
+    int retries, int timeout, int nointeract, char **fname)
+{
+	int i;
+	boolean_t ret = B_TRUE;
+	boolean_t retrieved = B_FALSE;
+
+	if (!init_session()) {
+	    ret = B_FALSE;
+	    goto cleanup;
+	}
+
+	if (!parse_url_proxy(err, url, proxy, proxy_port)) {
+		ret = B_FALSE;
+		goto cleanup;
+	}
+
+	ps->timeout = timeout;
+
+	if (keystore != NULL)
+		ps->keystore = keystore;
+
+	if (dwnld_dir != NULL)
+		ps->dwnld_dir = xstrdup(dwnld_dir);
+	else {
+		pkgerr_add(err, PKGERR_WEB, gettext(ERR_NO_DWNLD_DIR));
+		ret = B_FALSE;
+		goto cleanup;
+	}
+
+	if (!check_dwnld_dir(err, dwnld_dir)) {
+		ret = B_FALSE;
+		goto cleanup;
+	}
+
+	for (i = 0; i < retries && !retrieved; i++) {
+		if (!web_setup(err)) {
+			ret = B_FALSE;
+			goto cleanup;
+		}
+
+		switch (web_connect(err)) {
+		    /* time out and wait a little bit for these failures */
+		case WEB_OK:
+		    /* were able to connect */
+			reset_backoff();
+			break;
+		case WEB_TIMEOUT:
+			echo_out(nointeract, gettext(MSG_DWNLD_TIMEOUT));
+			(void) web_disconnect();
+			backoff();
+			continue;
+
+		case WEB_CONNREFUSED:
+			echo_out(nointeract, gettext(MSG_DWNLD_CONNREF),
+			    ps->url.hport.hostname);
+			(void) web_disconnect();
+			backoff();
+			continue;
+		case WEB_HOSTDOWN:
+			echo_out(nointeract, gettext(MSG_DWNLD_HOSTDWN),
+			    ps->url.hport.hostname);
+			(void) web_disconnect();
+			backoff();
+			continue;
+
+		default:
+			/* every other failure is a hard failure, so bail */
+			ret = B_FALSE;
+			goto cleanup;
+		}
+
+		switch (web_send_request(err, HTTP_REQ_TYPE_HEAD,
+				ps->data.cur_pos, ps->data.content_length)) {
+		case WEB_OK:
+		    /* were able to connect */
+			reset_backoff();
+			break;
+		case WEB_TIMEOUT:
+			echo_out(nointeract, gettext(MSG_DWNLD_TIMEOUT));
+			(void) web_disconnect();
+			backoff();
+			continue;
+
+		case WEB_CONNREFUSED:
+			echo_out(nointeract, gettext(MSG_DWNLD_CONNREF),
+			    ps->url.hport.hostname);
+			(void) web_disconnect();
+			backoff();
+			continue;
+		case WEB_HOSTDOWN:
+			echo_out(nointeract, gettext(MSG_DWNLD_HOSTDWN),
+			    ps->url.hport.hostname);
+			(void) web_disconnect();
+			backoff();
+			continue;
+		default:
+			/* every other case is failure, so bail */
+			ret = B_FALSE;
+			goto cleanup;
+		}
+
+		if (!web_eval_headers(err)) {
+			ret = B_FALSE;
+			goto cleanup;
+		}
+
+		switch (web_get_file(err, dwnld_dir, nointeract, fname)) {
+		case WEB_OK:
+			/* were able to retrieve file */
+			retrieved = B_TRUE;
+			reset_backoff();
+			break;
+
+		case WEB_TIMEOUT:
+			echo_out(nointeract, gettext(MSG_DWNLD_TIMEOUT));
+			(void) web_disconnect();
+			backoff();
+			continue;
+
+		case WEB_CONNREFUSED:
+			echo_out(nointeract, gettext(MSG_DWNLD_CONNREF),
+			    ps->url.hport.hostname);
+			(void) web_disconnect();
+			backoff();
+			continue;
+		case WEB_HOSTDOWN:
+			echo_out(nointeract, gettext(MSG_DWNLD_HOSTDWN),
+			    ps->url.hport.hostname);
+			(void) web_disconnect();
+			backoff();
+			continue;
+		default:
+			/* every other failure is a hard failure, so bail */
+			ret = B_FALSE;
+			goto cleanup;
+		}
+	}
+
+	if (!retrieved) {
+		/* max retries attempted */
+		pkgerr_add(err, PKGERR_WEB,
+		    gettext(ERR_DWNLD_FAILED), retries);
+		ret = B_FALSE;
+	}
+cleanup:
+	(void) web_disconnect();
+	if (!ret) {
+		pkgerr_add(err, PKGERR_WEB, gettext(ERR_DWNLD), url);
+	}
+	return (ret);
+}
+
+/*
+ * Name:		get_signature
+ * Description:	retrieves signature from signed package.
+ *
+ * Arguments:	err - where to record any errors.
+ *		ids_name - name of package stream, for error reporting
+ *     		devp - Device on which package resides that we
+ *		result - where to store resulting PKCS7 signature
+ *
+ * Returns :	B_TRUE - package is signed and signature returned OR
+ *		package is not signed, in which case result is NULL
+ *
+ *		B_FALSE - there were problems accessing signature,
+ *		and it is unknown whether it is signed or not.  Errors
+ *		recorded in 'err'.
+ */
+boolean_t
+get_signature(PKG_ERR *err, char *ids_name, struct pkgdev *devp, PKCS7 **result)
+{
+	char path[PATH_MAX];
+	int len, fd = -1;
+	struct stat buf;
+	FILE *fp = NULL;
+	boolean_t	ret = B_TRUE;
+	BIO	*sig_in = NULL;
+	PKCS7	*p7 = NULL;
+
+	/*
+	 * look for signature.  If one was in the stream,
+	 * it is now extracted
+	 */
+	if (((len = snprintf(path, PATH_MAX, "%s/%s", devp->dirname,
+	    SIGNATURE_FILENAME)) >= PATH_MAX) || (len < 0)) {
+		pkgerr_add(err, PKGERR_WEB, gettext(ERR_LEN), ids_name);
+		ret = B_FALSE;
+		goto cleanup;
+	}
+
+	if ((fd = open(path, O_RDONLY|O_NONBLOCK)) == -1) {
+		/*
+		 * only if the signature is non-existant
+		 * do we "pass"
+		 */
+		if (errno != ENOENT) {
+			pkgerr_add(err, PKGERR_WEB, gettext(ERR_OPENSIG),
+			    strerror(errno));
+			ret = B_FALSE;
+			goto cleanup;
+		}
+	} else {
+		/* found sig file.  parse it. */
+		if (fstat(fd, &buf) == -1) {
+			pkgerr_add(err, PKGERR_WEB,
+			    gettext(ERR_OPENSIG), strerror(errno));
+			ret = B_FALSE;
+			goto cleanup;
+		}
+
+		if (!S_ISREG(buf.st_mode)) {
+			pkgerr_add(err, PKGERR_WEB, gettext(ERR_OPENSIG),
+			    (gettext(ERR_NOT_REG)));
+			ret = B_FALSE;
+			goto cleanup;
+		}
+
+		if ((fp = fdopen(fd, "r")) == NULL) {
+			pkgerr_add(err, PKGERR_WEB,
+			    gettext(ERR_OPENSIG), strerror(errno));
+			ret = B_FALSE;
+			goto cleanup;
+		}
+
+		/*
+		 * read in signature.  If it's invalid, we
+		 * punt, unless we're ignoring it
+		 */
+		if ((sig_in = BIO_new_fp(fp, BIO_NOCLOSE)) == NULL) {
+			pkgerr_add(err, PKGERR_WEB,
+			    gettext(ERR_OPENSIG), strerror(errno));
+			goto cleanup;
+		}
+
+		if ((p7 = PEM_read_bio_PKCS7(sig_in,
+		    NULL, NULL, NULL)) == NULL) {
+			pkgerr_add(err, PKGERR_WEB, gettext(ERR_CORRUPTSIG),
+			    ids_name);
+			ret = B_FALSE;
+			goto cleanup;
+		}
+		*result = p7;
+		p7 = NULL;
+	}
+
+cleanup:
+	if (sig_in)
+		(void) BIO_free(sig_in);
+	if (fp)
+		(void) fclose(fp);
+	if (fd != -1)
+		(void) close(fd);
+	if (p7)
+		(void) PKCS7_free(p7);
+
+	return (ret);
+}
+
+/*
+ * Name:		echo_out
+ * Description:	Conditionally output a message to stdout
+ *
+ * Arguments:	nointeract - if non-zero, do not output anything
+ *		fmt - print format
+ *		... - print arguments
+ *
+ * Returns :	none
+ */
+void
+echo_out(int nointeract, char *fmt, ...)
+{
+	va_list ap;
+
+	va_start(ap, fmt);
+
+	if (nointeract)
+		return;
+
+	(void) vfprintf(stdout, fmt, ap);
+
+	va_end(ap);
+
+	(void) putc('\n', stdout);
+}
+
+/*
+ * Name:		strip_port
+ * Description:	Returns "port" portion of a "hostname:port" string
+ *
+ * Arguments:	proxy - full "hostname:port" string pointer
+ *
+ * Returns :	the "port" portion of a "hostname:port" string,
+ *		converted to a decimal integer, or (int)0
+ *		if string contains no :port suffix.
+ */
+ushort_t
+strip_port(char *proxy)
+{
+	char *tmp_port;
+
+	if ((tmp_port = strpbrk(proxy, ":")) != NULL)
+		return (atoi(tmp_port));
+	else
+		return (0);
+}
+
+/*
+ * Name:		set_web_install
+ * Description:	Sets flag indicating we are doing a web-based install
+ *
+ * Arguments:	none
+ *
+ * Returns :	none
+ */
+void
+set_web_install(void)
+{
+	webpkg_install++;
+}
+
+/*
+ * Name:		is_web_install
+ * Description:	Determines whether we are doing a web-based install
+ *
+ * Arguments:	none
+ *
+ * Returns :	non-zero if we are doing a web-based install, 0 otherwise
+ */
+int
+is_web_install(void)
+{
+	return (webpkg_install);
+}
+
+/* ~~~~~~~~~~~~~~ Private Functions ~~~~~~~~~~~~~~~~~~~ */
+
+/*
+ * Name:		web_disconnect
+ * Description:	Disconnects connection to web server
+ *
+ * Arguments:	none
+ *
+ * Returns :	B_TRUE - successful disconnect, B_FALSE otherwise
+ *		Temp certificiate files are deleted,
+ *		if one was used to initiate the connection
+ *		(such as when using SSL)
+ */
+static boolean_t
+web_disconnect(void)
+{
+	if (ps->certfile) {
+		(void) unlink(ps->certfile);
+	}
+	if (http_srv_disconnect(ps->hps) == 0)
+		if (http_srv_close(ps->hps) == 0)
+			return (B_TRUE);
+
+	return (B_FALSE);
+}
+
+/*
+ * Name:		check_dwnld_dir
+ * Description:	Creates temp download directory
+ *
+ * Arguments:	err - where to record any errors.
+ *     		dwnld_dir - name of directory to create
+ *
+ * Returns :	B_TRUE - success, B_FALSE otherwise
+ *		on success, directory is created with
+ *		safe permissions
+ */
+static boolean_t
+check_dwnld_dir(PKG_ERR *err, char *dwnld_dir)
+{
+	DIR *dirp;
+
+	/*
+	 * Check the directory passed in. If it doesn't exist, create it
+	 * with strict permissions
+	 */
+	if ((dirp = opendir(dwnld_dir)) == NULL) {
+		if (mkdir(dwnld_dir, 0744) == -1) {
+			pkgerr_add(err, PKGERR_WEB, gettext(MSG_NOTEMP),
+			    dwnld_dir);
+			return (B_FALSE);
+		}
+	}
+	if (dirp) {
+		(void) closedir(dirp);
+	}
+	return (B_TRUE);
+}
+
+/*
+ * Name:		ds_validate_signature
+ * Description:	Validates signature found in a package datastream
+ *
+ * Arguments:	err - where to record any errors.
+ *		pkgdev - Package context handle of package to verify
+ *		pkgs - Null-terminated List of package name to verify
+ *		ids_name - Pathname to stream to validate
+ *		p7 - PKCS7 signature decoded from stream header
+ *		cas - List of trusted CA certificates
+ *		proxy - Proxy to use when doing online validation (OCSP)
+ *		nointeract - if non-zero, do not output to screen
+ *
+ * Returns :	B_TRUE - success, B_FALSE otherwise
+ *		success means signature was completely validated,
+ *		and contents of stream checked against signature.
+ */
+boolean_t
+ds_validate_signature(PKG_ERR *err, struct pkgdev *pkgdev, char **pkgs,
+    char *ids_name, PKCS7 *p7, STACK_OF(X509) *cas,
+    url_hport_t *proxy, int nointeract)
+{
+	BIO			 *p7_bio;
+	boolean_t		ret = B_TRUE;
+
+	/* make sure it's a Signed PKCS7 message */
+	if (!PKCS7_type_is_signed(p7)) {
+		pkgerr_add(err, PKGERR_WEB, gettext(ERR_CORRUPTSIG_TYPE),
+		    ids_name);
+		ret = B_FALSE;
+		goto cleanup;
+	}
+
+	/* initialize PKCS7 object to be filled in */
+	if (!PKCS7_get_detached(p7)) {
+		pkgerr_add(err, PKGERR_WEB, gettext(ERR_CORRUPTSIG_DT),
+		    ids_name);
+		ret = B_FALSE;
+		goto cleanup;
+	}
+
+	/* dump header and packages into BIO to calculate the message digest */
+	if ((p7_bio = PKCS7_dataInit(p7, NULL)) == NULL) {
+		pkgerr_add(err, PKGERR_WEB, gettext(ERR_CORRUPTSIG),
+		    ids_name);
+		ret = B_FALSE;
+		goto cleanup;
+	}
+
+	if ((BIO_ds_dump_header(err, p7_bio) != 0) ||
+	    (BIO_ds_dump(err, ids_name, p7_bio) != 0)) {
+		ret = B_FALSE;
+		goto cleanup;
+	}
+	(void) BIO_flush(p7_bio);
+
+	/* validate the stream and its signature */
+	if (!validate_signature(err, ids_name, p7_bio, p7, cas,
+	    proxy, nointeract)) {
+		ret = B_FALSE;
+		goto cleanup;
+	}
+
+	/* reset device stream (really bad performance for tapes) */
+	(void) ds_close(1);
+	(void) ds_init(ids_name, pkgs, pkgdev->norewind);
+
+cleanup:
+	return (ret);
+}
+
+
+/*
+ * Name:		validate_signature
+ * Description:	Validates signature of an arbitrary stream of bits
+ *
+ * Arguments:	err - where to record any errors.
+ *		name - Descriptive name of object being validated,
+ *			for good error reporting messages
+ *		indata - BIO object to read stream bits from
+ *		p7 - PKCS7 signature of stream
+ *		cas - List of trusted CA certificates
+ *		proxy - Proxy to use when doing online validation (OCSP)
+ *		nointeract - if non-zero, do not output to screen
+ *
+ * Returns :	B_TRUE - success, B_FALSE otherwise
+ *		success means signature was completely validated,
+ *		and contents of stream checked against signature.
+ */
+boolean_t
+validate_signature(PKG_ERR *err, char *name, BIO *indata, PKCS7 *p7,
+    STACK_OF(X509) *cas, url_hport_t *proxy, int nointeract)
+{
+	STACK_OF(PKCS7_SIGNER_INFO) *sec_sinfos = NULL;
+
+	PKCS7_SIGNER_INFO	*signer = NULL;
+	X509_STORE		*sec_truststore = NULL;
+	X509_STORE_CTX		*ctx = NULL;
+	X509			*signer_cert = NULL, *issuer = NULL;
+	STACK_OF(X509)		*chaincerts = NULL;
+	int			i, k;
+	unsigned long		errcode;
+	const char		*err_data = NULL;
+	const char		*err_reason = NULL;
+	char			*err_string;
+	int			err_flags;
+	verify_cb_data_t	verify_data;
+	char			*signer_sname;
+	char			*signer_iname;
+	PKCS7_ISSUER_AND_SERIAL	*ias;
+	boolean_t		ret = B_TRUE;
+
+	/* only support signed PKCS7 signatures */
+	if (!PKCS7_type_is_signed(p7)) {
+	    PKCS7err(PKCS7_F_PKCS7_DATAVERIFY, PKCS7_R_WRONG_PKCS7_TYPE);
+	    ret = B_FALSE;
+	    goto cleanup;
+	}
+
+	/* initialize temporary internal trust store used for verification */
+	sec_truststore = X509_STORE_new();
+
+	for (i = 0; i < sk_X509_num(cas); i++) {
+		if (X509_STORE_add_cert(sec_truststore,
+		    sk_X509_value(cas, i)) == 0) {
+			pkgerr_add(err, PKGERR_VERIFY, gettext(ERR_MEM));
+			ret = B_FALSE;
+			goto cleanup;
+		}
+	}
+
+	/* get signers from the signature */
+	if ((sec_sinfos = PKCS7_get_signer_info(p7)) == NULL) {
+		pkgerr_add(err, PKGERR_WEB, gettext(ERR_CORRUPTSIG), name);
+		ret = B_FALSE;
+		goto cleanup;
+	}
+
+	/* verify each signer found in the PKCS7 signature */
+	for (k = 0; k < sk_PKCS7_SIGNER_INFO_num(sec_sinfos); k++) {
+		signer = sk_PKCS7_SIGNER_INFO_value(sec_sinfos, k);
+		signer_cert = PKCS7_cert_from_signer_info(p7, signer);
+		signer_sname = get_subject_display_name(signer_cert);
+		signer_iname = get_issuer_display_name(signer_cert);
+
+		echo_out(nointeract, gettext(MSG_VERIFY), signer_sname);
+
+		/* find the issuer of the current cert */
+		chaincerts = p7->d.sign->cert;
+		ias = signer->issuer_and_serial;
+		issuer = X509_find_by_issuer_and_serial(chaincerts,
+		    ias->issuer, ias->serial);
+
+		/* were we not able to find the issuer cert */
+		if (issuer == NULL) {
+			pkgerr_add(err, PKGERR_WEB,
+			    gettext(ERR_VERIFY_ISSUER),
+			    signer_iname, signer_sname);
+			ret = B_FALSE;
+			goto cleanup;
+		}
+
+		/* Lets verify */
+		if ((ctx = X509_STORE_CTX_new()) == NULL) {
+			pkgerr_add(err, PKGERR_VERIFY, gettext(ERR_MEM));
+			ret = B_FALSE;
+			goto cleanup;
+		}
+		(void) X509_STORE_CTX_init(ctx, sec_truststore,
+		    issuer, chaincerts);
+		(void) X509_STORE_CTX_set_purpose(ctx,
+		    X509_PURPOSE_ANY);
+
+		/* callback will perform OCSP on certificates with OCSP data */
+		X509_STORE_CTX_set_verify_cb(ctx, web_verify);
+
+		/* pass needed data into callback through the app_data handle */
+		verify_data.proxy = proxy;
+		verify_data.cas = cas;
+		verify_data.err = err;
+		(void) X509_STORE_CTX_set_app_data(ctx, &verify_data);
+
+		/* first verify the certificate chain */
+		i = X509_verify_cert(ctx);
+		if (i <= 0 && ctx->error != X509_V_ERR_CERT_HAS_EXPIRED) {
+			signer_sname =
+			    get_subject_display_name(ctx->current_cert);
+			signer_iname =
+			    get_issuer_display_name(ctx->current_cert);
+			/* if the verify context holds an error, print it */
+			if (ctx->error != X509_V_OK) {
+				pkgerr_add(err, PKGERR_VERIFY,
+				    gettext(ERR_VERIFY_SIG), signer_sname,
+				    signer_iname,
+			    (char *)X509_verify_cert_error_string(ctx->error));
+			} else {
+				/* some other error.  print them all. */
+				while ((errcode = ERR_get_error_line_data(NULL,
+				    NULL, &err_data, &err_flags)) != 0) {
+					err_reason =
+					    ERR_reason_error_string(errcode);
+					if (err_reason == NULL) {
+						err_reason =
+						    gettext(ERR_SIG_INT);
+					}
+
+					if (!(err_flags & ERR_TXT_STRING)) {
+						err_data =
+						    gettext(ERR_SIG_INT);
+					}
+					err_string =
+					    xmalloc(strlen(err_reason) +
+						strlen(err_data) + 3);
+					(void) sprintf(err_string, "%s: %s",
+					    err_reason, err_data);
+					pkgerr_add(err, PKGERR_VERIFY,
+					    gettext(ERR_VERIFY_SIG),
+					    signer_sname, signer_iname,
+					    err_string);
+					free(err_string);
+				}
+			}
+			ret = B_FALSE;
+			goto cleanup;
+		}
+
+		/* now verify the signature */
+		i = PKCS7_signatureVerify(indata, p7, signer, issuer);
+
+		if (i <= 0) {
+			/* print out any OpenSSL-specific errors */
+			signer_sname =
+			    get_subject_display_name(ctx->current_cert);
+			signer_iname =
+			    get_subject_display_name(ctx->current_cert);
+			while ((errcode = ERR_get_error_line_data(NULL,
+			    NULL, &err_data, &err_flags)) != 0) {
+				err_reason =
+				    ERR_reason_error_string(errcode);
+				if (err_reason == NULL) {
+					err_reason =
+					    gettext(ERR_SIG_INT);
+				}
+
+				if (!(err_flags & ERR_TXT_STRING)) {
+					err_data =
+					    gettext(ERR_SIG_INT);
+				}
+				pkgerr_add(err, PKGERR_VERIFY,
+				    gettext(ERR_VERIFY_SIG), signer_sname,
+				    signer_iname, err_reason);
+				pkgerr_add(err, PKGERR_VERIFY,
+				    gettext(ERR_VERIFY_SIG), signer_sname,
+				    signer_iname, err_data);
+			}
+			ret = B_FALSE;
+			goto cleanup;
+		}
+
+		echo_out(nointeract, gettext(MSG_VERIFY_OK), signer_sname);
+	}
+
+	/* signature(s) verified successfully */
+cleanup:
+	if (ctx)
+		X509_STORE_CTX_cleanup(ctx);
+	return (ret);
+}
+
+/*
+ * Name:		web_verify
+ * Description:	Callback used by PKCS7_dataVerify when
+ *		verifying a certificate chain.
+ *
+ * Arguments:	err - where to record any errors.
+ *     		ctx - The context handle of the current verification operation
+ *
+ * Returns :	B_TRUE - success, B_FALSE otherwise
+ *		if it's '0' (not OK) we simply return it, since the
+ *		verification operation has already determined that the
+ *		cert is invalid.  if 'ok' is non-zero, then we do our
+ *		checks, and return 0 or 1 based on if the cert is
+ *		invalid or valid.
+ */
+static int
+web_verify(int ok, X509_STORE_CTX *ctx)
+{
+	X509	*curr_cert;
+	X509	*curr_issuer;
+	char	*uri;
+	url_hport_t	*proxy;
+	PKG_ERR	*err = NULL;
+	STACK_OF(X509) *cas;
+	if (!ok) {
+		/* don't override a verify failure */
+		return (ok);
+	}
+
+
+	/* get app data supplied through callback context */
+	err = ((verify_cb_data_t *)X509_STORE_CTX_get_app_data(ctx))->err;
+	proxy = ((verify_cb_data_t *)X509_STORE_CTX_get_app_data(ctx))->proxy;
+	cas = ((verify_cb_data_t *)X509_STORE_CTX_get_app_data(ctx))->cas;
+
+	/* Check revocation status */
+	curr_cert = X509_STORE_CTX_get_current_cert(ctx);
+
+	/* this shouldn't happen */
+	if (curr_cert == NULL) {
+		pkgerr_add(err, PKGERR_INTERNAL, gettext(ERR_PKG_INTERNAL),
+		    __FILE__, __LINE__);
+		return (0);
+	}
+
+	/* don't perform OCSP unless cert has required OCSP extensions */
+	if (get_ocsp_uri(curr_cert, &uri)) {
+		if (get_issuer(&curr_issuer, ctx, curr_cert) <= 0) {
+			/* no issuer! */
+			pkgerr_add(err, PKGERR_INTERNAL,
+			    gettext(ERR_PKG_INTERNAL),
+			    __FILE__, __LINE__);
+			return (0);
+		}
+
+		/*
+		 * ok we have the current cert
+		 * and its issuer.  Do the OCSP check
+		 */
+
+		/*
+		 * OCSP extensions are, by, RFC 2459, never critical
+		 * extensions, therefore, we only fail if we were able
+		 * to explicitly contact an OCSP responder, and that
+		 * responder did not indicate the cert was valid.  We
+		 * also fail if user-supplied data could not be parsed
+		 * or we run out of memory.  We succeeed for "soft"
+		 * failures, such as not being able to connect to the
+		 * OCSP responder, or trying to use if the OCSP URI
+		 * indicates SSL must be used (which we do not
+		 * support)
+		 */
+		switch (ocsp_verify(err, curr_cert, curr_issuer,
+		    uri, proxy, cas)) {
+		case OCSPMem:		/* Ran out of memory */
+		case OCSPInternal:	/* Some internal error */
+		case OCSPVerify:	/* OCSP responder indicated fail */
+			return (0);
+		}
+		/* all other cases are success, or soft failures */
+		pkgerr_clear(err);
+	}
+
+	return (ok);
+}
+
+/*
+ * Name:		get_time_string
+ * Description:	Generates a human-readable string from an ASN1_GENERALIZED_TIME
+ *
+ * Arguments:	intime - The time to convert
+ *
+ * Returns :	A pointer to a static string representing the passed-in time.
+ */
+static char
+*get_time_string(ASN1_GENERALIZEDTIME *intime)
+{
+
+	static char	time[ATTR_MAX];
+	BIO		*mem;
+	char	*p;
+
+	if (intime == NULL) {
+		return (NULL);
+	}
+	if ((mem = BIO_new(BIO_s_mem())) == NULL) {
+		return (NULL);
+	}
+
+	if (ASN1_GENERALIZEDTIME_print(mem, intime) == 0) {
+		(void) BIO_free(mem);
+		return (NULL);
+	}
+
+	if (BIO_gets(mem, time, ATTR_MAX) <= 0) {
+		(void) BIO_free(mem);
+		return (NULL);
+	}
+
+	(void) BIO_free(mem);
+
+	/* trim the end of the string */
+	for (p = time + strlen(time) - 1; isspace(*p); p--) {
+		*p = '\0';
+	}
+
+	return (time);
+}
+
+/*
+ * Name:		get_ocsp_uri
+ * Description:	Examines an X509 certificate and retrieves the embedded
+ *		OCSP Responder URI if one exists.
+ *
+ * Arguments:	cert - The cert to inspect
+ *     		uri - pointer where the newly-allocated URI is placed, if found
+ *
+ * Returns :	Success if the URI was found.  Appropriate status otherwise.
+ */
+static boolean_t
+get_ocsp_uri(X509 *cert, char **uri)
+{
+	AUTHORITY_INFO_ACCESS		*aia;
+	ACCESS_DESCRIPTION		*ad;
+	int				i;
+
+	if (getenv("PKGWEB_TEST_OCSP")) {
+		*uri = xstrdup(getenv("PKGWEB_TEST_OCSP"));
+		return (B_TRUE);
+	}
+
+	/* get the X509v3 extension holding the OCSP URI */
+	if ((aia = X509_get_ext_d2i(cert, NID_info_access,
+	    NULL, NULL)) != NULL) {
+		for (i = 0; i < sk_ACCESS_DESCRIPTION_num(aia); i++) {
+			ad = sk_ACCESS_DESCRIPTION_value(aia, i);
+			if (OBJ_obj2nid(ad->method) == NID_ad_OCSP) {
+				if (ad->location->type == GEN_URI) {
+					*uri =
+		    xstrdup((char *)ASN1_STRING_data(ad->location->d.ia5));
+					return (B_TRUE);
+				}
+			}
+		}
+	}
+
+	/* no URI was found */
+	return (B_FALSE);
+}
+
+/*
+ * Name:		ocsp_verify
+ * Description:	Attempts to contact an OCSP Responder and ascertain the validity
+ *		of an X509 certificate.
+ *
+ * Arguments:	err - Error object to add error messages to
+ *		cert - The cert to validate
+ *		issuer - The certificate of the issuer of 'cert'
+ *     		uri - The OCSP Responder URI
+ *		cas - The trusted CA certificates used to verify the
+ *		signed OCSP response
+ * Returns :	Success - The OCSP Responder reported a 'good'
+ *		status for the cert otherwise, appropriate
+ *		error is returned.
+ */
+static OCSPStatus
+ocsp_verify(PKG_ERR *err, X509 *cert, X509 *issuer,
+    char *uri, url_hport_t *proxy, STACK_OF(X509) *cas)
+{
+	OCSP_CERTID		*id;
+	OCSP_REQUEST		*req;
+	OCSP_RESPONSE		*resp;
+	OCSP_BASICRESP		*bs;
+	BIO			*cbio, *mem;
+	char			ocspbuf[OCSP_BUFSIZ];
+	char *host = NULL, *portstr = NULL, *path = "/", *p, *q, *r;
+	int		port, status, reason;
+	int	len, retval, respcode, use_ssl = 0;
+	ASN1_GENERALIZEDTIME	*rev, *thisupd, *nextupd;
+	char	*subjname;
+	time_t			currtime;
+	char			currtimestr[ATTR_MAX];
+	unsigned long		errcode;
+	const char		*err_reason;
+
+	subjname = get_subject_display_name(cert);
+
+	/* parse the URI into its constituent parts */
+	if (OCSP_parse_url(uri, &host, &portstr, &path, &use_ssl) == NULL) {
+		pkgerr_add(err, PKGERR_PARSE, gettext(ERR_OCSP_PARSE), uri);
+		return (OCSPParse);
+	}
+
+	/* we don't currently support SSL-based OCSP Responders */
+	if (use_ssl) {
+		pkgerr_add(err, PKGERR_PARSE, gettext(ERR_OCSP_UNSUP), uri);
+		return (OCSPUnsupported);
+	}
+
+	/* default port if none specified */
+	if (portstr == NULL) {
+		port = (int)URL_DFLT_SRVR_PORT;
+	} else {
+		port = (int)strtoul(portstr, &r, 10);
+		if (*r != '\0') {
+			pkgerr_add(err, PKGERR_PARSE,
+			    gettext(ERR_OCSP_PARSE), uri);
+			return (OCSPParse);
+		}
+	}
+
+	/* allocate new request structure */
+	if ((req = OCSP_REQUEST_new()) == NULL) {
+		pkgerr_add(err, PKGERR_PARSE, gettext(ERR_MEM));
+		return (OCSPMem);
+	}
+
+	/* convert cert and issuer fields into OCSP request data */
+	if ((id = OCSP_cert_to_id(NULL, cert, issuer)) == NULL) {
+		pkgerr_add(err, PKGERR_PARSE, gettext(ERR_PKG_INTERNAL),
+		    __FILE__, __LINE__);
+		return (OCSPInternal);
+	}
+
+	/* fill out request structure with request data */
+	if ((OCSP_request_add0_id(req, id)) == NULL) {
+		pkgerr_add(err, PKGERR_PARSE, gettext(ERR_PKG_INTERNAL),
+		    __FILE__, __LINE__);
+		return (OCSPInternal);
+	}
+
+	/* add nonce */
+	OCSP_request_add1_nonce(req, NULL, -1);
+
+	/* connect to host, or proxy */
+	if (proxy != NULL) {
+		if ((cbio = BIO_new_connect(proxy->hostname)) == NULL) {
+			pkgerr_add(err, PKGERR_PARSE, gettext(ERR_MEM));
+			return (OCSPMem);
+		}
+
+		/*
+		 * BIO_set_conn_int_port takes an int *, so let's give it one
+		 * rather than an ushort_t *
+		 */
+		port = proxy->port;
+		(void) BIO_set_conn_int_port(cbio, &port);
+		if (BIO_do_connect(cbio) <= 0) {
+			pkgerr_add(err, PKGERR_PARSE,
+			    gettext(ERR_OCSP_CONNECT),
+			    proxy->hostname, port);
+			return (OCSPConnect);
+		}
+	} else {
+		if ((cbio = BIO_new_connect(host)) == NULL) {
+			pkgerr_add(err, PKGERR_PARSE, gettext(ERR_MEM));
+			return (OCSPMem);
+		}
+
+		(void) BIO_set_conn_int_port(cbio, &port);
+		if (BIO_do_connect(cbio) <= 0) {
+			pkgerr_add(err, PKGERR_PARSE,
+			    gettext(ERR_OCSP_CONNECT),
+			    host, port);
+			return (OCSPConnect);
+		}
+	}
+
+	/* calculate length of binary request data */
+	len = i2d_OCSP_REQUEST(req, NULL);
+
+	/* send the request headers */
+	if (proxy != NULL) {
+		retval = BIO_printf(cbio, OCSP_REQUEST_FORMAT, uri, len);
+	} else {
+		retval = BIO_printf(cbio, OCSP_REQUEST_FORMAT, path, len);
+	}
+
+	if (retval <= 0) {
+		pkgerr_add(err, PKGERR_PARSE, gettext(ERR_OCSP_SEND), host);
+		return (OCSPRequest);
+	}
+
+	/* send the request binary data */
+	if (i2d_OCSP_REQUEST_bio(cbio, req) <= 0) {
+		pkgerr_add(err, PKGERR_PARSE, gettext(ERR_OCSP_SEND), host);
+		return (OCSPRequest);
+	}
+
+	/*
+	 * read the response into a memory BIO, so we can 'gets'
+	 * (socket bio's don't support BIO_gets)
+	 */
+	if ((mem = BIO_new(BIO_s_mem())) == NULL) {
+		pkgerr_add(err, PKGERR_PARSE, gettext(ERR_MEM));
+		return (OCSPMem);
+	}
+
+	while ((len = BIO_read(cbio, ocspbuf, OCSP_BUFSIZ))) {
+		if (len < 0) {
+			pkgerr_add(err, PKGERR_PARSE,
+			    gettext(ERR_OCSP_READ), host);
+			return (OCSPRequest);
+		}
+		if (BIO_write(mem, ocspbuf, len) != len) {
+			pkgerr_add(err, PKGERR_PARSE, gettext(ERR_MEM));
+			return (OCSPMem);
+		}
+	}
+
+	/* now get the first line of the response */
+	if (BIO_gets(mem, ocspbuf, OCSP_BUFSIZ) <= 0) {
+		pkgerr_add(err, PKGERR_PARSE, gettext(ERR_OCSP_RESP_PARSE));
+		return (OCSPRequest);
+	}
+
+	/* parse the header response */
+	/* it should look like "HTTP/x.x 200 OK" */
+
+	/* skip past the protocol info */
+	for (p = ocspbuf; (*p != '\0') && !isspace(*p); p++)
+		continue;
+
+	/* skip past whitespace betwen protocol and start of response code */
+	while ((*p != '\0') && isspace(*p)) {
+		p++;
+	}
+
+	if (*p == '\0') {
+		/* premature end */
+		pkgerr_add(err, PKGERR_PARSE,
+		    gettext(ERR_OCSP_RESP_PARSE), ocspbuf);
+		return (OCSPRequest);
+	}
+
+	/* find end of response code */
+	for (q = p; (*q != NULL) && !isspace(*q); q++)
+		continue;
+
+	/* mark end of response code */
+	*q++ = '\0';
+
+	/* parse response code */
+	respcode = strtoul(p, &r, 10);
+	if (*r != '\0') {
+		pkgerr_add(err, PKGERR_PARSE,
+		    gettext(ERR_OCSP_RESP_PARSE), ocspbuf);
+		return (OCSPRequest);
+	}
+
+	/* now find beginning of the response string */
+	while ((*q != NULL) && isspace(*q)) {
+		q++;
+	}
+
+	/* trim whitespace from end of message */
+	for (r = (q + strlen(q) - 1); isspace(*r); r--) {
+		*r = '\0';
+	}
+
+	/* response must be OK */
+	if (respcode != 200) {
+		pkgerr_add(err, PKGERR_PARSE,
+		    gettext(ERR_OCSP_RESP_NOTOK), 200,
+		    respcode, q);
+		return (OCSPRequest);
+	}
+
+	/* read headers, looking for content-type or a blank line */
+	while (BIO_gets(mem, ocspbuf, OCSP_BUFSIZ) > 0) {
+
+		/* if we get a content type, make sure it's the right type */
+		if (ci_strneq(ocspbuf, CONTENT_TYPE_HDR,
+		    strlen(CONTENT_TYPE_HDR))) {
+
+			/* look for the delimiting : */
+			p = strchr(ocspbuf + strlen(CONTENT_TYPE_HDR), ':');
+
+			if (p == NULL) {
+				pkgerr_add(err, PKGERR_PARSE,
+				    gettext(ERR_OCSP_RESP_PARSE), ocspbuf);
+				return (OCSPResponder);
+			}
+
+			/* skip over ':' */
+			p++;
+
+			/* find beginning of the content type */
+			while ((*p != NULL) && isspace(*p)) {
+				p++;
+			}
+
+			if (!ci_strneq(p, CONTENT_OCSP_RESP,
+			    strlen(CONTENT_OCSP_RESP))) {
+				/* response is not right type */
+				pkgerr_add(err, PKGERR_PARSE,
+				    gettext(ERR_OCSP_RESP_TYPE),
+				    p, CONTENT_OCSP_RESP);
+				return (OCSPResponder);
+			}
+
+			/* continue with next header line */
+			continue;
+		}
+
+		/* scan looking for a character */
+		for (p = ocspbuf; (*p != '\0') && isspace(*p); p++) {
+			continue;
+		}
+		/*
+		 * if we got to the end of the line with
+		 *  no chars, then this is a blank line
+		 */
+		if (*p == '\0') {
+			break;
+		}
+	}
+
+
+	if (*p != '\0') {
+		/* last line was not blank */
+		pkgerr_add(err, PKGERR_PARSE,
+		    gettext(ERR_OCSP_RESP_PARSE), ocspbuf);
+		return (OCSPResponder);
+	}
+
+	/* now read in the binary response */
+	if ((resp = d2i_OCSP_RESPONSE_bio(mem, NULL)) == NULL) {
+		pkgerr_add(err, PKGERR_PARSE, gettext(ERR_OCSP_READ), host);
+		return (OCSPResponder);
+	}
+
+	/* free temp BIOs */
+	(void) BIO_free(mem);
+	(void) BIO_free_all(cbio);
+	cbio = NULL;
+
+	/* make sure request was successful */
+	if (OCSP_response_status(resp) != OCSP_RESPONSE_STATUS_SUCCESSFUL) {
+		pkgerr_add(err, PKGERR_PARSE, gettext(ERR_OCSP_RESP_NOTOK),
+		    OCSP_RESPONSE_STATUS_SUCCESSFUL,
+		    OCSP_response_status(resp),
+		    OCSP_response_status_str(OCSP_response_status(resp)));
+		return (OCSPResponder);
+	}
+
+	/* parse binary response into internal structure */
+	if ((bs = OCSP_response_get1_basic(resp)) == NULL) {
+		pkgerr_add(err, PKGERR_PARSE, gettext(ERR_OCSP_READ), host);
+		return (OCSPParse);
+	}
+
+	/*
+	 * From here to the end of the code, the return values
+	 * should be hard failures
+	 */
+
+	/* verify the response, warn if no nonce */
+	if (OCSP_check_nonce(req, bs) <= 0) {
+		logerr(pkg_gt(WRN_OCSP_RESP_NONCE));
+	}
+
+	if (OCSP_basic_verify(bs, cas, NULL, OCSP_TRUSTOTHER) <= 0) {
+		while ((errcode = ERR_get_error()) != NULL) {
+			err_reason = ERR_reason_error_string(errcode);
+			if (err_reason == NULL) {
+				err_reason =
+				    gettext(ERR_SIG_INT);
+			}
+			pkgerr_add(err, PKGERR_PARSE, (char *)err_reason);
+		}
+		pkgerr_add(err, PKGERR_PARSE, gettext(ERR_OCSP_VERIFY_FAIL),
+		    uri);
+		return (OCSPVerify);
+	}
+
+	/* check the validity of our certificate */
+	if (OCSP_resp_find_status(bs, id, &status, &reason,
+	    &rev, &thisupd, &nextupd) == NULL) {
+		pkgerr_add(err, PKGERR_PARSE,
+		    gettext(ERR_OCSP_VERIFY_NO_STATUS), subjname);
+		return (OCSPVerify);
+	}
+
+	if ((currtime = time(NULL)) == (time_t)-1) {
+		pkgerr_add(err, PKGERR_PARSE,
+		    gettext(ERR_OCSP_VERIFY_NOTIME));
+		return (OCSPVerify);
+	}
+
+	(void) strlcpy(currtimestr, ctime(&currtime), ATTR_MAX);
+
+	/* trim end */
+	for (r = currtimestr + strlen(currtimestr) - 1;
+		isspace(*r); r--) {
+		*r = '\0';
+	}
+
+	if (!OCSP_check_validity(thisupd, nextupd,
+	    OCSP_VALIDITY_PERIOD, -1)) {
+		if (nextupd != NULL) {
+			pkgerr_add(err, PKGERR_PARSE,
+			    gettext(ERR_OCSP_VERIFY_VALIDITY),
+			    get_time_string(thisupd), get_time_string(nextupd),
+			    currtimestr);
+		} else {
+			pkgerr_add(err, PKGERR_PARSE,
+			    gettext(ERR_OCSP_VERIFY_VALIDITY),
+			    get_time_string(thisupd),
+			    currtimestr);
+		}
+		return (OCSPVerify);
+	}
+
+	if (status != V_OCSP_CERTSTATUS_GOOD) {
+		pkgerr_add(err, PKGERR_PARSE,
+		    gettext(ERR_OCSP_VERIFY_STATUS), subjname,
+		    OCSP_cert_status_str(status));
+		return (OCSPVerify);
+	}
+
+	/* everythign checks out */
+	return (OCSPSuccess);
+}
+
+/*
+ * Name:		get_issuer
+ * Description:	Attempts to find the issuing certificate for a given certificate
+ *		This will look in both the list of trusted certificates found in
+ *		the X509_STORE_CTX structure, as well as the list of untrusted
+ *		chain certificates found in the X509_STORE_CTX structure.
+ * Arguments:
+ *		issuer - The resulting issuer cert is placed here, if found
+ *		ctx - The current verification context
+ *		x - The certificate whose issuer we are looking for
+ * Returns :	Success - The issuer cert was found and placed in *issuer.
+ *		otherwise, appropriate error is returned.
+ */
+static int
+get_issuer(X509 **issuer, X509_STORE_CTX *ctx, X509 *x)
+{
+	int		i, ok;
+
+	/*
+	 * first look in the list of trusted
+	 * certs, using the context's method to do so
+	 */
+	if ((ok = ctx->get_issuer(issuer, ctx, x)) > 0) {
+		return (ok);
+	}
+
+	if (ctx->untrusted != NULL) {
+		/* didn't find it in trusted certs, look through untrusted */
+		for (i = 0; i < sk_X509_num(ctx->untrusted); i++) {
+			if (X509_check_issued(sk_X509_value(ctx->untrusted, i),
+			    x) == X509_V_OK) {
+				*issuer = sk_X509_value(ctx->untrusted, i);
+				return (1);
+			}
+		}
+	}
+	*issuer = NULL;
+	return (0);
+}
+
+/*
+ * Name:		parse_url_proxy
+ * Description:	Parses URL and optional proxy specification, populates static
+ *		'ps' structure
+ *
+ * Arguments:	err - where to record any errors.
+ *		url - URL to parse
+ *		proxy - proxy to parse, or NULL for no proxy
+ *		proxy_port - Default proxy port to use if no proxy
+ *		port specified in 'proxy'
+ *
+ * Returns :	B_TRUE - success, B_FALSE otherwise
+ *		on success, 'ps->url' and 'ps->proxy' are populated
+ *		with parsed data.
+ */
+static boolean_t
+parse_url_proxy(PKG_ERR *err, char *url, char *proxy, ushort_t proxy_port)
+{
+	boolean_t ret = B_TRUE;
+	if (!path_valid(url)) {
+		ret = B_FALSE;
+		goto cleanup;
+	}
+
+	if (url_parse(url, &ps->url) != URL_PARSE_SUCCESS) {
+		pkgerr_add(err, PKGERR_WEB, gettext(ERR_PARSE_URL), url);
+		ret = B_FALSE;
+		goto cleanup;
+	}
+
+	if (proxy != NULL) {
+		if (url_parse_hostport(proxy, &ps->proxy, proxy_port)
+				!= URL_PARSE_SUCCESS) {
+			pkgerr_add(err, PKGERR_WEB,
+			    gettext(ERR_BAD_PROXY), proxy);
+			ret = B_FALSE;
+			goto cleanup;
+		}
+	}
+
+cleanup:
+	return (ret);
+}
+
+/*
+ * Name:		web_setup
+ * Description:	Initializes http library settings
+ *
+ * Arguments:	err - where to record any errors.
+ *
+ * Returns :	B_TRUE - success, B_FALSE otherwise
+ */
+static boolean_t
+web_setup(PKG_ERR *err)
+{
+	boolean_t ret = B_TRUE;
+	static boolean_t keepalive = B_TRUE;
+
+	if ((ps->hps = http_srv_init(&ps->url)) == NULL) {
+		pkgerr_add(err, PKGERR_WEB, gettext(ERR_INIT_SESS), ps->url);
+		ret = B_FALSE;
+		goto cleanup;
+	}
+
+	if (getenv("WEBPKG_DEBUG") != NULL) {
+		http_set_verbose(B_TRUE);
+	}
+
+	if (ps->proxy.hostname[0] != '\0' &&
+			http_set_proxy(ps->hps, &ps->proxy) != 0) {
+		pkgerr_add(err, PKGERR_WEB, gettext(ERR_INIT_SESS), ps->url);
+		ret = B_FALSE;
+		goto cleanup;
+	}
+	if (http_set_keepalive(ps->hps, keepalive) != 0) {
+		pkgerr_add(err, PKGERR_WEB, gettext(ERR_INIT_SESS), ps->url);
+		ret = B_FALSE;
+		goto cleanup;
+	}
+	if (http_set_socket_read_timeout(ps->hps, ps->timeout) != 0) {
+		pkgerr_add(err, PKGERR_WEB, gettext(ERR_INIT_SESS), ps->url);
+		ret = B_FALSE;
+		goto cleanup;
+	}
+	if (http_set_random_file(ps->hps, RANDOM) != 0) {
+		pkgerr_add(err, PKGERR_WEB, gettext(ERR_INIT_SESS), ps->url);
+		ret = B_FALSE;
+		goto cleanup;
+	}
+
+	(void) http_set_p12_format(B_TRUE);
+
+cleanup:
+	return (ret);
+}
+
+/*
+ * Name:		web_connect
+ * Description:	Makes connection with URL stored in static 'ps' structure.
+ *
+ * Arguments:	err - where to record any errors.
+ *
+ * Returns :   	WEB_OK - connection successful
+ *		WEB_VERIFY_SETUP - Unable to complete necessary
+ *			SSL setup
+ *		WEB_CONNREFUSED - Connection was refused to web site
+ *		WEB_HOSTDOWN - Host was not responding to request
+ *		WEB_NOCONNECT - Some other connection failure
+ */
+static WebStatus
+web_connect(PKG_ERR *err)
+{
+	STACK_OF(X509)  *sec_cas = NULL;
+	char *path;
+	WebStatus ret = WEB_OK;
+	ulong_t		errcode;
+	uint_t		errsrc;
+	int		my_errno = 0;
+	const char		*libhttperr = NULL;
+
+	if (ps->url.https == B_TRUE) {
+		/* get CA certificates */
+		if (find_ca_certs(err, ps->keystore, &sec_cas) != 0) {
+			ret = WEB_VERIFY_SETUP;
+			goto cleanup;
+		}
+
+		if (sk_X509_num(sec_cas) < 1) {
+			/* no trusted websites */
+			pkgerr_add(err, PKGERR_WEB,
+			    gettext(ERR_KEYSTORE_NOTRUST));
+			ret = WEB_VERIFY_SETUP;
+			goto cleanup;
+		}
+
+		/*
+		 * write out all CA certs to temp file.  libwanboot should
+		 * have an interface for giving it a list of trusted certs
+		 * through an in-memory structure, but currently that does
+		 * not exist
+		 */
+		if ((path = write_ca_file(err, ps->dwnld_dir, sec_cas,
+		    WEB_CA_PHRASE)) == NULL) {
+			ret = WEB_VERIFY_SETUP;
+			goto cleanup;
+		}
+
+		ps->certfile = path;
+		if (http_set_password(ps->hps, WEB_CA_PHRASE) != 0) {
+			pkgerr_add(err, PKGERR_WEB,
+			    gettext(ERR_HTTPS_PASSWD));
+			ret = WEB_VERIFY_SETUP;
+			goto cleanup;
+		}
+
+		if (http_set_certificate_authority_file(path) != 0) {
+			pkgerr_add(err, PKGERR_WEB,
+			    gettext(ERR_HTTPS_CA));
+			ret = WEB_VERIFY_SETUP;
+			goto cleanup;
+		}
+	}
+
+	if (http_srv_connect(ps->hps) != 0) {
+		while ((errcode = http_get_lasterr(ps->hps, &errsrc)) != 0) {
+			/* Have an error - is it EINTR? */
+			if (errsrc == ERRSRC_SYSTEM) {
+				my_errno = errcode;
+				break;
+			} else if (libhttperr == NULL) {
+				/* save the first non-system error message */
+				libhttperr = http_errorstr(errsrc, errcode);
+			}
+		}
+		switch (my_errno) {
+		case EINTR:
+		case ETIMEDOUT:
+				/* Timed out.  Try, try again */
+			ret = WEB_TIMEOUT;
+			break;
+		case ECONNREFUSED:
+			ret = WEB_CONNREFUSED;
+			break;
+		case EHOSTDOWN:
+			ret = WEB_HOSTDOWN;
+			break;
+		default:
+				/* some other fatal error */
+			ret = WEB_NOCONNECT;
+			if (libhttperr == NULL) {
+				pkgerr_add(err, PKGERR_WEB,
+				    gettext(ERR_INIT_CONN),
+				    ps->url.hport.hostname);
+			} else {
+				pkgerr_add(err, PKGERR_WEB,
+				    gettext(ERR_HTTP), libhttperr);
+			}
+			break;
+		}
+	}
+cleanup:
+	return (ret);
+}
+
+/*
+ * Name:		write_ca_file
+ * Description:	Writes out a PKCS12 file containing all trusted certs
+ *		found in keystore recorded in static 'ps' structure
+ *
+ *		This routine is used because the libwanboot library's
+ *		HTTPS routines cannot accept trusted certificates
+ *		through an in-memory structure, when initiating an
+ *		SSL connection.  They must be in a PKCS12, which is
+ *		admittedly a poor interface.
+ *
+ * Arguments:	err - where to record any errors.
+ *     		tmpdir - Directory to write certificate file in
+ *		cacerts - Certs to write out
+ *		passwd - password used to encrypt certs
+ *
+ * Returns :	path to resulting file, if successfullly written,
+ *		otherwise NULL.
+ */
+static char
+*write_ca_file(PKG_ERR *err, char *tmpdir, STACK_OF(X509) *cacerts,
+    char *passwd)
+{
+	int fd, len;
+	FILE *fp;
+	PKCS12	*p12 = NULL;
+	char *ret = NULL;
+	static char tmp_file[PATH_MAX] = "";
+	struct stat buf;
+
+	if (!path_valid(tmpdir)) {
+		pkgerr_add(err, PKGERR_WEB, gettext(MSG_NOTEMP), tmpdir);
+		goto cleanup;
+	}
+
+	/* mkstemp replaces XXXXXX with a unique string */
+	if (((len = snprintf(tmp_file, PATH_MAX, "%s/%sXXXXXX", tmpdir,
+	    "cert")) < 0) ||
+	    (len >= PATH_MAX)) {
+		pkgerr_add(err, PKGERR_WEB, gettext(MSG_NOTEMP), tmpdir);
+		goto cleanup;
+	}
+
+	if ((fd = mkstemp(tmp_file)) == -1) {
+		pkgerr_add(err, PKGERR_WEB, gettext(MSG_NOTMPFIL), tmp_file);
+		goto cleanup;
+	}
+
+	if (fstat(fd, &buf) == -1) {
+		pkgerr_add(err, PKGERR_WEB, gettext(MSG_NOTMPFIL), tmp_file);
+		goto cleanup;
+	}
+
+	if (!S_ISREG(buf.st_mode)) {
+		pkgerr_add(err, PKGERR_WEB, gettext(MSG_NOTMPFIL), tmp_file);
+		goto cleanup;
+	}
+
+	if ((fp = fdopen(fd, "w")) == NULL) {
+		pkgerr_add(err, PKGERR_WEB, gettext(MSG_NOTMPFIL), tmp_file);
+		goto cleanup;
+	}
+
+	if ((p12 = sunw_PKCS12_create(passwd, NULL, NULL, cacerts)) == NULL) {
+		pkgerr_add(err, PKGERR_WEB,
+		    gettext(ERR_KEYSTORE_FORM), tmp_file);
+		goto cleanup;
+	}
+
+	if (i2d_PKCS12_fp(fp, p12) == 0) {
+		pkgerr_add(err, PKGERR_WEB,
+		    gettext(ERR_KEYSTORE_FORM), tmp_file);
+		goto cleanup;
+	}
+
+	(void) fflush(fp);
+	(void) fclose(fp);
+	(void) close(fd);
+	fp = NULL;
+	fd = -1;
+	ret = tmp_file;
+
+cleanup:
+	if (p12 != NULL)
+		PKCS12_free(p12);
+	if (fp != NULL)
+		(void) fclose(fp);
+	if (fd != -1) {
+		(void) close(fd);
+		(void) unlink(tmp_file);
+	}
+
+	return (ret);
+}
+
+/*
+ * Name:		web_send_request
+ * Description:	Sends an HTTP request for a file to the
+ *		web server being communicated with in the static
+ *		'ps' structure
+ *
+ * Arguments:	err - where to record any errors.
+ *		request_type - HTTP_REQ_TYPE_HEAD to send an HTTP HEAD request,
+ *		or HTTP_REQ_TYPE_GET to send an HTTP GET request
+ *		cp -
+ * Returns :   	WEB_OK - request sent successfully
+ *		WEB_CONNREFUSED - Connection was refused to web site
+ *		WEB_HOSTDOWN - Host was not responding to request
+ *		WEB_NOCONNECT - Some other connection failure
+ */
+static WebStatus
+web_send_request(PKG_ERR *err, int request_type, int cp, int ep)
+{
+	WebStatus ret = WEB_OK;
+	ulong_t		errcode;
+	uint_t		errsrc;
+	int		my_errno = 0;
+	const char		*libhttperr = NULL;
+	switch (request_type) {
+	case HTTP_REQ_TYPE_HEAD:
+		if ((http_head_request(ps->hps, ps->url.abspath)) != 0) {
+			while ((errcode = http_get_lasterr(ps->hps,
+			    &errsrc)) != 0) {
+				/* Have an error - is it EINTR? */
+			    if (errsrc == ERRSRC_SYSTEM) {
+				    my_errno = errcode;
+				    break;
+			    } else if (libhttperr == NULL) {
+				    /* save first non-system error message */
+				    libhttperr =
+					http_errorstr(errsrc, errcode);
+			    }
+			}
+			switch (my_errno) {
+			    case EINTR:
+			case ETIMEDOUT:
+				/* Timed out.  Try, try again */
+				ret = WEB_TIMEOUT;
+				break;
+			case ECONNREFUSED:
+				ret = WEB_CONNREFUSED;
+				break;
+			case EHOSTDOWN:
+				ret = WEB_HOSTDOWN;
+				break;
+			default:
+				/* some other fatal error */
+				ret = WEB_NOCONNECT;
+				if (libhttperr == NULL) {
+					pkgerr_add(err, PKGERR_WEB,
+					    gettext(ERR_INIT_CONN),
+					    ps->url.hport.hostname);
+				} else {
+					pkgerr_add(err, PKGERR_WEB,
+					    gettext(ERR_HTTP), libhttperr);
+				}
+				break;
+			}
+			goto cleanup;
+			}
+		break;
+
+	case HTTP_REQ_TYPE_GET:
+		if (cp && ep) {
+			if (http_get_range_request(ps->hps, ps->url.abspath,
+			    cp, ep - cp) != 0) {
+				while ((errcode = http_get_lasterr(ps->hps,
+				    &errsrc)) != 0) {
+					/* Have an error - is it EINTR? */
+					if (errsrc == ERRSRC_SYSTEM) {
+						my_errno = errcode;
+						break;
+					} else {
+						/*
+						 * save first non-system
+						 * error message
+						 */
+						libhttperr =
+						    http_errorstr(errsrc,
+							errcode);
+					}
+				}
+				switch (my_errno) {
+				case EINTR:
+				case ETIMEDOUT:
+					/* Timed out.  Try, try again */
+					ret = WEB_TIMEOUT;
+					break;
+				case ECONNREFUSED:
+					ret = WEB_CONNREFUSED;
+					break;
+				case EHOSTDOWN:
+					ret = WEB_HOSTDOWN;
+					break;
+				default:
+					/* some other fatal error */
+					ret = WEB_NOCONNECT;
+					if (libhttperr == NULL) {
+						pkgerr_add(err, PKGERR_WEB,
+						    gettext(ERR_INIT_CONN),
+						    ps->url.hport.hostname);
+					} else {
+						pkgerr_add(err, PKGERR_WEB,
+						    gettext(ERR_HTTP),
+						    libhttperr);
+					}
+					break;
+				}
+				goto cleanup;
+			}
+
+			if (!web_eval_headers(err)) {
+				ret = WEB_NOCONNECT;
+				goto cleanup;
+			}
+		} else {
+			if ((http_get_request(ps->hps, ps->url.abspath))
+					!= 0) {
+				while ((errcode = http_get_lasterr(ps->hps,
+				    &errsrc)) != 0) {
+					/* Have an error - is it EINTR? */
+					if (errsrc == ERRSRC_SYSTEM) {
+						my_errno = errcode;
+						break;
+					} else {
+						/*
+						 * save the first non-system
+						 * error message
+						 */
+						libhttperr =
+						    http_errorstr(errsrc,
+							errcode);
+					}
+				}
+				switch (my_errno) {
+				case EINTR:
+				case ETIMEDOUT:
+					/* Timed out.  Try, try again */
+					ret = WEB_TIMEOUT;
+					break;
+				case ECONNREFUSED:
+					ret = WEB_CONNREFUSED;
+					break;
+				case EHOSTDOWN:
+					ret = WEB_HOSTDOWN;
+					break;
+				default:
+					/* some other fatal error */
+					ret = WEB_NOCONNECT;
+					if (libhttperr == NULL) {
+						pkgerr_add(err, PKGERR_WEB,
+						    gettext(ERR_INIT_CONN),
+						    ps->url.hport.hostname);
+					} else {
+						pkgerr_add(err, PKGERR_WEB,
+						    gettext(ERR_HTTP),
+						    libhttperr);
+					}
+					break;
+				}
+				goto cleanup;
+			}
+
+			if (!web_eval_headers(err)) {
+				ret = WEB_NOCONNECT;
+				goto cleanup;
+			}
+		}
+		break;
+	default:
+		pkgerr_add(err, PKGERR_INTERNAL, gettext(ERR_PKG_INTERNAL),
+		    __FILE__, __LINE__);
+	}
+
+cleanup:
+	return (ret);
+}
+
+/*
+ * Name:		web_eval_headers
+ * Description:	Evaluates HTTP headers returned during an HTTP request.
+ *		This must be called before calling
+ *		http_get_header_value().
+ *
+ * Arguments:	err - where to record any errors.
+ *
+ * Returns :	B_TRUE - success, B_FALSE otherwise
+ */
+static boolean_t
+web_eval_headers(PKG_ERR *err)
+{
+	const char *http_err;
+	ulong_t herr;
+	uint_t errsrc;
+
+	if (http_process_headers(ps->hps, &ps->resp) != 0) {
+		if ((ps->resp != NULL) && (ps->resp->statusmsg != NULL)) {
+			pkgerr_add(err, PKGERR_WEB, gettext(ERR_HTTP),
+			    ps->resp->statusmsg);
+		}
+
+		herr = http_get_lasterr(ps->hps, &errsrc);
+		http_err = http_errorstr(errsrc, herr);
+		pkgerr_add(err, PKGERR_WEB, gettext(ERR_HTTP),
+		    http_err);
+		return (B_FALSE);
+	}
+	return (B_TRUE);
+}
+
+/*
+ * Name:		web_get_file
+ * Description:	Downloads the file URL from the website, all of
+ *		which are recorded in the static 'ps' struct
+ *
+ * Arguments:	err - where to record any errors.
+ *		dwnld_dir - Directory to download file into
+ *		device - Where to store path to resulting
+ *			file
+ *		nointeract - if non-zero, do not output
+ *		progress
+ *		fname - name of downloaded file link in the dwnld_dir
+ *
+ * Returns :   	WEB_OK - download successful
+ *		WEB_CONNREFUSED - Connection was refused to web site
+ *		WEB_HOSTDOWN - Host was not responding to request
+ *		WEB_GET_FAIL - Unable to initialize download
+ *		state (temp file creation, header parsing, etc)
+ *		WEB_NOCONNECT - Some other connection failure
+ */
+static WebStatus
+web_get_file(PKG_ERR *err, char *dwnld_dir, int nointeract, char **fname)
+{
+	int		i, fd;
+	int		n = 0;
+	ulong_t		abs_pos = 0;
+	char		*head_val = NULL;
+	char		*lastmod_val = NULL;
+	char		*bname = NULL;
+	struct stat	status;
+	WebStatus	ret = WEB_OK;
+	WebStatus	req_ret;
+	ulong_t		errcode;
+	uint_t		errsrc;
+	int		my_errno = 0;
+	const char	*libhttperr = NULL;
+	char		*disp;
+	char		tmp_file[PATH_MAX];
+	int		len;
+
+	ps->data.prev_cont_length =
+	ps->data.content_length =
+	ps->data.cur_pos = 0;
+
+	if ((head_val = http_get_header_value(ps->hps,
+	    CONTENT_LENGTH_HDR)) != NULL) {
+		ps->data.content_length = atol(head_val);
+	} else {
+		pkgerr_add(err, PKGERR_WEB, gettext(ERR_NO_HEAD_VAL),
+		    CONTENT_LENGTH_HDR);
+		ret = WEB_GET_FAIL;
+		goto cleanup;
+	}
+
+	free(head_val);
+	head_val = NULL;
+
+	if ((head_val = http_get_header_value(ps->hps,
+	    CONTENT_DISPOSITION_HDR)) != NULL) {
+		/* "inline; parm=val; parm=val */
+		if ((disp = strtok(head_val, "; \t\n\f\r")) != NULL) {
+			/* disp = "inline" */
+			while ((disp = strtok(NULL, "; \t\n\f\r")) != NULL) {
+				/* disp = "parm=val" */
+				if (ci_strneq(disp, "filename=", 9)) {
+					bname = xstrdup(basename(disp + 9));
+					trim(bname);
+					dequote(bname);
+				}
+			}
+		}
+		free(head_val);
+		head_val = NULL;
+	}
+
+	if (bname == NULL) {
+		/*
+		 * couldn't determine filename from header value,
+		 * so take basename of URL
+		 */
+		if ((bname = get_endof_string(ps->url.abspath, '/')) == NULL) {
+			/* URL is bad */
+			pkgerr_add(err, PKGERR_PARSE,
+			    gettext(ERR_PARSE_URL), ps->url.abspath);
+			ret = WEB_GET_FAIL;
+			goto cleanup;
+		}
+	}
+
+	*fname = bname;
+
+	if ((head_val = http_get_header_value(ps->hps, LAST_MODIFIED_HDR))
+			!= NULL) {
+
+		if ((lastmod_val = condense_lastmodified(head_val)) == NULL) {
+			pkgerr_add(err, PKGERR_WEB, gettext(ERR_BAD_HEAD_VAL),
+			    LAST_MODIFIED_HDR, head_val);
+			ret = WEB_GET_FAIL;
+			goto cleanup;
+		}
+		free(head_val);
+		head_val = NULL;
+
+		if ((ps->uniqfile = get_unique_filename(dwnld_dir,
+		    lastmod_val)) == NULL) {
+			pkgerr_add(err, PKGERR_WEB, gettext(ERR_OPEN_TMP));
+			ret = WEB_GET_FAIL;
+			goto cleanup;
+		}
+
+		free(lastmod_val);
+		lastmod_val = NULL;
+
+		if ((fd = open(ps->uniqfile,
+		    O_NONBLOCK|O_RDWR|O_APPEND|O_CREAT|O_EXCL,
+		    640)) == -1) {
+
+			/*
+			 * A partial downloaded file
+			 * already exists, so open it.
+			 */
+			if ((fd = open(ps->uniqfile,
+			    O_NONBLOCK|O_RDWR|O_APPEND)) != -1) {
+				if (fstat(fd, &status) == -1 ||
+				    !S_ISREG(status.st_mode)) {
+					pkgerr_add(err, PKGERR_WEB,
+					    gettext(ERR_DWNLD_NO_CONT),
+					    ps->uniqfile);
+					ret = WEB_GET_FAIL;
+					goto cleanup;
+				} else {
+					echo_out(nointeract,
+					    gettext(MSG_DWNLD_PART),
+					    ps->uniqfile,
+					    status.st_size);
+					ps->data.prev_cont_length =
+					    status.st_size;
+				}
+			} else {
+				/* unable to open partial file */
+				pkgerr_add(err, PKGERR_WEB,
+				    gettext(ERR_DWNLD_NO_CONT),
+				    ps->uniqfile);
+				ret = WEB_GET_FAIL;
+				goto cleanup;
+			}
+		}
+	} else {
+		/*
+		 * no "Last-Modified" header, so this file is not eligible for
+		 * spooling and "resuming last download" operations
+		 */
+		ps->spool = B_FALSE;
+
+		/* mkstemp replaces XXXXXX with a unique string */
+		if (((len = snprintf(tmp_file, PATH_MAX,
+		    "%s/%sXXXXXX", dwnld_dir, "stream")) < 0) ||
+		    (len >= PATH_MAX)) {
+			pkgerr_add(err, PKGERR_WEB,
+			    gettext(MSG_NOTEMP), dwnld_dir);
+			ret = WEB_GET_FAIL;
+			goto cleanup;
+		}
+
+		if ((fd = mkstemp(tmp_file)) == -1) {
+			pkgerr_add(err, PKGERR_WEB,
+			    gettext(MSG_NOTMPFIL), tmp_file);
+			ret = WEB_GET_FAIL;
+			goto cleanup;
+		}
+
+		if (fstat(fd, &status) == -1 ||
+		    !S_ISREG(status.st_mode)) {
+			pkgerr_add(err, PKGERR_WEB,
+			    gettext(ERR_DWNLD_NO_CONT),
+			    ps->uniqfile);
+			ret = WEB_GET_FAIL;
+			goto cleanup;
+		}
+
+		ps->data.prev_cont_length = 0;
+		ps->uniqfile = xstrdup(tmp_file);
+	}
+
+	/* File has already been completely downloaded */
+	if (ps->data.prev_cont_length == ps->data.content_length) {
+		echo_out(nointeract, gettext(MSG_DWNLD_PREV), ps->uniqfile);
+		ps->data.cur_pos = ps->data.prev_cont_length;
+		if (!make_link(dwnld_dir, bname)) {
+			pkgerr_add(err, PKGERR_WEB, gettext(MSG_NOTEMP),
+			    dwnld_dir);
+			ret = WEB_GET_FAIL;
+			goto cleanup;
+		}
+		/* we're done, so cleanup and return success */
+		goto cleanup;
+	} else if (ps->data.prev_cont_length != 0) {
+		ps->data.cur_pos = ps->data.prev_cont_length;
+	}
+
+	if (!ck_dwnld_dir_space(err, dwnld_dir,
+	    (ps->data.prev_cont_length != 0) ?
+	    (ps->data.content_length - ps->data.cur_pos) :
+	    ps->data.content_length)) {
+		ret = WEB_GET_FAIL;
+		goto cleanup;
+	}
+
+	if ((req_ret = web_send_request(err, HTTP_REQ_TYPE_GET,
+	    ps->data.cur_pos, ps->data.content_length)) != WEB_OK) {
+		ret = req_ret;
+		goto cleanup;
+	}
+
+	if (ps->data.prev_cont_length != 0)
+		echo_out(nointeract, gettext(MSG_DWNLD_CONT));
+	else
+		echo_out(nointeract, gettext(MSG_DWNLD));
+
+	progress_setup(nointeract, ps->data.content_length);
+
+	/* Download the file a BLOCK at a time */
+	while (ps->data.cur_pos < ps->data.content_length) {
+		progress_report(nointeract, abs_pos);
+		i = ((ps->data.content_length - ps->data.cur_pos) < BLOCK) ?
+		    (ps->data.content_length - ps->data.cur_pos)
+				: BLOCK;
+		if ((n = http_read_body(ps->hps, ps->content, i)) <= 0) {
+			while ((errcode = http_get_lasterr(ps->hps,
+			    &errsrc)) != 0) {
+				/* Have an error - is it EINTR? */
+				if (errsrc == ERRSRC_SYSTEM) {
+					my_errno = errcode;
+					break;
+				} else {
+					/*
+					 * save first non-system
+					 * error message
+					 */
+					libhttperr =
+					    http_errorstr(errsrc, errcode);
+				}
+			}
+			switch (my_errno) {
+			case EINTR:
+			case ETIMEDOUT:
+				/* Timed out.  Try, try again */
+				ret = WEB_TIMEOUT;
+				break;
+			case ECONNREFUSED:
+				ret = WEB_CONNREFUSED;
+				break;
+			case EHOSTDOWN:
+				ret = WEB_HOSTDOWN;
+				break;
+			default:
+				/* some other fatal error */
+				ret = WEB_NOCONNECT;
+				if (libhttperr == NULL) {
+					pkgerr_add(err, PKGERR_WEB,
+					    gettext(ERR_INIT_CONN),
+					    ps->url.hport.hostname);
+				} else {
+					pkgerr_add(err, PKGERR_WEB,
+					    gettext(ERR_HTTP), libhttperr);
+				}
+				break;
+			}
+			goto cleanup;
+		}
+		if ((n = write(fd, ps->content, n)) == 0) {
+			pkgerr_add(err, PKGERR_WEB, gettext(ERR_WRITE),
+			    ps->uniqfile, strerror(errno));
+			ret = WEB_GET_FAIL;
+			goto cleanup;
+		}
+		ps->data.cur_pos += n;
+		abs_pos += n;
+	}
+
+	progress_finish(nointeract);
+	echo_out(nointeract, gettext(MSG_DWNLD_COMPLETE));
+
+	if (!make_link(dwnld_dir, bname)) {
+		pkgerr_add(err, PKGERR_WEB, gettext(MSG_NOTEMP),
+		    dwnld_dir);
+		ret = WEB_GET_FAIL;
+		goto cleanup;
+	}
+
+cleanup:
+	sync();
+	if (fd != -1) {
+		(void) close(fd);
+	}
+
+	if (head_val != NULL)
+		free(head_val);
+
+	if (lastmod_val != NULL)
+		free(lastmod_val);
+
+	return (ret);
+}
+
+/*
+ * Name:		make_link
+ * Description:	Create new link to file being downloaded
+ *
+ * Arguments:	dwnld_dir - directory in which downloaded file exists
+ *		bname - name of link
+ *
+ * Returns :	B_TRUE - success, B_FALSE otherwise
+ */
+static boolean_t
+make_link(char *dwnld_dir, char *bname)
+{
+	int len;
+
+	if ((ps->link = (char *)xmalloc(PATH_MAX)) == NULL)
+		return (B_FALSE);
+	if (((len = snprintf(ps->link, PATH_MAX, "%s/%s",
+	    dwnld_dir, bname)) < 0) ||
+	    len >= PATH_MAX)
+		return (B_FALSE);
+
+	(void) link(ps->uniqfile, ps->link);
+
+	return (B_TRUE);
+}
+
+/*
+ * Name:		get_startof_string
+ * Description:	searches string for token, returns a newly-allocated
+ *		substring of the given string up to, but not
+ *		including, token.  for example
+ *		get_startof_string("abcd", 'c') will return "ab"
+ *
+ * Arguments:	path - path to split
+ *     		token - character to split on
+ *
+ * Returns :	substring of 'path', up to, but not including,
+ *		token, if token appears in path.  Otherwise,
+ *		returns NULL.
+ */
+char *
+get_startof_string(char *path, char token)
+{
+	char *p, *p2;
+
+	if (path == NULL)
+		return (NULL);
+
+	p = xstrdup(path);
+
+	p2 = strchr(p, token);
+	if (p2 == NULL) {
+		free(p);
+		return (NULL);
+	} else {
+		*p2 = '\0';
+		return (p);
+	}
+}
+
+/*
+ * Name:		get_endof_string
+ * Description:	searches string for token, returns a
+ *		newly-allocated substring of the given string,
+ *		starting at character following token, to end of
+ *		string.
+ *
+ *		for example get_end_string("abcd", 'c')
+ *		will return "d"
+ *
+ * Arguments:	path - path to split
+ *     		token - character to split on
+ *
+ * Returns :	substring of 'path', beginning at character
+ *		following token, to end of string, if
+ *		token appears in path.  Otherwise,
+ * returns NULL.
+ */
+char *
+get_endof_string(char *path, char token)
+{
+	char *p, *p2;
+
+	if (path == NULL)
+		return (NULL);
+
+	p = xstrdup(path);
+
+	if ((p2 = strrchr(p, token)) == NULL) {
+		return (NULL);
+	}
+
+	return (p2 + 1);
+}
+
+/*
+ * Name:		progress_setup
+ * Description:	Initialize session for reporting progress
+ *
+ * Arguments:	nointeract - if non-zero, do not do anything
+ *		ulong_t - size of job to report progress for
+ *
+ * Returns :	none
+ */
+static void
+progress_setup(int nointeract, ulong_t size_of_load)
+{
+	ulong_t divisor;
+	ulong_t term_width = TERM_WIDTH;
+
+	if (nointeract)
+		return;
+
+	if (size_of_load > MED_DWNLD && size_of_load < LARGE_DWNLD)
+		divisor = MED_DIVISOR;
+	else if (size_of_load > LARGE_DWNLD) {
+		term_width = TERM_WIDTH - 8;
+		divisor = LARGE_DIVISOR;
+	} else
+		divisor = SMALL_DIVISOR;
+
+	const_increment = size_of_load / term_width;
+	const_divider = size_of_load / divisor;
+	const_completed = 100 / divisor;
+}
+
+/*
+ * Name:		progress_report
+ * Description:	Report progress for current progress context,
+ *		to stderr
+ *
+ * Arguments:	nointeract - if non-zero, do not do anything
+ *		position - how far along in the job to report.
+ *		This should be <= size used during progress_setup
+ *
+ * Returns :	none
+ */
+static void
+progress_report(int nointeract, ulong_t position)
+{
+	static ulong_t increment;
+	static ulong_t divider;
+
+	if (nointeract)
+		return;
+
+	if (position == 0) {
+		increment = const_increment;
+		divider = const_divider;
+	}
+	if (position > increment && position < divider) {
+		(void) putc('.', stderr);
+		increment += const_increment;
+	} else if (position > divider) {
+		completed += const_completed;
+		(void) fprintf(stderr, "%ld%c", completed, '%');
+		increment += const_increment;
+		divider += const_divider;
+	}
+}
+
+/*
+ * Name:		progress_finish
+ * Description:	Finalize session for reporting progress.
+ *		"100%" is reported to screen
+ *
+ * Arguments:	nointeract - if non-zero, do not do anything
+ *
+ * Returns :	none
+ */
+static void
+progress_finish(int nointeract)
+{
+	if (nointeract)
+		return;
+
+	(void) fprintf(stderr, "%d%c\n", 100, '%');
+}
+
+/*
+ * Name:		init_session
+ * Description:	Initializes static 'ps' structure with default
+ *		values
+ *
+ * Arguments:	none
+ *
+ * Returns :	B_TRUE - success, B_FALSE otherwise
+ */
+static boolean_t
+init_session(void)
+{
+	if ((ps = (WEB_SESSION *)
+		xmalloc(sizeof (WEB_SESSION))) == NULL) {
+		return (B_FALSE);
+	}
+	(void) memset(ps, 0, sizeof (*ps));
+
+	if ((ps->content = (char *)xmalloc(BLOCK)) == NULL) {
+		return (B_FALSE);
+	}
+
+	(void) memset(ps->content, 0, BLOCK);
+
+	ps->data.cur_pos = 0UL;
+	ps->data.content_length = 0UL;
+	ps->url.https = B_FALSE;
+	ps->uniqfile = NULL;
+	ps->link = NULL;
+	ps->dwnld_dir = NULL;
+	ps->spool = B_TRUE;
+	ps->errstr = NULL;
+	ps->keystore = NULL;
+
+	return (B_TRUE);
+}
+
+/*
+ * Name:		ck_downld_dir_space
+ * Description:	Verify enough space exists in directory to hold file
+ *
+ * Arguments:	err - where to record any errors.
+ *     		dwnld_dir - Directory to check available space in
+ *		bytes_needed - How many bytes are need
+ *
+ * Returns :	B_TRUE - enough space exists in dwnld_dir to hold
+ *		bytes_needed bytes, otherwise B_FALSE
+ */
+static boolean_t
+ck_dwnld_dir_space(PKG_ERR *err, char *dwnld_dir, ulong_t bytes_needed)
+{
+	u_longlong_t bytes_avail;
+	u_longlong_t block_pad;
+	struct statvfs64 status;
+
+	if (statvfs64(dwnld_dir, &status)) {
+		pkgerr_add(err, PKGERR_WEB, gettext(ERR_TMPDIR), dwnld_dir);
+		return (B_FALSE);
+	}
+
+	block_pad = (status.f_frsize ? status.f_frsize : status.f_bsize);
+	bytes_avail = status.f_bavail * block_pad;
+
+	if ((((u_longlong_t)bytes_needed) + block_pad) > bytes_avail) {
+		pkgerr_add(err, PKGERR_WEB, gettext(ERR_DISK_SPACE),
+		    dwnld_dir,
+		    (((u_longlong_t)bytes_needed) + block_pad) / 1024ULL,
+		    bytes_avail / 1024ULL);
+		return (B_FALSE);
+	}
+
+	return (B_TRUE);
+}
+
+/*
+ * Description:
+ *    This function returns a unique file name based on the parts of the
+ *    URI. This is done to enable partially downloaded files to be resumed.
+ * Arguments:
+ *    dir - The directory that should contain the filename.
+ *    last_modified - A string representing the date of last modification,
+ *	used as part of generating unique name
+ * Returns:
+ *    A valid filename or NULL.
+ */
+
+static char *
+get_unique_filename(char *dir, char *last_modified)
+{
+	char *buf, *buf2, *beg_str;
+	int len;
+
+	if ((buf = (char *)xmalloc(PATH_MAX)) == NULL) {
+		return (NULL);
+	}
+	if ((buf2 = (char *)xmalloc(PATH_MAX)) == NULL) {
+		return (NULL);
+	}
+
+	/* prepare strings for being cat'ed onto */
+	buf[0] = buf2[0] = '\0';
+	/*
+	 * No validation of the path is done here. We just construct the path
+	 * and it must be validated later
+	 */
+
+	if (dir) {
+		if (((len = snprintf(buf2, PATH_MAX, "%s/", dir)) < 0) ||
+		    (len >= PATH_MAX))
+			return (NULL);
+	} else {
+		return (NULL);
+	}
+
+	if (ps->url.abspath)
+		if (strlcat(buf, ps->url.abspath, PATH_MAX) >= PATH_MAX)
+			return (NULL);
+	if (ps->url.hport.hostname)
+		if (isdigit((int)ps->url.hport.hostname[0])) {
+			if (strlcat(buf, ps->url.hport.hostname, PATH_MAX)
+					>= PATH_MAX)
+				return (NULL);
+		} else {
+			if ((beg_str =
+				get_startof_string(ps->url.hport.hostname, '.'))
+					!= NULL)
+				if (strlcat(buf, beg_str, PATH_MAX) >= PATH_MAX)
+					return (NULL);
+		}
+	if (last_modified != NULL)
+		if (strlcat(buf, last_modified, PATH_MAX) >= PATH_MAX)
+			return (NULL);
+
+	if ((buf = replace_token(buf, '/', '_')) != NULL) {
+		if (strlcat(buf2, buf, PATH_MAX) >= PATH_MAX) {
+			return (NULL);
+		} else {
+			if (buf) free(buf);
+			return (buf2);
+		}
+	} else {
+		if (buf) free(buf);
+		if (buf2) free(buf2);
+		return (NULL);
+	}
+}
+
+/*
+ * Description:
+ *    Removes token(s) consisting of one character from any path.
+ * Arguments:
+ *    path  - The path to search for the token in.
+ *    token - The token to search for
+ * Returns:
+ *    The path with all tokens removed or NULL.
+ */
+static char *
+replace_token(char *path, char oldtoken, char newtoken)
+{
+	char *newpath, *p;
+
+	if ((path == NULL) || (oldtoken == '\0') || (newtoken == '\0')) {
+		return (NULL);
+	}
+
+	newpath = xstrdup(path);
+
+	for (p = newpath; *p != '\0'; p++) {
+		if (*p == oldtoken) {
+			*p = newtoken;
+		}
+	}
+
+	return (newpath);
+}
+
+/*
+ * Name:        trim
+ * Description: Trims whitespace from a string
+ *              has been registered)
+ * Scope:       private
+ * Arguments:   string  - string to trim.  It is assumed
+ *              this string is writable up to it's entire
+ *              length.
+ * Returns:     none
+ */
+static void
+trim(char *str)
+{
+	int len, i;
+	if (str == NULL) {
+		return;
+	}
+
+	len = strlen(str);
+	/* strip from front */
+	while (isspace(*str)) {
+		for (i = 0; i < len; i++) {
+			str[i] = str[i+1];
+		}
+	}
+
+	/* strip from back */
+	len = strlen(str);
+	while (isspace(str[len-1])) {
+		len--;
+	}
+	str[len] = '\0';
+}
+
+/*
+ * Description:
+ *    Resolves double quotes
+ * Arguments:
+ *    str  - The string to resolve
+ * Returns:
+ *    None
+ */
+static void
+dequote(char *str)
+{
+	char *cp;
+
+	if ((str == NULL) || (str[0] != '"')) {
+		/* no quotes */
+		return;
+	}
+
+	/* remove first quote */
+	memmove(str, str + 1, strlen(str) - 1);
+
+	/*
+	 * scan string looking for ending quote.
+	 * escaped quotes like \" don't count
+	 */
+	cp = str;
+
+	while (*cp != '\0') {
+		switch (*cp) {
+		case '\\':
+			/* found an escaped character */
+			/* make sure end of string is not '\' */
+			if (*++cp != '\0') {
+				cp++;
+			}
+			break;
+
+		case '"':
+			*cp = '\0';
+			break;
+		default:
+			cp++;
+		}
+	}
+}
+
+/*
+ * Name:		get_ENV_proxy
+ * Description:	Retrieves setting of proxy env variable
+ *
+ * Arguments:	err - where to record any errors.
+ *		proxy - where to store proxy
+ *
+ * Returns :	B_TRUE - http proxy was found and valid, stored in proxy
+ *		B_FALSE - error, errors recorded in err
+ */
+static boolean_t
+get_ENV_proxy(PKG_ERR *err, char **proxy)
+{
+	char *buf;
+
+	if ((buf = getenv("HTTPPROXY")) != NULL) {
+		if (!path_valid(buf)) {
+			pkgerr_add(err, PKGERR_WEB,
+			    gettext(ERR_ILL_ENV), "HTTPPROXY", buf);
+			return (B_FALSE);
+		} else {
+			*proxy = buf;
+			return (B_TRUE);
+		}
+	} else {
+		/* try the other env variable */
+		if ((buf = getenv("http_proxy")) != NULL) {
+			if (!path_valid(buf)) {
+				pkgerr_add(err, PKGERR_WEB,
+				    gettext(ERR_ILL_ENV), "http_proxy", buf);
+				return (B_FALSE);
+			}
+			if (!strneq(buf, "http://", 7)) {
+				pkgerr_add(err, PKGERR_WEB,
+				    gettext(ERR_ILL_ENV), "http_proxy", buf);
+				return (B_FALSE);
+			}
+
+			/* skip over the http:// part of the proxy "url" */
+			    *proxy = buf + 7;
+			    return (B_TRUE);
+		}
+	}
+
+	/* either the env variable(s) were set and valid, or not set */
+	return (B_TRUE);
+}
+
+/*
+ * Name:		get_ENV_proxyport
+ * Description:	Retrieves setting of PROXYPORT env variable
+ *
+ * Arguments:	err - where to record any errors.
+ *		port - where to store resulting port
+ *
+ * Returns :	B_TRUE - string found in PROXYPORT variable, converted
+ *		to decimal integer, if it exists
+ *		and is valid.  Or, PROXYPORT not set, port set to 1.
+ *		B_FALSE - env variable set, but invalid
+ *			(not a number for example)
+ */
+static boolean_t
+get_ENV_proxyport(PKG_ERR *err, ushort_t *port)
+{
+	char *buf;
+	ushort_t	newport;
+	buf = getenv("HTTPPROXYPORT");
+	if (buf != NULL) {
+		if (!path_valid(buf)) {
+			pkgerr_add(err, PKGERR_WEB,
+			    gettext(ERR_ILL_ENV), "HTTPPROXYPORT", buf);
+			return (B_FALSE);
+		}
+		if ((newport = atoi(buf)) == 0) {
+			pkgerr_add(err, PKGERR_WEB,
+			    gettext(ERR_ILL_ENV), "HTTPPROXYPORT", buf);
+			return (B_FALSE);
+		}
+		*port = newport;
+		return (B_TRUE);
+	} else {
+		*port = 1;
+		return (B_TRUE);
+	}
+}
+
+/*
+ * Name:		remove_dwnld_file
+ * Description:	Removes newly-downloaded file if completely downloaded.
+ *
+ * Arguments:	path - path to file to remove
+ *
+ * Returns :	B_TRUE - success, B_FALSE otherwise
+ *		if it's '0' (not OK) we simply return it, since the
+ *		verification operation has already determined that the
+ *		cert is invalid.  if 'ok' is non-zero, then we do our
+ *		checks, and return 0 or 1 based on if the cert is
+ *		invalid or valid.
+ */
+static boolean_t
+remove_dwnld_file(char *path)
+{
+	if (path && path != NULL) {
+		/*
+		 * Only remove the downloaded file if it has been completely
+		 * downloaded, or is not eligible for spooling
+		 */
+		if ((!ps->spool) ||
+		    (ps->data.cur_pos  >= ps->data.content_length)) {
+			(void) unlink(path);
+		}
+	} else {
+		return (B_FALSE);
+	}
+	return (B_TRUE);
+}
+
+/*
+ * Name:		condense_lastmodifided
+ * Description:	generates a substring of a last-modified string,
+ *		and removes colons.
+ *
+ * Arguments:	last_modified - string of the form
+ *		"Wed, 23 Oct 2002 21:59:45 GMT"
+ *
+ * Returns :
+ *		new string, consisting of hours/minutes/seconds only,
+ *		sans any colons.
+ */
+char *
+condense_lastmodified(char *last_modified)
+{
+	char *p, *p2;
+
+	/*
+	 * Last-Modified: Wed, 23 Oct 2002 21:59:45 GMT
+	 * Strip the hours, minutes and seconds, without the ':'s, from
+	 * the above string, void of the ':".
+	 */
+
+	if (last_modified == NULL)
+		return (NULL);
+
+	if ((p = xstrdup(last_modified)) == NULL)
+		return (NULL);
+	p2 = (strstr(p, ":") - 2);
+	p2[8] = '\0';
+	return (replace_token(p2, ':', '_'));
+}
+
+/*
+ * Name:		backoff
+ * Description:	sleeps for a certain # of seconds after a network
+ *		failure.
+ * Scope:	public
+ * Arguments:	none
+ * Returns:	none
+ */
+void
+backoff()
+{
+	static boolean_t initted = B_FALSE;
+	int backoff;
+	long seed;
+
+	if (!initted) {
+		/* seed the rng */
+		(void) _get_random_info(&seed, sizeof (seed));
+		srand48(seed);
+		initted = B_TRUE;
+	}
+
+	backoff = drand48() * (double)cur_backoff;
+	(void) sleep(backoff);
+	if (cur_backoff < MAX_BACKOFF) {
+		/*
+		 * increase maximum time we might wait
+		 * next time so as to fall off over
+		 * time.
+		 */
+		cur_backoff *= BACKOFF_FACTOR;
+	}
+}
+
+/*
+ * Name:		reset_backoff
+ * Description:	notifies the backoff service that whatever was
+ *		being backoff succeeded.
+ * Scope:	public
+ * Arguments:	none
+ * Returns:	none
+ */
+void
+reset_backoff()
+{
+	cur_backoff = MIN_BACKOFF;
+}
+
+/*
+ * Name:	_get_random_info
+ * Description:	generate an amount of random bits.  Currently
+ *		only a small amount (a long long) can be
+ *		generated at one time.
+ * Scope:	private
+ * Arguments:	buf	- [RO, *RW] (char *)
+ *			  Buffer to copy bits into
+ *		size	- amount to copy
+ * Returns:	B_TRUE on success, B_FALSE otherwise.  The buffer is filled
+ *		with the amount of bytes of random data specified.
+ */
+static boolean_t
+_get_random_info(void *buf, int size)
+{
+	struct timeval tv;
+	typedef struct {
+		long low_time;
+		long hostid;
+	} randomness;
+	randomness r;
+
+	/* if the RANDOM file exists, use it */
+	if (access(RANDOM, R_OK) == 0) {
+		if ((RAND_load_file(RANDOM, 1024 * 1024)) > 0) {
+			if (RAND_bytes((uchar_t *)buf, size) == 1) {
+				/* success */
+				return (B_TRUE);
+			}
+		}
+	}
+
+	/* couldn't use RANDOM file, so fallback to time of day and hostid */
+	(void) gettimeofday(&tv, (struct timezone *)0);
+
+	/* Wouldn't it be nice if we could hash these */
+	r.low_time = tv.tv_usec;
+	r.hostid = gethostid();
+
+	if (sizeof (r) < size) {
+		/*
+		 * Can't copy correctly
+		 */
+		return (B_FALSE);
+	}
+	(void) memcpy(buf, &r, size);
+	return (B_TRUE);
+}
+
+/*
+ * Name:		pkg_passphrase_cb
+ * Description:	Default callback that applications can use when
+ *		a passphrase is needed.  This routine collects
+ *		a passphrase from the user using the given
+ *		passphrase retrieval method set with
+ *		set_passphrase_passarg().  If the method
+ *		indicates an interactive prompt, then the
+ *		prompt set with set_passphrase_prompt()
+ *		is displayed.
+ *
+ * Arguments:	buf	- Buffer to copy passphrase into
+ *		size	- Max amount to copy to buf
+ *		rw	- Whether this passphrase is needed
+ *			to read something off disk, or
+ *			write something to disk.  Applications
+ *			typically want to ask twice when getting
+ *			a passphrase for writing something.
+ *		data	- application-specific data.  In this
+ *			callback, data is a pointer to
+ *			a keystore_passphrase_data structure.
+ *
+ * Returns:	Length of passphrase collected, or -1 on error.
+ *		Errors recorded in 'err' object in the *data.
+ */
+int
+pkg_passphrase_cb(char *buf, int size, int rw, void *data)
+{
+	BIO		*pwdbio = NULL;
+	char		passphrase_copy[MAX_PHRASELEN + 1];
+	PKG_ERR		*err;
+	int		passlen;
+	char		*ws;
+	char		prompt_copy[MAX_VERIFY_MSGLEN];
+	char		*passphrase;
+	char		*arg;
+
+	err = ((keystore_passphrase_data *)data)->err;
+
+	if (passarg == NULL) {
+		arg = "console";
+	} else {
+		arg = passarg;
+	}
+
+	/* default method of collecting password is by prompting */
+	if (ci_streq(arg, "console")) {
+		if ((passphrase = getpassphrase(prompt)) == NULL) {
+			pkgerr_add(err, PKGERR_BADPASS,
+			    gettext(MSG_NOPASS), arg);
+			return (-1);
+		}
+
+		if (rw) {
+			/*
+			 * if the password is being supplied for
+			 * writing something to disk, verify it first
+			 */
+
+			/* make a copy (getpassphrase overwrites) */
+			strlcpy(passphrase_copy, passphrase,
+			    MAX_PHRASELEN + 1);
+
+			if (((passlen = snprintf(prompt_copy,
+					MAX_VERIFY_MSGLEN, "%s: %s",
+					gettext(MSG_PASSWD_AGAIN),
+					prompt)) < 0) ||
+			    (passlen >= (MAX_PHRASELEN + 1))) {
+				pkgerr_add(err, PKGERR_BADPASS,
+				    gettext(MSG_NOPASS), arg);
+				return (-1);
+			}
+
+			if ((passphrase =
+			    getpassphrase(prompt_copy)) == NULL) {
+				pkgerr_add(err, PKGERR_BADPASS,
+				    gettext(MSG_NOPASS), arg);
+				return (-1);
+			}
+
+			if (!streq(passphrase_copy, passphrase)) {
+				pkgerr_add(err, PKGERR_READ,
+				    gettext(MSG_PASSWD_NOMATCH));
+				return (-1);
+			}
+		}
+	} else if (ci_strneq(arg, "pass:", 5)) {
+		passphrase = arg + 5;
+	} else if (ci_strneq(arg, "env:", 4)) {
+		passphrase = getenv(arg + 4);
+	} else if (ci_strneq(arg, "file:", 5)) {
+
+		/* open file for reading */
+		if ((pwdbio = BIO_new_file(arg + 5, "r")) == NULL) {
+			pkgerr_add(err, PKGERR_EXIST,
+			    gettext(MSG_PASSWD_FILE), arg + 5);
+			return (-1);
+		}
+
+		/* read first line */
+		if (((passlen = BIO_gets(pwdbio, buf, size)) < 1) ||
+		    (passlen > size)) {
+			pkgerr_add(err, PKGERR_READ, gettext(MSG_PASSWD_FILE),
+			    arg + 5);
+			return (-1);
+		}
+		BIO_free_all(pwdbio);
+		pwdbio = NULL;
+
+		if (passlen == size) {
+			/*
+			 * password was maximum length, so there is
+			 * no null terminator. null-terminate it
+			 */
+			buf[size - 1] = '\0';
+		}
+
+		/* first newline found is end of passwd, so nuke it */
+		if ((ws = strchr(buf, '\n')) != NULL) {
+			*ws = '\0';
+		}
+		return (strlen(buf));
+	} else {
+		/* unrecognized passphrase */
+		pkgerr_add(err, PKGERR_BADPASS,
+		    gettext(MSG_BADPASSARG), arg);
+		return (-1);
+	}
+
+	if (passphrase == NULL) {
+		/* unable to collect passwd from given source */
+		pkgerr_add(err, PKGERR_BADPASS,
+		    gettext(MSG_NOPASS), arg);
+		return (-1);
+	}
+
+	strlcpy(buf, passphrase, size);
+	return (strlen(buf));
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/usr/src/lib/libpkg/common/pkgweb.h	Fri Jun 05 10:28:40 2009 -0400
@@ -0,0 +1,130 @@
+/*
+ * 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
+ * or http://www.opensolaris.org/os/licensing.
+ * 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 2009 Sun Microsystems, Inc.  All rights reserved.
+ * Use is subject to license terms.
+ */
+
+#ifndef _PKGWEB_H
+#define	_PKGWEB_H
+
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#include <netdb.h>
+#include <boot_http.h>
+
+/* shortest backoff delay possible (in seconds) */
+#define	MIN_BACKOFF	1
+
+/* how much to increase backoff time after each failure */
+#define	BACKOFF_FACTOR	2
+
+/* Maximum amount of backoff for a heavy network or flaky server */
+#define	MAX_BACKOFF	128
+
+typedef enum {
+	HTTP_REQ_TYPE_HEAD,
+	HTTP_REQ_TYPE_GET
+} HTTPRequestType;
+
+typedef enum {
+	OCSPSuccess,
+	OCSPMem,
+	OCSPParse,
+	OCSPConnect,
+	OCSPRequest,
+	OCSPResponder,
+	OCSPUnsupported,
+	OCSPVerify,
+	OCSPInternal,
+	OCSPNoURI
+} OCSPStatus;
+
+typedef enum {
+	none,
+	web_http,
+	web_https,
+	web_ftp
+} WebScheme;
+
+typedef enum {
+    WEB_OK,
+    WEB_TIMEOUT,
+    WEB_CONNREFUSED,
+    WEB_HOSTDOWN,
+    WEB_VERIFY_SETUP,
+    WEB_NOCONNECT,
+    WEB_GET_FAIL
+} WebStatus;
+
+typedef struct {
+	ulong_t prev_cont_length;
+	ulong_t content_length;
+	ulong_t cur_pos;
+} DwnldData;
+
+typedef struct {
+	keystore_handle_t keystore;
+	char *certfile;
+	char *uniqfile;
+	char *link;
+	char *errstr;
+	char *dwnld_dir;
+	boolean_t	spool;
+	void *content;
+	int timeout;
+	url_hport_t proxy;
+	url_t url;
+	DwnldData data;
+	http_respinfo_t *resp;
+	boot_http_ver_t *http_vers;
+	http_handle_t *hps;
+} WEB_SESSION;
+
+extern boolean_t web_session_control(PKG_ERR *, char *, char *,
+    keystore_handle_t, char *, ushort_t, int, int, int, char **);
+extern boolean_t get_signature(PKG_ERR *, char *, struct pkgdev *,
+    PKCS7 **);
+extern boolean_t validate_signature(PKG_ERR *, char *, BIO *, PKCS7 *,
+    STACK_OF(X509) *, url_hport_t *, int);
+extern boolean_t ds_validate_signature(PKG_ERR *, struct pkgdev *, char **,
+    char *, PKCS7 *, STACK_OF(X509) *, url_hport_t *, int);
+extern boolean_t get_proxy_port(PKG_ERR *, char **, ushort_t *);
+extern boolean_t path_valid(char *);
+extern void web_cleanup(void);
+extern ushort_t strip_port(char *proxy);
+extern void set_web_install(void);
+extern int is_web_install(void);
+extern void echo_out(int, char *, ...);
+extern void backoff(void);
+extern void reset_backoff(void);
+extern char *get_endof_string(char *, char);
+extern char *get_startof_string(char *, char);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* _PKGWEB_H */
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/usr/src/lib/libpkg/common/pkgxpand.c	Fri Jun 05 10:28:40 2009 -0400
@@ -0,0 +1,118 @@
+/*
+ * 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
+ * or http://www.opensolaris.org/os/licensing.
+ * 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 2009 Sun Microsystems, Inc.  All rights reserved.
+ * Use is subject to license terms.
+ */
+
+/* Copyright (c) 1984, 1986, 1987, 1988, 1989 AT&T */
+/* All Rights Reserved */
+
+
+
+#include <stdio.h>
+#include <limits.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <string.h>
+#include "pkglib.h"
+#include "pkglocale.h"
+
+extern char	*fpkginst(char *pkg, ...); 	/* libadm.a */
+extern char	*pkgdir; 		/* WHERE? */
+
+#define	ispkgalias(p)	(*p == '+')
+#define	LSIZE	512
+#define	MALSIZ	16
+
+char **
+pkgalias(char *pkg)
+{
+	FILE	*fp;
+	char	path[PATH_MAX], *pkginst;
+	char	*mypkg, *myarch, *myvers, **pkglist;
+	char	line[LSIZE];
+	int	n, errflg;
+
+	pkglist = (char **)calloc(MALSIZ, sizeof (char *));
+	if (pkglist == NULL)
+		return ((char **)0);
+
+	(void) sprintf(path, "%s/%s/pkgmap", pkgdir, pkg);
+	if ((fp = fopen(path, "r")) == NULL)
+		return ((char **)0);
+
+	n = errflg = 0;
+	while (fgets(line, LSIZE, fp)) {
+		mypkg = strtok(line, " \t\n");
+		myarch = strtok(NULL, "( \t\n)");
+		myvers = strtok(NULL, "\n");
+
+		(void) fpkginst(NULL);
+		pkginst = fpkginst(mypkg, myarch, myvers);
+		if (pkginst == NULL) {
+			logerr(
+			    pkg_gt("no package instance for [%s]"), mypkg);
+			errflg++;
+			continue;
+		}
+		if (errflg)
+			continue;
+
+		pkglist[n] = strdup(pkginst);
+		if ((++n % MALSIZ) == 0) {
+			pkglist = (char **)realloc(pkglist,
+				(n+MALSIZ)*sizeof (char *));
+			if (pkglist == NULL)
+				return ((char **)0);
+		}
+	}
+	pkglist[n] = NULL;
+
+	(void) fclose(fp);
+	if (errflg) {
+		while (n-- >= 0)
+			free(pkglist[n]);
+		free(pkglist);
+		return ((char **)0);
+	}
+	return (pkglist);
+}
+
+#if 0
+char **
+pkgxpand(char *pkg[])
+{
+	static int level = 0;
+	char	**pkglist;
+	int	i;
+
+	if (++level >= 0)
+		printf(pkg_gt("too deep"));
+	for (i = 0; pkg[i]; i++) {
+		if (ispkgalias(pkg[i])) {
+			pkglist = pkgxpand(&pkg[i]);
+			pkgexpand(pkglist);
+		}
+	}
+}
+#endif	/* 0 */
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/usr/src/lib/libpkg/common/ppkgmap.c	Fri Jun 05 10:28:40 2009 -0400
@@ -0,0 +1,139 @@
+/*
+ * 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
+ * or http://www.opensolaris.org/os/licensing.
+ * 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 2009 Sun Microsystems, Inc.  All rights reserved.
+ * Use is subject to license terms.
+ */
+
+/* Copyright (c) 1984, 1986, 1987, 1988, 1989 AT&T */
+/* All Rights Reserved */
+
+
+
+#include <stdio.h>
+#include <string.h>
+#include <limits.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <sys/types.h>
+#include "pkgstrct.h"
+
+int	holdcinfo = 0;
+
+int
+ppkgmap(struct cfent *ept, FILE *fp)
+{
+	if (ept->path == NULL)
+		return (-1);
+
+	if (ept->volno) {
+		if (fprintf(fp, "%d ", ept->volno) < 0)
+			return (-1);
+	}
+
+	if (ept->ftype == 'i') {
+		if (fprintf(fp, "%c %s", ept->ftype, ept->path) < 0)
+			return (-1);
+	} else {
+		if (fprintf(fp, "%c %s %s", ept->ftype, ept->pkg_class,
+		    ept->path) < 0)
+			return (-1);
+	}
+
+	if (ept->ainfo.local) {
+		if (fprintf(fp, "=%s", ept->ainfo.local) < 0)
+			return (-1);
+	}
+
+	if (strchr("cb", ept->ftype)) {
+#ifdef SUNOS41
+		if (ept->ainfo.xmajor == BADMAJOR) {
+			if (fprintf(fp, " ?") < 0)
+				return (-1);
+		} else {
+			if (fprintf(fp, " %d", ept->ainfo.xmajor) < 0)
+				return (-1);
+		}
+#else
+		if (ept->ainfo.major == BADMAJOR) {
+			if (fprintf(fp, " ?") < 0)
+				return (-1);
+		} else {
+			if (fprintf(fp, " %d", ept->ainfo.major) < 0)
+				return (-1);
+		}
+#endif
+#ifdef SUNOS41
+		if (ept->ainfo.xminor == BADMINOR) {
+			if (fprintf(fp, " ?") < 0)
+				return (-1);
+		} else {
+			if (fprintf(fp, " %d", ept->ainfo.xminor) < 0)
+				return (-1);
+		}
+#else
+		if (ept->ainfo.minor == BADMINOR) {
+			if (fprintf(fp, " ?") < 0)
+				return (-1);
+		} else {
+			if (fprintf(fp, " %d", ept->ainfo.minor) < 0)
+				return (-1);
+		}
+#endif
+	}
+
+	if (strchr("dxcbpfve", ept->ftype)) {
+		if (fprintf(fp, ((ept->ainfo.mode == BADMODE) ? " ?" : " %04o"),
+		    ept->ainfo.mode) < 0)
+			return (-1);
+		if (fprintf(fp, " %s %s", ept->ainfo.owner, ept->ainfo.group) <
+		    0)
+			return (-1);
+	}
+	if (holdcinfo) {
+		if (fputc('\n', fp) == EOF)
+			return (-1);
+		return (0);
+	}
+
+	if (strchr("ifve", ept->ftype)) {
+		if (fprintf(fp, ((ept->cinfo.size == BADCONT) ? " ?" : " %llu"),
+		    ept->cinfo.size) < 0)
+			return (-1);
+		if (fprintf(fp, ((ept->cinfo.cksum == BADCONT) ? " ?" : " %ld"),
+		    ept->cinfo.cksum) < 0)
+			return (-1);
+		if (fprintf(fp,
+		    ((ept->cinfo.modtime == BADCONT) ? " ?" : " %ld"),
+		    ept->cinfo.modtime) < 0)
+			return (-1);
+	}
+
+	if (ept->ftype == 'i') {
+		if (fputc('\n', fp) == EOF)
+			return (-1);
+		return (0);
+	}
+	if (fprintf(fp, "\n") < 0)
+		return (-1);
+	return (0);
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/usr/src/lib/libpkg/common/progerr.c	Fri Jun 05 10:28:40 2009 -0400
@@ -0,0 +1,201 @@
+/*
+ * 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
+ * or http://www.opensolaris.org/os/licensing.
+ * 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 2009 Sun Microsystems, Inc.  All rights reserved.
+ * Use is subject to license terms.
+ */
+
+/* Copyright (c) 1984, 1986, 1987, 1988, 1989 AT&T */
+/* All Rights Reserved */
+
+
+
+#include <stdio.h>
+#include <stdarg.h>
+#include <unistd.h>
+#include <string.h>
+#include <stdlib.h>
+#include <errno.h>
+#include "pkglocale.h"
+#include "pkgerr.h"
+
+static char	*ProgName = NULL; 	/* Set via set_prog_name() */
+
+
+static void
+error_and_exit(int error_num)
+{
+	(void) fprintf(stderr, "%d\n", error_num);
+	exit(99);
+}
+
+static void	(*fatal_err_func)() = &error_and_exit;
+
+char *
+set_prog_name(char *name)
+{
+	if (name == NULL)
+		return (NULL);
+	if ((name = strdup(name)) == NULL) {
+		(void) fprintf(stderr,
+		    "set_prog_name(): strdup(name) failed.\n");
+		exit(1);
+	}
+	ProgName = strrchr(name, '/');
+	if (!ProgName++)
+		ProgName = name;
+
+	return (ProgName);
+}
+
+char *
+get_prog_name(void)
+{
+	return (ProgName);
+}
+
+
+/*VARARGS*/
+void
+progerr(char *fmt, ...)
+{
+	va_list ap;
+
+	va_start(ap, fmt);
+
+	if (ProgName && *ProgName)
+		(void) fprintf(stderr, pkg_gt("%s: ERROR: "), ProgName);
+	else
+		(void) fprintf(stderr, pkg_gt(" ERROR: "));
+
+	(void) vfprintf(stderr, fmt, ap);
+
+	va_end(ap);
+
+	(void) fprintf(stderr, "\n");
+}
+
+void
+pkgerr(PKG_ERR *err)
+{
+	int i;
+
+	for (i = 0; i < pkgerr_num(err); i++) {
+		progerr("%s", pkgerr_get(err, i));
+	}
+}
+
+
+/*
+ * set_memalloc_failure_func()
+ *	Allows an appliation to specify the function to be called when
+ *	a memory allocation function fails.
+ * Parameters:
+ *	(*alloc_proc)(int)	- specifies the function to call if fatal error
+ *			  (such as being unable to allocate memory) occurs.
+ * Return:
+ *	none
+ * Status:
+ *	Public
+ */
+void
+set_memalloc_failure_func(void (*alloc_proc)(int))
+{
+	if (alloc_proc != (void (*)())NULL)
+		fatal_err_func = alloc_proc;
+}
+
+/*
+ * xmalloc()
+ * 	Alloc 'size' bytes from heap using malloc()
+ * Parameters:
+ *	size	- number of bytes to malloc
+ * Return:
+ *	NULL	- malloc() failure
+ *	void *	- pointer to allocated structure
+ * Status:
+ *	public
+ */
+void *
+xmalloc(size_t size)
+{
+	void *tmp;
+
+	if ((tmp = (void *) malloc(size)) == NULL) {
+		fatal_err_func(errno);
+		return (NULL);
+	} else
+		return (tmp);
+}
+
+/*
+ * xrealloc()
+ *	Calls realloc() with the specfied parameters. xrealloc()
+ *	checks for realloc failures and adjusts the return value
+ *	automatically.
+ * Parameters:
+ *	ptr	- pointer to existing data block
+ * 	size	- number of bytes additional
+ * Return:
+ *	NULL	- realloc() failed
+ *	void *	- pointer to realloc'd structured
+ * Status:
+ *	public
+ */
+void *
+xrealloc(void *ptr, size_t size)
+{
+	void *tmp;
+
+	if ((tmp = (void *)realloc(ptr, size)) == (void *)NULL) {
+		fatal_err_func(errno);
+		return ((void *)NULL);
+	} else
+		return (tmp);
+}
+
+/*
+ * xstrdup()
+ *	Allocate space for the string from the heap, copy 'str' into it,
+ *	and return a pointer to it.
+ * Parameters:
+ *	str	- string to duplicate
+ * Return:
+ *	NULL	- duplication failed or 'str' was NULL
+ * 	char *	- pointer to newly allocated/initialized structure
+ * Status:
+ *	public
+ */
+char *
+xstrdup(char *str)
+{
+	char *tmp;
+
+	if (str == NULL)
+		return ((char *)NULL);
+
+	if ((tmp = strdup(str)) == NULL) {
+		fatal_err_func(errno);
+		return ((char *)NULL);
+	} else
+		return (tmp);
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/usr/src/lib/libpkg/common/putcfile.c	Fri Jun 05 10:28:40 2009 -0400
@@ -0,0 +1,376 @@
+/*
+ * 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
+ * or http://www.opensolaris.org/os/licensing.
+ * 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 2009 Sun Microsystems, Inc.  All rights reserved.
+ * Use is subject to license terms.
+ */
+
+/* Copyright (c) 1984, 1986, 1987, 1988, 1989 AT&T */
+/* All Rights Reserved */
+
+
+
+#include <stdio.h>
+#include <string.h>
+#include <limits.h>
+#include <sys/types.h>
+#include "pkgstrct.h"
+#include "pkglib.h"
+
+/*
+ * Name:	putcfile
+ * Description:	Write contents file entry to specified FILE
+ * Arguments:	struct cfent a_ept - data for contents file entry
+ *		FILE *a_fp - FP of file to write contents file entry to
+ * Notes:	This is identical to putcvfpfile() but this function takes a
+ *		stdio FILE* file to write to instead of a VFP_T file. It is
+ *		MUCH slower than putcvfpfile().
+ */
+
+int
+putcfile(struct cfent *a_ept, FILE *a_fp)
+{
+	struct pinfo *pinfo;
+
+	if (a_ept->ftype == 'i') {
+		return (0); /* no ifiles stored in contents DB */
+	}
+
+	if (a_ept->path == NULL) {
+		return (-1);	/* no path name - no entry to write */
+	}
+
+	if (fputs(a_ept->path, a_fp) == EOF) {
+		return (-1);
+	}
+
+	if (a_ept->ainfo.local) {
+		if (putc('=', a_fp) == EOF) {
+			return (-1);
+		}
+		if (fputs(a_ept->ainfo.local, a_fp) == EOF)
+			return (-1);
+	}
+
+	if (a_ept->volno) {
+		if (fprintf(a_fp, " %d", a_ept->volno) < 0) {
+			return (-1);
+		}
+	}
+
+	if (putc(' ', a_fp) == EOF) {
+		return (-1);
+	}
+
+	if (putc(a_ept->ftype, a_fp) == EOF) {
+		return (-1);
+	}
+
+	if (putc(' ', a_fp) == EOF) {
+		return (-1);
+	}
+
+	if (fputs(a_ept->pkg_class, a_fp) == EOF) {
+		return (-1);
+	}
+
+	if ((a_ept->ftype == 'c') || (a_ept->ftype == 'b')) {
+		if (a_ept->ainfo.major == BADMAJOR) {
+			if (putc(' ', a_fp) == EOF) {
+				return (-1);
+			}
+
+			if (putc('?', a_fp) == EOF) {
+				return (-1);
+			}
+		} else {
+			if (fprintf(a_fp, " %d", a_ept->ainfo.major) < 0)
+				return (-1);
+		}
+
+		if (a_ept->ainfo.minor == BADMINOR) {
+			if (putc(' ', a_fp) == EOF) {
+				return (-1);
+			}
+
+			if (putc('?', a_fp) == EOF) {
+				return (-1);
+			}
+		} else {
+			if (fprintf(a_fp, " %d", a_ept->ainfo.minor) < 0)
+				return (-1);
+		}
+	}
+
+	if ((a_ept->ftype == 'd') || (a_ept->ftype == 'x') ||
+		(a_ept->ftype == 'c') || (a_ept->ftype == 'b') ||
+		(a_ept->ftype == 'p') || (a_ept->ftype == 'f') ||
+		(a_ept->ftype == 'v') || (a_ept->ftype == 'e')) {
+		if (fprintf(a_fp,
+			((a_ept->ainfo.mode == BADMODE) ? " ?" : " %04o"),
+			a_ept->ainfo.mode) < 0)
+			return (-1);
+
+		if (putc(' ', a_fp) == EOF) {
+			return (-1);
+		}
+
+		if (fputs(a_ept->ainfo.owner, a_fp) == EOF) {
+			return (-1);
+		}
+
+		if (putc(' ', a_fp) == EOF) {
+			return (-1);
+		}
+
+		if (fputs(a_ept->ainfo.group, a_fp) == EOF) {
+			return (-1);
+		}
+	}
+
+	if ((a_ept->ftype == 'f') || (a_ept->ftype == 'v') ||
+		(a_ept->ftype == 'e')) {
+		if (fprintf(a_fp,
+			((a_ept->cinfo.size == BADCONT) ? " ?" : " %llu"),
+			a_ept->cinfo.size) < 0)
+			return (-1);
+
+		if (fprintf(a_fp,
+			((a_ept->cinfo.cksum == BADCONT) ? " ?" : " %ld"),
+			a_ept->cinfo.cksum) < 0)
+			return (-1);
+
+		if (fprintf(a_fp,
+		    ((a_ept->cinfo.modtime == BADCONT) ? " ?" : " %ld"),
+		    a_ept->cinfo.modtime) < 0)
+			return (-1);
+	}
+
+	pinfo = a_ept->pinfo;
+	while (pinfo) {
+		if (putc(' ', a_fp) == EOF) {
+			return (-1);
+		}
+
+		if (pinfo->status) {
+			if (fputc(pinfo->status, a_fp) == EOF) {
+				return (-1);
+			}
+		}
+
+		if (fputs(pinfo->pkg, a_fp) == EOF) {
+			return (-1);
+		}
+
+		if (pinfo->editflag) {
+			if (putc('\\', a_fp) == EOF) {
+				return (-1);
+			}
+		}
+
+		if (pinfo->aclass[0]) {
+			if (putc(':', a_fp) == EOF) {
+				return (-1);
+			}
+			if (fputs(pinfo->aclass, a_fp) == EOF) {
+				return (-1);
+			}
+		}
+		pinfo = pinfo->next;
+	}
+
+	if (putc('\n', a_fp) == EOF) {
+		return (-1);
+	}
+	return (0);
+}
+
+/*
+ * Name:	putcvfpfile
+ * Description:	Write contents file entry to specified VFP
+ * Arguments:	struct cfent a_ept - data for contents file entry
+ *		VFP_T *a_vfp - VFP of file to write contents file entry to
+ * Notes:	This is identical to putcfile() but this function takes a
+ *		VFP_T file to write to instead of a stdio FILE file. It is
+ *		MUCH faster tha putcfile().
+ */
+
+int
+putcvfpfile(struct cfent *a_ept, VFP_T *a_vfp)
+{
+	struct pinfo *pinfo;
+
+	/* contents file does not maintain any 'i' file entries */
+
+	if (a_ept->ftype == 'i') {
+		return (0);
+	}
+
+	/* cannot create an entry if it has no file name */
+
+	if (a_ept->path == NULL) {
+		return (-1);
+	}
+
+	/*
+	 * Format of contents file line could be one of:
+	 * /file=./dir/file s class SUNWxxx
+	 * /file=../dir/file l class SUNWxxx
+	 * /dir d class mode owner group SUNWxxx SUNWyyy
+	 * /devices/name c class major minor mode owner group SUNWxxx
+	 * /file f class mode owner group size cksum modtime SUNWxxx
+	 * /file x class mode owner group SUNWppro
+	 * /file v class mode owner group size cksum modtime SUNWxxx
+	 * /file e class mode owner group size cksum modtime SUNWxxx
+	 * The package name could be prefixed by one of the following
+	 * status indicators: +-*!%@#~
+	 */
+
+	/*
+	 * Adding an entry to the specified VFP.  During normal processing the
+	 * contents file is copied to a temporary contents file and entries are
+	 * added as appropriate.  When this processing is completed, a decision
+	 * is made on whether or not to overwrite the real contents file with
+	 * the contents of the temporary contents file.  If the temporary
+	 * contents file is just a copy of the real contents file then there is
+	 * no need to overwrite the real contents file with the contents of the
+	 * temporary contents file.  This decision is made in part on whether
+	 * or not any new or modified entries have been added to the temporary
+	 * contents file.  Set the "data is modified" indication associated
+	 * with this VFP so that the real contents file is overwritten when
+	 * processing is done.
+	 */
+
+	(void) vfpSetModified(a_vfp);
+
+	/* write initial path [all entries] */
+
+	vfpPuts(a_vfp, a_ept->path);
+
+	/* if link, write out '=' portion */
+
+	if (a_ept->ainfo.local) {
+		vfpPutc(a_vfp, '=');
+		vfpPuts(a_vfp, a_ept->ainfo.local);
+	}
+
+	/* if volume, write it out */
+
+	if (a_ept->volno) {
+		vfpPutc(a_vfp, ' ');
+		vfpPutInteger(a_vfp, a_ept->volno);
+	}
+
+	/* write out <space><entry type><space>class> */
+
+	vfpPutc(a_vfp, ' ');
+	vfpPutc(a_vfp, a_ept->ftype);
+	vfpPutc(a_vfp, ' ');
+	vfpPuts(a_vfp, a_ept->pkg_class);
+
+	/* if char/block device, write out major/minor numbers */
+
+	if ((a_ept->ftype == 'c') || (a_ept->ftype == 'b')) {
+		/* major device number */
+		if (a_ept->ainfo.major == BADMAJOR) {
+			vfpPutc(a_vfp, ' ');
+			vfpPutc(a_vfp, '?');
+		} else {
+			vfpPutc(a_vfp, ' ');
+			vfpPutInteger(a_vfp, a_ept->ainfo.major);
+		}
+
+		/* minor device number */
+		if (a_ept->ainfo.minor == BADMINOR) {
+			vfpPutc(a_vfp, ' ');
+			vfpPutc(a_vfp, '?');
+		} else {
+			vfpPutc(a_vfp, ' ');
+			vfpPutInteger(a_vfp, a_ept->ainfo.minor);
+		}
+	}
+
+	/* if dxcbpfve, write out mode, owner, group */
+
+	if ((a_ept->ftype == 'd') || (a_ept->ftype == 'x') ||
+		(a_ept->ftype == 'c') || (a_ept->ftype == 'b') ||
+		(a_ept->ftype == 'p') || (a_ept->ftype == 'f') ||
+		(a_ept->ftype == 'v') || (a_ept->ftype == 'e')) {
+
+		/* mode */
+		vfpPutFormat(a_vfp,
+			((a_ept->ainfo.mode == BADMODE) ? " ?" : " %04o"),
+			a_ept->ainfo.mode);
+
+		/* owner */
+		vfpPutc(a_vfp, ' ');
+		vfpPuts(a_vfp, a_ept->ainfo.owner);
+
+		/* group */
+		vfpPutc(a_vfp, ' ');
+		vfpPuts(a_vfp, a_ept->ainfo.group);
+	}
+	/* if f/v/e, write out size, cksum, modtime */
+
+	if ((a_ept->ftype == 'f') || (a_ept->ftype == 'v') ||
+		(a_ept->ftype == 'e')) {
+		/* size */
+		vfpPutFormat(a_vfp,
+			((a_ept->cinfo.size == BADCONT) ? " ?" : " %llu"),
+			a_ept->cinfo.size);
+
+		/* cksum */
+		vfpPutFormat(a_vfp,
+			((a_ept->cinfo.cksum == BADCONT) ? " ?" : " %ld"),
+			a_ept->cinfo.cksum);
+
+		/* modtime */
+		vfpPutFormat(a_vfp,
+			((a_ept->cinfo.modtime == BADCONT) ? " ?" : " %ld"),
+			a_ept->cinfo.modtime);
+	}
+
+	/* write out list of all packages referencing this entry */
+
+	pinfo = a_ept->pinfo;
+	while (pinfo) {
+		vfpPutc(a_vfp, ' ');
+		if (pinfo->status) {
+			vfpPutc(a_vfp, pinfo->status);
+		}
+
+		vfpPuts(a_vfp, pinfo->pkg);
+
+		if (pinfo->editflag) {
+			vfpPutc(a_vfp, '\\');
+		}
+
+		if (pinfo->aclass[0]) {
+			vfpPutc(a_vfp, ':');
+			vfpPuts(a_vfp, pinfo->aclass);
+		}
+		pinfo = pinfo->next;
+	}
+
+	vfpPutc(a_vfp, '\n');
+	return (0);
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/usr/src/lib/libpkg/common/rrmdir.c	Fri Jun 05 10:28:40 2009 -0400
@@ -0,0 +1,83 @@
+/*
+ * 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
+ * or http://www.opensolaris.org/os/licensing.
+ * 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 2009 Sun Microsystems, Inc.  All rights reserved.
+ * Use is subject to license terms.
+ */
+
+/* Copyright (c) 1984, 1986, 1987, 1988, 1989 AT&T */
+/* All Rights Reserved */
+
+
+
+#include <limits.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <string.h>
+#include <strings.h>
+#include "pkglocale.h"
+#include "pkglib.h"
+
+int
+rrmdir(char *a_path)
+{
+	char	path[PATH_MAX+13];
+	int	i;
+	int	status;
+
+	/*
+	 * For some reason, a simple "rm -rf" will remove the contents
+	 * of the directory, but will fail to remove the directory itself
+	 * with "No such file or directory" when running the pkg commands
+	 * under a virtual root via the "chroot" command.  This has been
+	 * seen so far only with the `pkgremove' command, and when the
+	 * the directory is NFS mounted from a 4.x server.  This should
+	 * probably be revisited at a later time, but for now we'll just
+	 * remove the directory contents first, then the directory.
+	 */
+
+	/* do not allow removal of all root files via blank path */
+
+	if ((a_path == NULL) || (*a_path == '\0')) {
+		(void) fprintf(stderr,
+		    pkg_gt("warning: rrmdir(path==NULL): nothing deleted\n"));
+		return (0);
+	}
+
+	/*
+	 * first generate path with slash-star at the end and attempt to remove
+	 * all files first. If successful then remove with just the path only.
+	 */
+
+	(void) snprintf(path, sizeof (path), "%s/", a_path);
+	i = e_ExecCmdList(&status, (char **)NULL, (char *)NULL,
+		"/bin/rm", "rm", "-rf", path, (char *)NULL);
+
+	if (access(a_path, F_OK) == 0) {
+		i = e_ExecCmdList(&status, (char **)NULL, (char *)NULL,
+			"/bin/rmdir", "rmdir", a_path, (char *)NULL);
+	}
+
+	/* return 0 if last command successful, else return 1 */
+	return ((i == 0 && status == 0) ? 0 : 1);
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/usr/src/lib/libpkg/common/runcmd.c	Fri Jun 05 10:28:40 2009 -0400
@@ -0,0 +1,808 @@
+/*
+ * 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
+ * or http://www.opensolaris.org/os/licensing.
+ * 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 2009 Sun Microsystems, Inc.  All rights reserved.
+ * Use is subject to license terms.
+ */
+
+/* Copyright (c) 1984, 1986, 1987, 1988, 1989 AT&T */
+/* All Rights Reserved */
+
+
+
+#include <stdio.h>
+#include <errno.h>
+#include <string.h>
+#include <strings.h>
+#include <signal.h>
+#include <fcntl.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <wait.h>
+#include <sys/types.h>
+#include "pkglib.h"
+#include "pkglocale.h"
+#include "pkglibmsgs.h"
+
+#ifndef _STDARG_H
+#include "stdarg.h"
+#endif
+
+/*
+ * Private definitions
+ */
+
+/* Maximum number of arguments to pkg_ExecCmdList */
+
+#define	MAX_EXEC_CMD_ARGS	100
+
+/* Size of buffer increments when reading from pipe */
+
+#define	PIPE_BUFFER_INCREMENT	256
+
+static char	errfile[L_tmpnam+1];
+
+/*
+ * This is the "argument array" definition that is returned by e_new_args and is
+ * used by e_add_args, e_free_args, etc.
+ */
+
+struct _argArray_t {
+	long	_aaNumArgs;	/* number of arguments set */
+	long	_aaMaxArgs;	/* number of arguments allocated */
+	char	**_aaArgs;	/* actual arguments */
+};
+
+typedef struct _argArray_t argArray_t;
+
+/*
+ * Private Methods
+ */
+static void		e_free_args(argArray_t *a_args);
+static argArray_t	*e_new_args(int initialCount);
+/*PRINTFLIKE2*/
+static boolean_t	e_add_arg(argArray_t *a_args, char *a_format, ...);
+static int		e_get_argc(argArray_t *a_args);
+static char		**e_get_argv(argArray_t *a_args);
+
+
+/*
+ * Public Methods
+ */
+
+
+void
+rpterr(void)
+{
+	FILE	*fp;
+	int	c;
+
+	if (errfile[0]) {
+		if (fp = fopen(errfile, "r")) {
+			while ((c = getc(fp)) != EOF)
+				putc(c, stderr);
+			(void) fclose(fp);
+		}
+		(void) unlink(errfile);
+		errfile[0] = '\0';
+	}
+}
+
+void
+ecleanup(void)
+{
+	if (errfile[0]) {
+		(void) unlink(errfile);
+		errfile[0] = NULL;
+	}
+}
+
+int
+esystem(char *cmd, int ifd, int ofd)
+{
+	char	*perrfile;
+	int	status = 0;
+	pid_t	pid;
+
+	perrfile = tmpnam(NULL);
+	if (perrfile == NULL) {
+		progerr(
+		    pkg_gt("unable to create temp error file, errno=%d"),
+		    errno);
+		return (-1);
+	}
+	(void) strlcpy(errfile, perrfile, sizeof (errfile));
+
+	/* flush standard i/o before creating new process */
+
+	(void) fflush(stderr);
+	(void) fflush(stdout);
+
+	/*
+	 * create new process to execute command in;
+	 * vfork() is being used to avoid duplicating the parents
+	 * memory space - this means that the child process may
+	 * not modify any of the parents memory including the
+	 * standard i/o descriptors - all the child can do is
+	 * adjust interrupts and open files as a prelude to a
+	 * call to exec().
+	 */
+
+	pid = vfork();
+	if (pid == 0) {
+		/*
+		 * this is the child process
+		 */
+		int	i;
+
+		/* reset any signals to default */
+
+		for (i = 0; i < NSIG; i++) {
+			(void) sigset(i, SIG_DFL);
+		}
+
+		if (ifd > 0) {
+			(void) dup2(ifd, STDIN_FILENO);
+		}
+
+		if (ofd >= 0 && ofd != STDOUT_FILENO) {
+			(void) dup2(ofd, STDOUT_FILENO);
+		}
+
+		i = open(errfile, O_WRONLY|O_CREAT|O_TRUNC, 0666);
+		if (i >= 0) {
+			dup2(i, STDERR_FILENO);
+		}
+
+		/* Close all open files except standard i/o */
+
+		closefrom(3);
+
+		/* execute target executable */
+
+		execl("/sbin/sh", "/sbin/sh", "-c", cmd, NULL);
+		progerr(pkg_gt("exec of <%s> failed, errno=%d"), cmd, errno);
+		_exit(99);
+	} else if (pid < 0) {
+		/* fork failed! */
+
+		logerr(pkg_gt("bad vfork(), errno=%d"), errno);
+		return (-1);
+	}
+
+	/*
+	 * this is the parent process
+	 */
+
+	sighold(SIGINT);
+	pid = waitpid(pid, &status, 0);
+	sigrelse(SIGINT);
+
+	if (pid < 0) {
+		return (-1); /* probably interrupted */
+	}
+
+	switch (status & 0177) {
+		case 0:
+		case 0177:
+			status = status >> 8;
+			/*FALLTHROUGH*/
+
+		default:
+			/* terminated by a signal */
+			status = status & 0177;
+	}
+
+	if (status == 0) {
+		ecleanup();
+	}
+
+	return (status);
+}
+
+FILE *
+epopen(char *cmd, char *mode)
+{
+	char	*buffer, *perrfile;
+	FILE	*pp;
+	size_t	len;
+	size_t	alen;
+
+	if (errfile[0]) {
+		/* cleanup previous errfile */
+		unlink(errfile);
+	}
+
+	perrfile = tmpnam(NULL);
+	if (perrfile == NULL) {
+		progerr(
+		    pkg_gt("unable to create temp error file, errno=%d"),
+		    errno);
+		return ((FILE *)0);
+	}
+
+	if (strlcpy(errfile, perrfile, sizeof (errfile)) > sizeof (errfile)) {
+		progerr(pkg_gt("file name max length %d; name is too long: %s"),
+						sizeof (errfile), perrfile);
+		return ((FILE *)0);
+	}
+
+	len = strlen(cmd)+6+strlen(errfile);
+	buffer = (char *)calloc(len, sizeof (char));
+	if (buffer == NULL) {
+		progerr(pkg_gt("no memory in epopen(), errno=%d"), errno);
+		return ((FILE *)0);
+	}
+
+	if (strchr(cmd, '|')) {
+		alen = snprintf(buffer, len, "(%s) 2>%s", cmd, errfile);
+	} else {
+		alen = snprintf(buffer, len, "%s 2>%s", cmd, errfile);
+	}
+
+	if (alen > len) {
+		progerr(pkg_gt("command max length %d; cmd is too long: %s"),
+								len, cmd);
+		return ((FILE *)0);
+	}
+
+	pp = popen(buffer, mode);
+
+	free(buffer);
+	return (pp);
+}
+
+int
+epclose(FILE *pp)
+{
+	int n;
+
+	n = pclose(pp);
+	if (n == 0)
+		ecleanup();
+	return (n);
+}
+
+/*
+ * Name:	e_ExecCmdArray
+ * Synopsis:	Execute Unix command and return results
+ * Description:	Execute a Unix command and return results and status
+ * Arguments:
+ *		r_status - [RO, *RW] - (int *)
+ *			Return (exit) status from Unix command:
+ *			== -1 : child terminated with a signal
+ *			!= -1 : lower 8-bit value child passed to exit()
+ *		r_results - [RO, *RW] - (char **)
+ *			Any output generated by the Unix command to stdout
+ *			and to stderr
+ *			== (char *)NULL if no output generated
+ *		a_inputFile - [RO, *RO] - (char *)
+ *			Pointer to character string representing file to be
+ *			used as "standard input" for the command.
+ *			== (char *)NULL to use "/dev/null" as standard input
+ *		a_cmd - [RO, *RO] - (char *)
+ *			Pointer to character string representing the full path
+ *			of the Unix command to execute
+ *		char **a_args - [RO, *RO] - (char **)
+ *			List of character strings representing the arguments
+ *			to be passed to the Unix command. The list must be
+ *			terminated with an element that is (char *)NULL
+ * Returns:	int
+ *			== 0 - Command executed
+ *				Look at r_status for results of Unix command
+ *			!= 0 - problems executing command
+ *				r_status and r_results have no meaning;
+ *				r_status will be -1
+ *				r_results will be NULL
+ * NOTE:    	Any results returned is placed in new storage for the
+ *		calling method. The caller must use 'free' to dispose
+ *		of the storage once the results are no longer needed.
+ * NOTE:	If 0 is returned, 'r_status' must be queried to
+ *		determine the results of the Unix command.
+ * NOTE:	The system "errno" value from immediately after waitpid() call
+ *		is preserved for the calling method to use to determine
+ *		the system reason why the operation failed.
+ */
+
+int
+e_ExecCmdArray(int *r_status, char **r_results,
+	char *a_inputFile, char *a_cmd, char **a_args)
+{
+	char		*buffer;
+	int		bufferIndex;
+	int		bufferSize;
+	int		ipipe[2] = {0, 0};
+	pid_t		pid;
+	pid_t		resultPid;
+	int		status;
+	int		lerrno;
+	int		stdinfile = -1;
+
+	/* reset return results buffer pointer */
+
+	if (r_results != (char **)NULL) {
+		*r_results = (char *)NULL;
+	}
+
+	*r_status = -1;
+
+	/*
+	 * See if command exists
+	 */
+
+	if (access(a_cmd, F_OK|X_OK) != 0) {
+		return (-1);
+	}
+
+	/*
+	 * See if input file exists
+	 */
+
+	if (a_inputFile != (char *)NULL) {
+		stdinfile = open(a_inputFile, O_RDONLY);
+	} else {
+		stdinfile = open("/dev/null", O_RDONLY); /* stdin = /dev/null */
+	}
+
+	if (stdinfile < 0) {
+		return (-1);
+	}
+
+	/*
+	 * Create a pipe to be used to capture the command output
+	 */
+
+	if (pipe(ipipe) != 0) {
+		(void) close(stdinfile);
+		return (-1);
+	}
+
+
+	bufferSize = PIPE_BUFFER_INCREMENT;
+	bufferIndex = 0;
+	buffer = calloc(1, bufferSize);
+	if (buffer == (char *)NULL) {
+		(void) close(stdinfile);
+		return (-1);
+	}
+
+	/* flush standard i/o before creating new process */
+
+	(void) fflush(stderr);
+	(void) fflush(stdout);
+
+	/*
+	 * create new process to execute command in;
+	 * vfork() is being used to avoid duplicating the parents
+	 * memory space - this means that the child process may
+	 * not modify any of the parents memory including the
+	 * standard i/o descriptors - all the child can do is
+	 * adjust interrupts and open files as a prelude to a
+	 * call to exec().
+	 */
+
+	pid = vfork();
+
+	if (pid == 0) {
+		/*
+		 * This is the forked (child) process ======================
+		 */
+
+		int	i;
+
+		/* reset any signals to default */
+
+		for (i = 0; i < NSIG; i++) {
+			(void) sigset(i, SIG_DFL);
+		}
+
+		/* assign stdin, stdout, stderr as appropriate */
+
+		(void) dup2(stdinfile, STDIN_FILENO);
+		(void) close(ipipe[0]);		/* close out pipe reader side */
+		(void) dup2(ipipe[1], STDOUT_FILENO);
+		(void) dup2(ipipe[1], STDERR_FILENO);
+
+		/* Close all open files except standard i/o */
+
+		closefrom(3);
+
+		/* execute target executable */
+
+		(void) execvp(a_cmd, a_args);
+		perror(a_cmd);	/* Emit error msg - ends up in callers buffer */
+		_exit(0x00FE);
+	}
+
+	/*
+	 * This is the forking (parent) process ====================
+	 */
+
+	(void) close(stdinfile);
+	(void) close(ipipe[1]);		/* Close write side of pipe */
+
+	/*
+	 * Spin reading data from the child into the buffer - when the read eofs
+	 * the child has exited
+	 */
+
+	for (;;) {
+		ssize_t	bytesRead;
+
+		/* read as much child data as there is available buffer space */
+
+		bytesRead = read(ipipe[0], buffer + bufferIndex,
+						bufferSize - bufferIndex);
+
+		/* break out of read loop if end-of-file encountered */
+
+		if (bytesRead == 0) {
+			break;
+		}
+
+		/* if error, continue if recoverable, else break out of loop */
+
+		if (bytesRead == -1) {
+			/* try again: EAGAIN - insufficient resources */
+
+			if (errno == EAGAIN) {
+				continue;
+			}
+
+			/* try again: EINTR - interrupted system call */
+
+			if (errno == EINTR) {
+				continue;
+			}
+
+			/* break out of loop - error not recoverable */
+			break;
+		}
+
+		/* at least 1 byte read: expand buffer if at end */
+
+		bufferIndex += bytesRead;
+		if (bufferIndex >= bufferSize) {
+			buffer = realloc(buffer,
+					bufferSize += PIPE_BUFFER_INCREMENT);
+			(void) memset(buffer + bufferIndex, 0,
+				bufferSize - bufferIndex);
+		}
+	}
+
+	(void) close(ipipe[0]);		/* Close read side of pipe */
+
+	/* Get subprocess exit status */
+
+	for (;;) {
+		resultPid = waitpid(pid, &status, 0L);
+		lerrno = (resultPid == -1 ? errno : 0);
+
+		/* break loop if child process status reaped */
+
+		if (resultPid != -1) {
+			break;
+		}
+
+		/* break loop if not interrupted out of waitpid */
+
+		if (errno != EINTR) {
+			break;
+		}
+	}
+
+	/*
+	 * If the child process terminated due to a call to exit(), then
+	 * set results equal to the 8-bit exit status of the child process;
+	 * otherwise, set the exit status to "-1" indicating that the child
+	 * exited via a signal.
+	 */
+
+	*r_status = WIFEXITED(status) ? WEXITSTATUS(status) : -1;
+
+	/* return appropriate output */
+
+	if (!*buffer) {
+		/* No contents in output buffer - discard */
+		free(buffer);
+	} else if (r_results == (char **)NULL) {
+		/* Not requested to return results - discard */
+		free(buffer);
+	} else {
+		/* have output and request to return: pass to calling method */
+		*r_results = buffer;
+	}
+
+	errno = lerrno;
+	return (resultPid == -1 ? -1 : 0);
+}
+
+/*
+ * Name:	e_ExecCmdList
+ * Synopsis:	Execute Unix command and return results
+ * Description:	Execute a Unix command and return results and status
+ * Arguments:
+ *		r_status - [RO, *RW] - (int *)
+ *			Return (exit) status from Unix command
+ *		r_results - [RO, *RW] - (char **)
+ *			Any output generated by the Unix command to stdout
+ *			and to stderr
+ *			== (char *)NULL if no output generated
+ *		a_inputFile - [RO, *RO] - (char *)
+ *			Pointer to character string representing file to be
+ *			used as "standard input" for the command.
+ *			== (char *)NULL to use "/dev/null" as standard input
+ *		a_cmd - [RO, *RO] - (char *)
+ *			Pointer to character string representing the full path
+ *			of the Unix command to execute
+ *		... - [RO] (?)
+ *			Zero or more arguments to the Unix command
+ *			The argument list must be ended with (void *)NULL
+ * Returns:	int
+ *			== 0 - Command executed
+ *				Look at r_status for results of Unix command
+ *			!= 0 - problems executing command
+ *				r_status and r_results have no meaning
+ * NOTE:    	Any results returned is placed in new storage for the
+ *		calling method. The caller must use 'free' to dispose
+ *		of the storage once the results are no longer needed.
+ * NOTE:	If LU_SUCCESS is returned, 'r_status' must be queried to
+ *		determine the results of the Unix command.
+ */
+
+int
+e_ExecCmdList(int *r_status, char **r_results,
+	char *a_inputFile, char *a_cmd, ...)
+{
+	va_list		ap;		/* references variable argument list */
+	char		*array[MAX_EXEC_CMD_ARGS+1];
+	int		argno = 0;
+
+	/*
+	 * Create argument array for exec system call
+	 */
+
+	bzero(array, sizeof (array));
+
+	va_start(ap, a_cmd);	/* Begin variable argument processing */
+
+	for (argno = 0; argno < MAX_EXEC_CMD_ARGS; argno++) {
+		array[argno] = va_arg(ap, char *);
+		if (array[argno] == (char *)NULL) {
+			break;
+		}
+	}
+
+	va_end(ap);
+	return (e_ExecCmdArray(r_status, r_results, a_inputFile,
+								a_cmd, array));
+}
+
+/*
+ * Name:	e_new_args
+ * Description:	create a new argument array for use in exec() calls
+ * Arguments:	initialCount - [RO, *RO] - (int)
+ *			Initial number of elements to populate the
+ *			argument array with - use best guess
+ * Returns:	argArray_t *
+ *			Pointer to argument array that can be used in other
+ *			functions that accept it as an argument
+ *			== (argArray_t *)NULL - error
+ * NOTE: you must call e_free_args() when the returned argument array is
+ * no longer needed so that all storage used can be freed up.
+ */
+
+argArray_t *
+e_new_args(int initialCount)
+{
+	argArray_t	*aa;
+
+	/* allocate new argument array structure */
+
+	aa = (argArray_t *)calloc(1, sizeof (argArray_t));
+	if (aa == (argArray_t *)NULL) {
+		progerr(ERR_MALLOC, strerror(errno), sizeof (argArray_t),
+			"<argArray_t>");
+		return ((argArray_t *)NULL);
+	}
+
+	/* allocate initial argument array */
+
+	aa->_aaArgs = (char **)calloc(initialCount+1, sizeof (char *));
+	if (aa->_aaArgs == (char **)NULL) {
+		progerr(ERR_MALLOC, strerror(errno),
+			(initialCount+1)*sizeof (char *), "<char **>");
+		return ((argArray_t *)NULL);
+	}
+
+	/* initialize argument indexes */
+
+	aa->_aaNumArgs = 0;
+	aa->_aaMaxArgs = initialCount;
+
+	return (aa);
+}
+
+/*
+ * Name:	e_add_arg
+ * Description:	add new argument to argument array for use in exec() calls
+ * Arguments:	a_args - [RO, *RW] - (argArray_t *)
+ *			Pointer to argument array (previously allocated via
+ *			a call to e_new_args) to add the argument to
+ *		a_format - [RO, *RO] - (char *)
+ *			Pointer to "printf" style format argument
+ *		... - [RO, *RO] - (varies)
+ *			Arguments as appropriate for format statement
+ * Returns:	boolean_t
+ *			B_TRUE - success
+ *			B_FALSE - failure
+ * Examples:
+ * - to add an argument that specifies a file descriptor:
+ *	int fd;
+ *	e_add_arg(aa, "/proc/self/fd/%d", fd);
+ * - to add a flag or other known text:
+ *	e_add_arg(aa, "-s")
+ * - to add random text:
+ *	char *random_text;
+ *	e_add_arg(aa, "%s", random_text);
+ */
+
+/*PRINTFLIKE2*/
+boolean_t
+e_add_arg(argArray_t *a_args, char *a_format, ...)
+{
+	char		*rstr = (char *)NULL;
+	char		bfr[MAX_CANON];
+	size_t		vres = 0;
+	va_list		ap;
+
+	/*
+	 * double argument array if array is full
+	 */
+
+	if (a_args->_aaNumArgs >= a_args->_aaMaxArgs) {
+		int	newMax;
+		char	**newArgs;
+
+		newMax = a_args->_aaMaxArgs * 2;
+		newArgs = (char **)realloc(a_args->_aaArgs,
+			(newMax+1) * sizeof (char *));
+		if (newArgs == (char **)NULL) {
+			progerr(ERR_MALLOC, strerror(errno),
+				((newMax+1) * sizeof (char *)), "<char **>");
+			return (B_FALSE);
+		}
+		a_args->_aaArgs = newArgs;
+		a_args->_aaMaxArgs = newMax;
+	}
+
+	/* determine size of argument to add to list */
+
+	va_start(ap, a_format);
+	vres = vsnprintf(bfr, sizeof (bfr), a_format, ap);
+	va_end(ap);
+
+	/* if it fit in the built in buffer, use that */
+	if (vres < sizeof (bfr)) {
+		/* dup text already generated in bfr */
+		rstr = strdup(bfr);
+		if (rstr == (char *)NULL) {
+			progerr(ERR_MALLOC, strerror(errno), vres+2,
+				"<char *>");
+			return (B_FALSE);
+		}
+	} else {
+		/* allocate space for argument to add */
+
+		rstr = (char *)malloc(vres+2);
+		if (rstr == (char *)NULL) {
+			progerr(ERR_MALLOC, strerror(errno), vres+2,
+				"<char *>");
+			return (B_FALSE);
+		}
+
+		/* generate argument to add */
+
+		va_start(ap, a_format);
+		vres = vsnprintf(rstr, vres+1, a_format, ap);
+		va_end(ap);
+	}
+
+	/* add argument to the end of the argument array */
+
+	a_args->_aaArgs[a_args->_aaNumArgs++] = rstr;
+	a_args->_aaArgs[a_args->_aaNumArgs] = (char *)NULL;
+
+	return (B_TRUE);
+}
+
+/*
+ * Name:	e_get_argv
+ * Description:	return (char **)argv pointer from argument array
+ * Arguments:	a_args - [RO, *RW] - (argArray_t *)
+ *			Pointer to argument array (previously allocated via
+ *			a call to e_new_args) to return argv pointer for
+ * Returns:	char **
+ *			Pointer to (char **)argv pointer suitable for use
+ *			in an exec*() call
+ * NOTE: the actual character array is always terminated with a (char *)NULL
+ */
+
+char **
+e_get_argv(argArray_t *a_args)
+{
+	return (a_args->_aaArgs);
+}
+
+/*
+ * Name:	e_get_argc
+ * Description:	return (int) argc count from argument array
+ * Arguments:	a_args - [RO, *RW] - (argArray_t *)
+ *			Pointer to argument array (previously allocated via
+ *			a call to e_new_args) to return argc count for
+ * Returns:	int
+ *			Count of the number of arguments in the argument array
+ *			suitable for use in an exec*() call
+ */
+
+int
+e_get_argc(argArray_t *a_args)
+{
+	return (a_args->_aaNumArgs);
+}
+
+/*
+ * Name:	e_free_args
+ * Description:	free all storage contained in an argument array previously
+ *		allocated by a call to e_new_args
+ * Arguments:	a_args - [RO, *RW] - (argArray_t *)
+ *			Pointer to argument array (previously allocated via
+ *			a call to e_new_args) to free
+ * Returns:	void
+ * NOTE:	preserves errno (usually called right after e_execCmd*())
+ */
+
+void
+e_free_args(argArray_t *a_args)
+{
+	int	i;
+	int	lerrno = errno;
+
+	/* free all arguments in the argument array */
+
+	for (i = (a_args->_aaNumArgs-1); i >= 0; i--) {
+		(void) free(a_args->_aaArgs[i]);
+		a_args->_aaArgs[i] = (char *)NULL;
+	}
+
+	/* free argument array */
+
+	(void) free(a_args->_aaArgs);
+
+	/* free argument array structure */
+
+	(void) free(a_args);
+
+	/* restore errno */
+
+	errno = lerrno;
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/usr/src/lib/libpkg/common/security.c	Fri Jun 05 10:28:40 2009 -0400
@@ -0,0 +1,282 @@
+/*
+ * 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
+ * or http://www.opensolaris.org/os/licensing.
+ * 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 2006 Sun Microsystems, Inc.  All rights reserved.
+ * Use is subject to license terms.
+ */
+
+
+/*
+ * Module: security.c
+ * Description:
+ *	Module for handling certificates and various
+ *	utilities to access their data.
+ */
+
+#include <stdio.h>
+#include <string.h>
+#include <errno.h>
+#include <ctype.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <limits.h>
+#include <pkgstrct.h>
+#include <pkginfo.h>
+#include <locale.h>
+#include <libintl.h>
+#include <unistd.h>
+#include <stdlib.h>
+
+#include <openssl/bio.h>
+#include <openssl/pkcs12.h>
+#include <openssl/pkcs7.h>
+#include <openssl/x509.h>
+#include <openssl/err.h>
+#include <openssl/ssl.h>
+#include "pkgerr.h"
+#include "pkglib.h"
+#include "pkglibmsgs.h"
+#include "pkglocale.h"
+#include "p12lib.h"
+
+/* length of allowable passwords */
+#define	MAX_PASSLEN		128
+
+/*
+ * Name:	init_security
+ * Description:	Initializes structures, libraries, for security operations
+ * Arguments:	none
+ * Returns:	0 if we couldn't initialize, non-zero otherwise
+ */
+void
+sec_init(void)
+{
+	OpenSSL_add_all_algorithms();
+	SSL_load_error_strings();
+	ERR_load_SUNW_strings();
+	(void) SSL_library_init();
+}
+
+/*
+ * get_cert_chain - Builds a chain of certificates, from a given
+ * user certificate to a trusted certificate.
+ *
+ * Arguments:
+ * err - Error object to add errors to
+ * cert - User cert to start with
+ * cas - Trusted certs to use as trust anchors
+ * chain - The resulting chain of certs (in the form of an
+ * ordered set) is placed here.
+ *
+ * Returns:
+ *   0 - Success - chain is stored in 'chain'.
+ * non-zero - Failure, errors recorded in err
+ */
+int
+get_cert_chain(PKG_ERR *err, X509 *cert, STACK_OF(X509) *clcerts,
+    STACK_OF(X509) *cas, STACK_OF(X509) **chain)
+{
+	X509_STORE_CTX	*store_ctx = NULL;
+	X509_STORE 	*ca_store = NULL;
+	X509		*ca_cert = NULL;
+	int i;
+	int ret = 0;
+
+	if ((ca_store = X509_STORE_new()) == NULL) {
+		pkgerr_add(err, PKGERR_NOMEM,
+		    gettext(ERR_MEM));
+		ret = 1;
+		goto cleanup;
+	}
+
+	/* add all ca certs into the store */
+	for (i = 0; i < sk_X509_num(cas); i++) {
+		/* LINTED pointer cast may result in improper alignment */
+		ca_cert = sk_X509_value(cas, i);
+		if (X509_STORE_add_cert(ca_store, ca_cert) == 0) {
+			pkgerr_add(err, PKGERR_NOMEM, gettext(ERR_MEM));
+			ret = 1;
+			goto cleanup;
+		}
+	}
+
+	/* initialize context object used during the chain resolution */
+
+	if ((store_ctx = X509_STORE_CTX_new()) == NULL) {
+		pkgerr_add(err, PKGERR_NOMEM, gettext(ERR_MEM));
+		ret = 1;
+		goto cleanup;
+	}
+
+	(void) X509_STORE_CTX_init(store_ctx, ca_store, cert, clcerts);
+	/* attempt to verify the cert, which builds the cert chain */
+	if (X509_verify_cert(store_ctx) <= 0) {
+		pkgerr_add(err, PKGERR_CHAIN,
+		    gettext(ERR_CERTCHAIN),
+		    get_subject_display_name(cert),
+		    X509_verify_cert_error_string(store_ctx->error));
+		ret = 1;
+		goto cleanup;
+	}
+	*chain = X509_STORE_CTX_get1_chain(store_ctx);
+
+cleanup:
+	if (ca_store != NULL)
+		(void) X509_STORE_free(ca_store);
+	if (store_ctx != NULL) {
+		(void) X509_STORE_CTX_cleanup(store_ctx);
+		(void) X509_STORE_CTX_free(store_ctx);
+	}
+
+	return (ret);
+}
+
+/*
+ * Name:		get_subject_name
+ * Description:	Retrieves a name used for identifying a certificate's subject.
+ *
+ * Arguments:	cert - The certificate to get the name from
+ *
+ * Returns :	A static buffer containing the common name (CN) of the
+ * 		subject of the cert.
+ *
+ *		if the CN is not available, returns a string with the entire
+ * X509 distinguished name.
+ */
+char
+*get_subject_display_name(X509 *cert)
+{
+
+	X509_NAME	*xname;
+	static char	sname[ATTR_MAX];
+
+	xname = X509_get_subject_name(cert);
+	if (X509_NAME_get_text_by_NID(xname,
+	    NID_commonName, sname,
+	    ATTR_MAX) <= 0) {
+		(void) strncpy(sname,
+		    X509_NAME_oneline(xname,
+			NULL, 0), ATTR_MAX);
+		sname[ATTR_MAX - 1] = '\0';
+	}
+	return (sname);
+}
+
+/*
+ * Name:		get_display_name
+ * Description:	Retrieves a name used for identifying a certificate's issuer.
+ *
+ * Arguments:	cert - The certificate to get the name from
+ *
+ * Returns :	A static buffer containing the common name (CN)
+ *		of the issuer of the cert.
+ *
+ *		if the CN is not available, returns a string with the entire
+ *		X509 distinguished name.
+ */
+char
+*get_issuer_display_name(X509 *cert)
+{
+
+	X509_NAME	*xname;
+	static char	sname[ATTR_MAX];
+
+	xname = X509_get_issuer_name(cert);
+	if (X509_NAME_get_text_by_NID(xname,
+	    NID_commonName, sname,
+	    ATTR_MAX) <= 0) {
+		(void) strncpy(sname,
+		    X509_NAME_oneline(xname,
+			NULL, 0), ATTR_MAX);
+		sname[ATTR_MAX - 1] = '\0';
+	}
+	return (sname);
+}
+
+
+/*
+ * Name:		get_serial_num
+ * Description:	Retrieves the serial number of an X509 cert
+ *
+ * Arguments:	cert - The certificate to get the data from
+ *
+ * Returns :	A static buffer containing the serial number
+ *		of the cert
+ *
+ *		if the SN is not available, returns NULL
+ */
+char
+*get_serial_num(X509 *cert)
+{
+	static char	 sn_str[ATTR_MAX];
+	ASN1_INTEGER	*sn;
+
+	if ((sn = X509_get_serialNumber(cert)) != 0) {
+		return (NULL);
+	} else {
+		(void) snprintf(sn_str, ATTR_MAX, "%ld",
+		    ASN1_INTEGER_get(sn));
+	}
+
+	return (sn_str);
+}
+
+/*
+ * Name:		get_fingerprint
+ * Description:	Generates a fingerprint string given
+ *		a digest algorithm with which to calculate
+ *		the fingerprint
+ *
+ * Arguments:	cert - The certificate to get the data from
+ * Arguments:	alg - The algorithm to use to calculate the fingerprint
+ *
+ * Returns :	A static buffer containing the digest
+ *		NULL if cert is NULL, or digest cannot be calculated
+ */
+char
+*get_fingerprint(X509 *cert, const EVP_MD *alg)
+{
+	static char	 fp_str[ATTR_MAX];
+	char		 tmp[ATTR_MAX] = "";
+	unsigned int n;
+	unsigned char md[EVP_MAX_MD_SIZE];
+	int i;
+
+	if (!X509_digest(cert, alg, md, &n)) {
+		return (NULL);
+	}
+
+	/* start with empty string */
+	fp_str[0] = '\0';
+
+	for (i = 0; i < (int)n; i++) {
+		/* form a byte of the fingerprint */
+		(void) snprintf(tmp, ATTR_MAX, "%02X:", md[i]);
+		/* cat it onto the end of the result */
+		(void) strlcat(fp_str, tmp, ATTR_MAX);
+	}
+
+	/* nuke trailing ':' */
+	fp_str[strlen(fp_str) - 1] = '\0';
+
+	return (fp_str);
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/usr/src/lib/libpkg/common/srchcfile.c	Fri Jun 05 10:28:40 2009 -0400
@@ -0,0 +1,1278 @@
+/*
+ * 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
+ * or http://www.opensolaris.org/os/licensing.
+ * 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 2009 Sun Microsystems, Inc.  All rights reserved.
+ * Use is subject to license terms.
+ */
+
+/* Copyright (c) 1984, 1986, 1987, 1988, 1989 AT&T */
+/* All Rights Reserved */
+
+
+
+#include <stdio.h>
+#include <limits.h>
+#include <stdlib.h>
+#include <string.h>
+#include <strings.h>
+#include <ctype.h>
+#include <sys/types.h>
+#include <libintl.h>
+#include "pkglib.h"
+#include "pkgstrct.h"
+#include "pkglocale.h"
+#include "pkglibmsgs.h"
+
+/*
+ * Forward declarations
+ */
+
+static void	findend(char **cp);
+static int	getend(char **cp);
+static int	getstr(char **cp, int n, char *str, int separator[]);
+
+/* from gpkgmap.c */
+int	getnumvfp(char **cp, int base, long *d, long bad);
+int	getlnumvfp(char **cp, int base, fsblkcnt_t *d, long bad);
+
+/*
+ * Module globals
+ */
+
+static char	lpath[PATH_MAX];	/* for ept->path */
+static char	mylocal[PATH_MAX];	/* for ept->ainfo.local */
+static int	decisionTableInit = 0;
+
+/*
+ * These arrays must be indexable by an unsigned char.
+ */
+
+static int	ISPKGPATHSEP[UCHAR_MAX+1];
+static int	ISWORDSEP[UCHAR_MAX+1];
+static int	ISPKGNAMESEP[UCHAR_MAX+1];
+
+/*
+ * Name:	WRITEDATA
+ * Description:	write out data to VFP_T given start and end pointers
+ * Arguments:	VFP - (VFP_T *) - [RO, *RW]
+ *			Contents file VFP to narrow search on
+ *		FIRSTPOS - (char *) - [RO, *RO]
+ *			Pointer to first byte to write out
+ *		LASTPOS - (char *) - [RO, *RO]
+ *			Pointer to last byte to write out
+ */
+
+#define	WRITEDATA(VFP, FIRSTPOS, LASTPOS)				\
+	{								\
+		ssize_t XXlenXX;					\
+		/* compute number of bytes skipped */			\
+		XXlenXX = (ptrdiff_t)(LASTPOS) - (ptrdiff_t)(FIRSTPOS);	\
+		/* write the bytes out */				\
+		vfpPutBytes((VFP), (FIRSTPOS), XXlenXX);		\
+	}
+
+/*
+ * Name:	COPYPATH
+ * Description:	copy path limiting size to destination capacity
+ * Arguments:	DEST - (char []) - [RW]
+ *		SRC - (char *) - [RO, *RO]
+ *			Pointer to first byte of path to copy
+ *		LEN - (int) - [RO]
+ *			Number of bytes to copy
+ */
+
+#define	COPYPATH(DEST, SRC, LEN)					\
+	{								\
+		/* assure return path does not overflow */		\
+		if ((LEN) > sizeof ((DEST))) {				\
+			(LEN) = sizeof ((DEST))-1;			\
+		}							\
+		/* copy return path to local storage */			\
+		(void) memcpy((DEST), (SRC), (LEN));			\
+		(DEST)[(LEN)] = '\0';					\
+	}
+
+/*
+ * Name:	narrowSearch
+ * Description:	narrow the search location for a specified path
+ *		The contents and package map files are always sorted by path.
+ *		This function is given a target path to search for given the
+ *		current location in a contents file. It is assured that the
+ *		target path has not been searched for yet in the contents file
+ *		so the current location in the contents file is guaranteed to
+ *		be less than the location of the target path (if present).
+ *		Given this employ a binary search to speed up the search for
+ *		the path nearest to a specified target path.
+ * Arguments:	a_vfp - (VFP_T *) - [RO, *RW]
+ *			Contents file VFP to narrow search on
+ *		a_path - (char *) - [RO, *RO]
+ *			Pointer to path to search for
+ *		a_pathLen - (size_t) - [RO]
+ *			Length of string (a_path)
+ * Returns:	char *	- pointer to first byte of entry in contents file that
+ *			is guaranteed to be the closest match to the specified
+ *			a_path without being "greater than" the path.
+ *			== (char *)NULL if no entry found
+ */
+
+static char *
+narrowSearch(VFP_T *a_vfp, char *a_path, size_t a_pathLen)
+{
+	char	*phigh;
+	char	*plow;
+	char	*pmid;
+	int	n;
+	size_t	plen;
+
+	/* if no path to compare, start at beginning */
+
+	if ((a_path == (char *)NULL) || (*a_path == '\0')) {
+		return ((char *)NULL);
+	}
+
+	/* if the contents file is empty, resort to sequential search */
+
+	if (vfpGetBytesRemaining(a_vfp) <= 1) {
+		return ((char *)NULL);
+	}
+
+	/*
+	 * test against first path - if the path specified is less than the
+	 * first path in the contents file, then the path can be inserted
+	 * before the first entry in the contents file.
+	 */
+
+	/* locate start of first line */
+
+	plow = vfpGetCurrCharPtr(a_vfp);
+	pmid = plow;
+
+	/* if first path not absolute, resort to sequential search */
+
+	if (*pmid != '/') {
+		return ((char *)NULL);
+	}
+
+	/* find end of path */
+
+	while (ISPKGPATHSEP[(int)*pmid] == 0) {
+		pmid++;
+	}
+
+	/* determine length of path */
+
+	plen = (ptrdiff_t)pmid - (ptrdiff_t)plow;
+
+	/* compare target path with current path */
+
+	n = strncmp(a_path, plow, plen);
+	if (n == 0) {
+		/* if lengths same exact match return position found */
+		if (a_pathLen == plen) {
+			return (plow);
+		}
+		/* not exact match - a_path > pm */
+		n = a_pathLen;
+	}
+
+	/* return if target is less than or equal to first entry */
+
+	if (n <= 0) {
+		return (plow);
+	}
+
+	/*
+	 * test against last path - if the path specified is greater than the
+	 * last path in the contents file, then the path can be appended after
+	 * the last entry in the contents file.
+	 */
+
+	/* locate start of last line */
+
+	plow = vfpGetCurrCharPtr(a_vfp);
+	pmid = vfpGetLastCharPtr(a_vfp);
+
+	while ((pmid > plow) && (!((pmid[0] == '/') && (pmid[-1] == '\n')))) {
+		pmid--;
+	}
+
+	/* if absolute path, do comparison */
+
+	if ((pmid > plow) && (*pmid == '/')) {
+		plow = pmid;
+
+		/* find end of path */
+
+		while (ISPKGPATHSEP[(int)*pmid] == 0) {
+			pmid++;
+		}
+
+		/* determine length of path */
+
+		plen = (ptrdiff_t)pmid - (ptrdiff_t)plow;
+
+		/* compare target path with current path */
+
+		n = strncmp(a_path, plow, plen);
+		if (n == 0) {
+			/* if lengths same exact match return position found */
+			if (a_pathLen == plen) {
+				return (plow);
+			}
+			/* not exact match - a_path > pm */
+			n = a_pathLen;
+		}
+
+		/* return if target is greater than or equal to entry */
+
+		if (n >= 0) {
+			return (plow);
+		}
+	}
+	/*
+	 * firstPath < targetpath < lastPath:
+	 * binary search looking for closest "less than" match
+	 */
+
+	plow = vfpGetCurrCharPtr(a_vfp);
+	phigh = vfpGetLastCharPtr(a_vfp);
+
+	for (;;) {
+		char	*pm;
+
+		/* determine number of bytes left in search area */
+
+		plen = (ptrdiff_t)phigh - (ptrdiff_t)plow;
+
+		/* calculate mid point between current low and high points */
+
+		pmid = plow + (plen >> 1);
+
+		/* backup and find first "\n/" -or- start of buffer */
+
+		while ((pmid > plow) &&
+				(!((pmid[0] == '/') && (pmid[-1] == '\n')))) {
+			pmid--;
+		}
+
+		/* return lowest line found if current line not past that */
+
+		if (pmid <= plow) {
+			return (plow);
+		}
+
+		/* remember start of this line */
+
+		pm = pmid;
+
+		/* find end of path */
+
+		while (ISPKGPATHSEP[(int)*pmid] == 0) {
+			pmid++;
+		}
+
+		/* determine length of path */
+
+		plen = (ptrdiff_t)pmid - (ptrdiff_t)pm;
+
+		/* compare target path with current path */
+
+		n = strncmp(a_path, pm, plen);
+
+		if (n == 0) {
+			/* if lengths same exact match return position found */
+			if (a_pathLen == plen) {
+				return (pm);
+			}
+			/* not exact match - a_path > pm */
+			n = a_pathLen;
+		}
+
+
+		/* not exact match - determine which watermark to split */
+
+		if (n > 0) {	/* a_path > pm */
+			plow = pm;
+		} else {	/* a_path < pm */
+			phigh = pm;
+		}
+	}
+	/*NOTREACHED*/
+}
+
+/*
+ * Name:	srchcfile
+ * Description:	search contents file looking for closest match to entry,
+ *		creating a new contents file if output contents file specified
+ * Arguments:	ept - (struct cfent *) - [RO, *RW]
+ *			- contents file entry, describing last item found
+ *		path - (char *) - [RO, *RO]
+ *			- path to search for in contents file
+ *			- If path is "*", then the next entry is returned;
+ *				the next entry always matches this path
+ *			- If the path is (char *)NULL or "", then all remaining
+ *				entries are processed and copied out to the
+ *				file specified by cfTmpVFp
+ *		cfVfp - (VFP_T *) - [RO, *RW]
+ *			- VFP_T open on contents file to search
+ *		cfTmpVfp - (VFP_T *) - [RO, *RW]
+ *			- VFP_T open on temporary contents file to populate
+ * Returns:	int
+ *		< 0 - error occurred
+ *			- Use getErrstr to retrieve character-string describing
+ *			  the reason for failure
+ *		== 0 - no match found
+ *			- specified path not in the contents file
+ *			- all contents of cfVfp copied to cfTmpVfp
+ *			- current character of cfVfp is at end of file
+ *		== 1 - exact match found
+ *			- specified path found in contents file
+ *			- contents of cfVfp up to entry found copied to cfTmpVfp
+ *			- current character of cfVfp is first character of
+ *				entry found
+ *			- this value is always returned if path is "*" and the
+ *			  next entry is returned - -1 is returned when no more
+ *			  entries are left to process
+ *		== 2 - entry found which is GREATER than path specified
+ *			- specified path would fit BEFORE entry found
+ *			- contents of cfVfp up to entry found copied to cfTmpVfp
+ *			- current character of cfVfp is first character of
+ *				entry found
+ * Side Effects:
+ *		- The ept structure supplied is filled in with a description of
+ *		  the item that caused the search to terminate, except in the
+ *		  case of '0' in which case the contents of 'ept' is undefined.
+ *		- NOTE: the ept->path item points to a path that is statically
+ *		  allocated and will be overwritten on the next call.
+ *		- NOTE: the ept->ainfo.local item points to a path that is
+ *		  statically allocated and will be overwritten on the next call.
+ */
+
+int
+srchcfile(struct cfent *ept, char *path, VFP_T *cfVfp, VFP_T *cfTmpVfp)
+{
+	char		*cpath_start = (char *)NULL;
+	char		*firstPos = vfpGetCurrCharPtr(cfVfp);
+	char		*lastPos = NULL;
+	char		*pos;
+	char		classname[CLSSIZ+1];
+	char		pkgname[PKGSIZ+1];
+	int		anypath = 0;
+	int		c;
+	int		dataSkipped = 0;
+	int		n;
+	int		rdpath;
+	size_t		cpath_len = 0;
+	size_t		pathLength;
+	struct pinfo	*lastpinfo;
+	struct pinfo	*pinfo;
+
+	/*
+	 * this code does not use nested subroutines because execution time
+	 * of this routine is especially critical to installation and upgrade
+	 */
+
+	/* initialize local variables */
+
+	setErrstr(NULL);	/* no error message currently cached */
+	pathLength = (path == (char *)NULL ? 0 : strlen(path));
+	lpath[0] = '\0';
+	lpath[sizeof (lpath)-1] = '\0';
+
+	/* initialize ept structure values */
+
+	(void) strlcpy(ept->ainfo.group, BADGROUP, sizeof (ept->ainfo.group));
+	(void) strlcpy(ept->ainfo.owner, BADOWNER, sizeof (ept->ainfo.owner));
+	(void) strlcpy(ept->pkg_class, BADCLASS,  sizeof (ept->pkg_class));
+	ept->ainfo.local = (char *)NULL;
+	ept->ainfo.mode = BADMODE;
+	ept->cinfo.cksum = BADCONT;
+	ept->cinfo.modtime = BADCONT;
+	ept->cinfo.size = (fsblkcnt_t)BADCONT;
+	ept->ftype = BADFTYPE;
+	ept->npkgs = 0;
+	ept->path = (char *)NULL;
+	ept->pinfo = (struct pinfo *)NULL;
+	ept->pkg_class_idx = -1;
+	ept->volno = 0;
+
+	/*
+	 * populate decision tables that implement fast character checking;
+	 * this is much faster than the equivalent strpbrk() call or a
+	 * while() loop checking for the characters. It is only faster if
+	 * there are at least 3 characters to scan for - when checking for
+	 * one or two characters (such as '\n' or '\0') its faster to do
+	 * a simple while() loop.
+	 */
+
+	if (decisionTableInit == 0) {
+		/*
+		 * any chars listed stop scan;
+		 * scan stops on first byte found that is set to '1' below
+		 */
+
+		/*
+		 * Separators for path names, normal space and =
+		 * for linked filenames
+		 */
+		bzero(ISPKGPATHSEP, sizeof (ISPKGPATHSEP));
+		ISPKGPATHSEP['='] = 1;		/* = */
+		ISPKGPATHSEP[' '] = 1;		/* space */
+		ISPKGPATHSEP['\t'] = 1;		/* horizontal-tab */
+		ISPKGPATHSEP['\n'] = 1;		/* new-line */
+		ISPKGPATHSEP['\0'] = 1;		/* NULL character */
+
+		/*
+		 * Separators for normal words
+		 */
+		bzero(ISWORDSEP, sizeof (ISWORDSEP));
+		ISWORDSEP[' '] = 1;
+		ISWORDSEP['\t'] = 1;
+		ISWORDSEP['\n'] = 1;
+		ISWORDSEP['\0'] = 1;
+
+		/*
+		 * Separators for list of packages, includes \\ for
+		 * alternate ftype and : for classname
+		 */
+		bzero(ISPKGNAMESEP, sizeof (ISPKGNAMESEP));
+		ISPKGNAMESEP[' '] = 1;
+		ISPKGNAMESEP['\t'] = 1;
+		ISPKGNAMESEP['\n'] = 1;
+		ISPKGNAMESEP[':'] = 1;
+		ISPKGNAMESEP['\\'] = 1;
+		ISPKGNAMESEP['\0'] = 1;
+
+		decisionTableInit = 1;
+	}
+
+	/* if no bytes in contents file, return 0 */
+
+	if (vfpGetBytesRemaining(cfVfp) <= 1) {
+		return (0);
+	}
+
+	/* if the path to scan for is empty, act like no path was specified */
+
+	if ((path != (char *)NULL) && (*path == '\0')) {
+		path = (char *)NULL;
+	}
+
+	/*
+	 * if path to search for is "*", then we will return the first path
+	 * we encounter as a match, otherwise we return an error
+	 */
+
+	if ((path != (char *)NULL) && (path[0] != '/')) {
+		if (strcmp(path, "*") != 0) {
+			setErrstr(pkg_gt(ERR_ILLEGAL_SEARCH_PATH));
+			return (-1);
+		}
+		anypath = 1;
+	}
+
+	/* attempt to narrow down the search for the specified path */
+
+	if (anypath == 0) {
+		char	*np;
+
+		np = narrowSearch(cfVfp, path, pathLength);
+		if (np != (char *)NULL) {
+			dataSkipped = 1;
+			lastPos = np;
+			vfpSetCurrCharPtr(cfVfp, np);
+		}
+	}
+
+	/*
+	 * If the path to search for in the source contents file is NULL, then
+	 * this is a request to scan to the end of the source contents file. If
+	 * there is a temporary contents file to copy entries to, all that needs
+	 * to be done is to copy the data remaining from the current location in
+	 * the source contents file to the end of the temporary contents file.
+	 * if there is no temporary contents file to copy to, then all that
+	 * needs to be done is to seek to the end of the source contents file.
+	 */
+
+	if ((anypath == 0) && (path == (char *)NULL)) {
+		if (cfTmpVfp != (VFP_T *)NULL) {
+			if (vfpGetBytesRemaining(cfVfp) > 0) {
+				WRITEDATA(cfTmpVfp, firstPos,
+					vfpGetLastCharPtr(cfVfp)+1);
+			}
+			*vfpGetLastCharPtr(cfTmpVfp) = '\0';
+		}
+		vfpSeekToEnd(cfVfp);
+		return (0);
+	}
+
+	/*
+	 * *********************************************************************
+	 * main loop processing entries from the contents file looking for
+	 * the specified path
+	 * *********************************************************************
+	 */
+
+	for (;;) {
+		char	*p;
+
+		/* not reading old style entry */
+
+		rdpath = 0;
+
+		/* determine first character of the next entry */
+
+		if (vfpGetBytesRemaining(cfVfp) <= 0) {
+			/* no bytes in contents file current char is NULL */
+
+			c = '\0';
+		} else {
+			/* grab path from first entry */
+
+			c = vfpGetcNoInc(cfVfp);
+		}
+
+		/* save current position in file */
+
+		pos = vfpGetCurrCharPtr(cfVfp);
+
+		/*
+		 * =============================================================
+		 * at the first character of the next entry in the contents file
+		 * if not absolute path check for exceptions and old style entry
+		 * --> if end of contents file write out skipped data and return
+		 * --> if comment character skip to end of line and restart loop
+		 * --> else process "old style entry: ftype class path"
+		 * =============================================================
+		 */
+
+		if (c != '/') {
+			/* if NULL character then end of contents file found */
+
+			if (c == '\0') {
+				/* write out skipped data before returning */
+				if (dataSkipped &&
+						(cfTmpVfp != (VFP_T *)NULL)) {
+					WRITEDATA(cfTmpVfp, firstPos, lastPos);
+					*vfpGetLastCharPtr(cfTmpVfp) = '\0';
+				}
+
+				return (0); /* no more entries */
+			}
+
+			/* ignore lines that begin with #, : or a "space" */
+
+			if ((isspace(c) != 0) || (c == '#') || (c == ':')) {
+				/* line is a comment */
+				findend(&vfpGetCurrCharPtr(cfVfp));
+				continue;
+			}
+
+			/*
+			 * old style entry - format is:
+			 *	ftype class path
+			 * set ept->ftype to the type
+			 * set ept->class to the class
+			 * set ept->path to point to lpath
+			 * set cpath_start/cpath_len to point to the file name
+			 * set rdpath to '1' to indicate old style entry parsed
+			 */
+
+			while (isspace((c = vfpGetc(cfVfp))))
+				;
+
+			switch (c) {
+			case '?': case 'f': case 'v': case 'e': case 'l':
+			case 's': case 'p': case 'c': case 'b': case 'd':
+			case 'x':
+				/* save ftype */
+				ept->ftype = (char)c;
+
+				/* save class */
+				if (getstr(&vfpGetCurrCharPtr(cfVfp), CLSSIZ,
+						ept->pkg_class, ISWORDSEP)) {
+					setErrstr(ERR_CANNOT_READ_CLASS_TOKEN);
+					findend(&vfpGetCurrCharPtr(cfVfp));
+					return (-1);
+				}
+
+				/*
+				 * locate file name up to "=", set cpath_start
+				 * and cpath_len to point to the file name
+				 */
+				cpath_start = vfpGetCurrCharPtr(cfVfp);
+				p = vfpGetCurrCharPtr(cfVfp);
+
+				/*
+				 * skip past all bytes until first '= \t\n\0':
+				 */
+				while (ISPKGPATHSEP[(int)*p] == 0) {
+					p++;
+				}
+
+				cpath_len = vfpGetCurrPtrDelta(cfVfp, p);
+
+				/*
+				 * if the path is zero bytes, line is corrupted
+				 */
+
+				if (cpath_len < 1) {
+					setErrstr(ERR_CANNOT_READ_PATHNAME_FLD);
+					findend(&vfpGetCurrCharPtr(cfVfp));
+					return (-1);
+				}
+
+				vfpIncCurrPtrBy(cfVfp, cpath_len);
+
+				/* set path to point to local path cache */
+				ept->path = lpath;
+
+				/* set flag indicating path already parsed */
+				rdpath = 1;
+				break;
+
+			case '\0':
+				/* end of line before new-line seen */
+				vfpDecCurrPtr(cfVfp);
+				setErrstr(ERR_INCOMPLETE_ENTRY);
+				return (-1);
+
+			case '0': case '1': case '2': case '3': case '4':
+			case '5': case '6': case '7': case '8': case '9':
+				/* volume number seen */
+				setErrstr(ERR_VOLUMENO_UNEXPECTED);
+				findend(&vfpGetCurrCharPtr(cfVfp));
+				return (-1);
+
+			case 'i':
+				/* type i files are not cataloged */
+				setErrstr(ERR_FTYPE_I_UNEXPECTED);
+				findend(&vfpGetCurrCharPtr(cfVfp));
+				return (-1);
+
+			default:
+				/* unknown ftype */
+				setErrstr(ERR_UNKNOWN_FTYPE);
+				findend(&vfpGetCurrCharPtr(cfVfp));
+				return (-1);
+			}
+		} else {
+			/*
+			 * current entry DOES start with absolute path
+			 * set ept->path to point to lpath
+			 * set cpath_start/cpath_len to point to the file name
+			 */
+		/* copy first token into path element of passed structure */
+
+			cpath_start = vfpGetCurrCharPtr(cfVfp);
+
+			p = cpath_start;
+
+			/*
+			 * skip past all bytes until first from '= \t\n\0':
+			 */
+
+			while (ISPKGPATHSEP[(int)*p] == 0) {
+				p++;
+			}
+
+			cpath_len = vfpGetCurrPtrDelta(cfVfp, p);
+
+			vfpIncCurrPtrBy(cfVfp, cpath_len);
+
+			if (vfpGetcNoInc(cfVfp) == '\0') {
+				setErrstr(ERR_INCOMPLETE_ENTRY);
+				findend(&vfpGetCurrCharPtr(cfVfp));
+				return (-1);
+			}
+
+			ept->path = lpath;
+		}
+
+		/*
+		 * =============================================================
+		 * if absolute path then the path is collected and we are at the
+		 * first byte following the absolute path name;
+		 * if not an absolute path then an old style entry, ept has been
+		 * filled with the type and class and path name.
+		 * determine if we have read the pathname which identifies
+		 * the entry we are searching for
+		 * =============================================================
+		 */
+
+		if (anypath != 0) {
+			n = 0;	/* next entry is "equal to" */
+		} else if (path == (char *)NULL) {
+			n = 1;	/* next entry is "greater than" */
+		} else {
+			n = strncmp(path, cpath_start, cpath_len);
+			if ((n == 0) && (cpath_len != pathLength)) {
+				n = cpath_len;
+			}
+		}
+
+		/* get first character following the end of the path */
+
+		c = vfpGetc(cfVfp);
+
+		/*
+		 * if an exact match, always parse out the local path
+		 */
+
+		if (n == 0) {
+			/*
+			 * we want to return information about this path in
+			 * the structure provided, so parse any local path
+			 * and jump to code which parses rest of the input line
+			 */
+			if (c == '=') {
+				/* parse local path specification */
+				if (getstr(&vfpGetCurrCharPtr(cfVfp), PATH_MAX,
+						mylocal, ISWORDSEP)) {
+
+					/* copy path found to 'lpath' */
+					COPYPATH(lpath, cpath_start, cpath_len);
+
+					setErrstr(ERR_CANNOT_READ_LL_PATH);
+					findend(&vfpGetCurrCharPtr(cfVfp));
+					return (-1);
+				}
+				ept->ainfo.local = mylocal;
+			}
+		}
+
+		/*
+		 * if an exact match and processing a new style entry, read the
+		 * remaining information from the new style entry - if this is
+		 * an old style entry (rdpath != 0) then the existing info has
+		 * already been processed as it exists before the pathname and
+		 * not after like a new style entry
+		 */
+
+		if (n == 0 && rdpath == 0) {
+			while (isspace((c = vfpGetc(cfVfp))))
+				;
+
+			switch (c) {
+			case '?': case 'f': case 'v': case 'e': case 'l':
+			case 's': case 'p': case 'c': case 'b': case 'd':
+			case 'x':
+				/* save ftype */
+				ept->ftype = (char)c;
+
+				/* save class */
+				if (getstr(&vfpGetCurrCharPtr(cfVfp), CLSSIZ,
+						ept->pkg_class, ISWORDSEP)) {
+
+					/* copy path found to 'lpath' */
+					COPYPATH(lpath, cpath_start, cpath_len);
+
+					setErrstr(ERR_CANNOT_READ_CLASS_TOKEN);
+					findend(&vfpGetCurrCharPtr(cfVfp));
+					return (-1);
+				}
+				break; /* we already read the pathname */
+
+			case '\0':
+				/* end of line before new-line seen */
+				vfpDecCurrPtr(cfVfp);
+
+				/* copy path found to 'lpath' */
+				COPYPATH(lpath, cpath_start, cpath_len);
+
+				setErrstr(ERR_INCOMPLETE_ENTRY);
+				return (-1);
+
+			case '0': case '1': case '2': case '3': case '4':
+			case '5': case '6': case '7': case '8': case '9':
+
+				/* copy path found to 'lpath' */
+				COPYPATH(lpath, cpath_start, cpath_len);
+
+				setErrstr(ERR_VOLUMENO_UNEXPECTED);
+				findend(&vfpGetCurrCharPtr(cfVfp));
+				return (-1);
+
+			case 'i':
+
+				/* copy path found to 'lpath' */
+				COPYPATH(lpath, cpath_start, cpath_len);
+
+				setErrstr(ERR_FTYPE_I_UNEXPECTED);
+				findend(&vfpGetCurrCharPtr(cfVfp));
+				return (-1);
+
+			default:
+				/* unknown ftype */
+
+				/* copy path found to 'lpath' */
+				COPYPATH(lpath, cpath_start, cpath_len);
+
+				setErrstr(ERR_UNKNOWN_FTYPE);
+				findend(&vfpGetCurrCharPtr(cfVfp));
+				return (-1);
+			}
+		}
+
+		/*
+		 * if an exact match all processing is completed; break out of
+		 * the main processing loop and finish processing this entry
+		 * prior to returning to the caller.
+		 */
+
+		if (n == 0) {
+			break;
+		}
+
+		/*
+		 * this entry is not an exact match for the path being searched
+		 * for - if this entry is GREATER THAN the path being searched
+		 * for then finish processing and return GREATER THAN result
+		 * to the caller so the entry for the path being searched for
+		 * can be added to the contents file.
+		 */
+
+		if (n < 0) {
+			/*
+			 * the entry we want would fit BEFORE the one we just
+			 * read, so we need to unread what we've read by
+			 * seeking back to the start of this entry
+			 */
+
+			vfpSetCurrCharPtr(cfVfp, pos);
+
+			/* copy path found to 'lpath' */
+			COPYPATH(lpath, cpath_start, cpath_len);
+
+			/* write out any skipped data before returning */
+			if (dataSkipped && (cfTmpVfp != (VFP_T *)NULL)) {
+				WRITEDATA(cfTmpVfp, firstPos, lastPos);
+			}
+
+			return (2); /* path would insert here */
+		}
+
+		/*
+		 * This entry is "LESS THAN" the specified path to search for
+		 * need to process the next entry from the contents file. First,
+		 * if writing to new contents file, update new contents file if
+		 * processing old style entry; otherwise, update skipped data
+		 * information to remember current last byte of skipped data.
+		 */
+
+		if (cfTmpVfp != (VFP_T *)NULL) {
+			char	*px;
+			ssize_t	len;
+
+			if (rdpath != 0) {
+				/* modify record: write out any skipped data */
+				if (dataSkipped) {
+					WRITEDATA(cfTmpVfp, firstPos, lastPos);
+				}
+
+				/*
+				 * copy what we've read and the rest of this
+				 * line onto the specified output stream
+				 */
+				vfpPutBytes(cfTmpVfp, cpath_start, cpath_len);
+				vfpPutc(cfTmpVfp, c);
+				vfpPutc(cfTmpVfp, ept->ftype);
+				vfpPutc(cfTmpVfp, ' ');
+				vfpPuts(cfTmpVfp, ept->pkg_class);
+
+				px = strchr(vfpGetCurrCharPtr(cfVfp), '\n');
+
+				if (px == (char *)NULL) {
+					len = vfpGetBytesRemaining(cfVfp);
+					vfpPutBytes(cfTmpVfp,
+						vfpGetCurrCharPtr(cfVfp), len);
+					vfpPutc(cfTmpVfp, '\n');
+					vfpSeekToEnd(cfVfp);
+				} else {
+					len = vfpGetCurrPtrDelta(cfVfp, px);
+					vfpPutBytes(cfTmpVfp,
+						vfpGetCurrCharPtr(cfVfp), len);
+					vfpIncCurrPtrBy(cfVfp, len);
+				}
+
+				/* reset skiped bytes if any data skipped */
+				if (dataSkipped) {
+					dataSkipped = 0;
+					lastPos = (char *)NULL;
+					firstPos = vfpGetCurrCharPtr(cfVfp);
+				}
+			} else {
+				/* skip data */
+				dataSkipped = 1;
+
+				px = strchr(vfpGetCurrCharPtr(cfVfp), '\n');
+
+				if (px == (char *)NULL) {
+					vfpSeekToEnd(cfVfp);
+				} else {
+					len = vfpGetCurrPtrDelta(cfVfp, px)+1;
+					vfpIncCurrPtrBy(cfVfp, len);
+				}
+				lastPos = vfpGetCurrCharPtr(cfVfp);
+			}
+		} else {
+			/*
+			 * since this isn't the entry we want, just read the
+			 * stream until we find the end of this entry and
+			 * then start this search loop again
+			 */
+			char	*px;
+
+			px = strchr(vfpGetCurrCharPtr(cfVfp), '\n');
+
+			if (px == (char *)NULL) {
+				vfpSeekToEnd(cfVfp);
+
+				/* copy path found to 'lpath' */
+				COPYPATH(lpath, cpath_start, cpath_len);
+
+				setErrstr(pkg_gt(ERR_MISSING_NEWLINE));
+				findend(&vfpGetCurrCharPtr(cfVfp));
+				return (-1);
+			} else {
+				ssize_t	len;
+
+				len = vfpGetCurrPtrDelta(cfVfp, px)+1;
+				vfpIncCurrPtrBy(cfVfp, len);
+			}
+		}
+	}
+
+	/*
+	 * *********************************************************************
+	 * end of main loop processing entries from contents file
+	 * the loop is broken out of when an exact match for the
+	 * path being searched for has been found and the type is one of:
+	 *   - ?fvelspcbdx
+	 * at this point parsing is at the first character past the full path
+	 * name on an exact match for the path being looked for - parse the
+	 * remainder of the entries information into the ept structure.
+	 * *********************************************************************
+	 */
+
+	/* link/symbolic link must have link destination */
+
+	if (((ept->ftype == 's') || (ept->ftype == 'l')) &&
+					(ept->ainfo.local == NULL)) {
+		/* copy path found to 'lpath' */
+		COPYPATH(lpath, cpath_start, cpath_len);
+
+		setErrstr(ERR_NO_LINK_SOURCE_SPECIFIED);
+		findend(&vfpGetCurrCharPtr(cfVfp));
+		return (-1);
+	}
+
+	/* character/block devices have major/minor device numbers */
+
+	if (((ept->ftype == 'c') || (ept->ftype == 'b'))) {
+		ept->ainfo.major = BADMAJOR;
+		ept->ainfo.minor = BADMINOR;
+		if (getnumvfp(&vfpGetCurrCharPtr(cfVfp), 10,
+				(long *)&ept->ainfo.major, BADMAJOR) ||
+		    getnumvfp(&vfpGetCurrCharPtr(cfVfp), 10,
+				(long *)&ept->ainfo.minor, BADMINOR)) {
+			/* copy path found to 'lpath' */
+			COPYPATH(lpath, cpath_start, cpath_len);
+
+			setErrstr(pkg_gt(ERR_CANNOT_READ_MM_NUMS));
+			findend(&vfpGetCurrCharPtr(cfVfp));
+			return (-1);
+		}
+	}
+
+	/* most types have mode, owner, group identification components */
+
+	if ((ept->ftype == 'd') || (ept->ftype == 'x') || (ept->ftype == 'c') ||
+		(ept->ftype == 'b') || (ept->ftype == 'p') ||
+		(ept->ftype == 'f') || (ept->ftype == 'v') ||
+		(ept->ftype == 'e')) {
+		/* mode, owner, group should be here */
+		if (getnumvfp(&vfpGetCurrCharPtr(cfVfp), 8,
+				(long *)&ept->ainfo.mode, BADMODE) ||
+		    getstr(&vfpGetCurrCharPtr(cfVfp), sizeof (ept->ainfo.owner),
+				ept->ainfo.owner, ISWORDSEP) ||
+		    getstr(&vfpGetCurrCharPtr(cfVfp), sizeof (ept->ainfo.group),
+				ept->ainfo.group, ISWORDSEP)) {
+			/* copy path found to 'lpath' */
+			COPYPATH(lpath, cpath_start, cpath_len);
+
+			setErrstr(ERR_CANNOT_READ_MOG);
+			findend(&vfpGetCurrCharPtr(cfVfp));
+			return (-1);
+		}
+	}
+
+	/* i/f/v/e have size, checksum, modification time components */
+
+	if ((ept->ftype == 'i') || (ept->ftype == 'f') ||
+			(ept->ftype == 'v') || (ept->ftype == 'e')) {
+		/* look for content description */
+		if (getlnumvfp(&vfpGetCurrCharPtr(cfVfp), 10,
+				(fsblkcnt_t *)&ept->cinfo.size, BADCONT) ||
+		    getnumvfp(&vfpGetCurrCharPtr(cfVfp), 10,
+				(long *)&ept->cinfo.cksum, BADCONT) ||
+		    getnumvfp(&vfpGetCurrCharPtr(cfVfp), 10,
+				(long *)&ept->cinfo.modtime, BADCONT)) {
+			/* copy path found to 'lpath' */
+			COPYPATH(lpath, cpath_start, cpath_len);
+
+			setErrstr(ERR_CANNOT_READ_CONTENT_INFO);
+			findend(&vfpGetCurrCharPtr(cfVfp));
+			return (-1);
+		}
+	}
+
+	/* i files processing is completed - return 'exact match found' */
+
+	if (ept->ftype == 'i') {
+		/* copy path found to 'lpath' */
+		COPYPATH(lpath, cpath_start, cpath_len);
+
+		if (getend(&vfpGetCurrCharPtr(cfVfp))) {
+			/* copy path found to 'lpath' */
+			COPYPATH(lpath, cpath_start, cpath_len);
+
+			setErrstr(ERR_EXTRA_TOKENS);
+			return (-1);
+		}
+
+		/* write out any skipped data before returning */
+		if (dataSkipped && (cfTmpVfp != (VFP_T *)NULL)) {
+			WRITEDATA(cfTmpVfp, firstPos, lastPos);
+		}
+
+		return (1);
+	}
+
+	/*
+	 * determine list of packages which reference this entry
+	 */
+
+	lastpinfo = (struct pinfo *)NULL;
+	while ((c = getstr(&vfpGetCurrCharPtr(cfVfp), sizeof (pkgname),
+						pkgname, ISPKGNAMESEP)) <= 0) {
+		/* if c < 0 the string was too long to fix in the buffer */
+
+		if (c < 0) {
+			/* copy path found to 'lpath' */
+			COPYPATH(lpath, cpath_start, cpath_len);
+
+			setErrstr(ERR_PACKAGE_NAME_TOO_LONG);
+			findend(&vfpGetCurrCharPtr(cfVfp));
+			return (-1);
+		}
+
+		/* a package is present - create and populate pinfo structure */
+
+		pinfo = (struct pinfo *)calloc(1, sizeof (struct pinfo));
+		if (!pinfo) {
+			/* copy path found to 'lpath' */
+			COPYPATH(lpath, cpath_start, cpath_len);
+
+			setErrstr(ERR_NO_MEMORY);
+			findend(&vfpGetCurrCharPtr(cfVfp));
+			return (-1);
+		}
+		if (!lastpinfo) {
+			ept->pinfo = pinfo; /* first one */
+		} else {
+			lastpinfo->next = pinfo; /* link list */
+		}
+		lastpinfo = pinfo;
+
+		if ((pkgname[0] == '-') || (pkgname[0] == '+') ||
+			(pkgname[0] == '*') || (pkgname[0] == '~') ||
+			(pkgname[0] == '!') || (pkgname[0] == '%')) {
+			pinfo->status = pkgname[0];
+			(void) strlcpy(pinfo->pkg, pkgname+1,
+							sizeof (pinfo->pkg));
+		} else {
+			(void) strlcpy(pinfo->pkg, pkgname,
+							sizeof (pinfo->pkg));
+		}
+
+		/* pkg/[:[ftype][:class] */
+		c = (vfpGetc(cfVfp));
+		if (c == '\\') {
+			/* get alternate ftype */
+			pinfo->editflag++;
+			c = (vfpGetc(cfVfp));
+		}
+
+		if (c == ':') {
+			/* get special classname */
+			(void) getstr(&vfpGetCurrCharPtr(cfVfp),
+				sizeof (classname), classname, ISWORDSEP);
+			(void) strlcpy(pinfo->aclass, classname,
+							sizeof (pinfo->aclass));
+			c = (vfpGetc(cfVfp));
+		}
+		ept->npkgs++;
+
+		/* break out of while if at end of entry */
+
+		if ((c == '\n') || (c == '\0')) {
+			break;
+		}
+
+		/* if package not separated by a space return an error */
+
+		if (!isspace(c)) {
+			/* copy path found to 'lpath' */
+			COPYPATH(lpath, cpath_start, cpath_len);
+
+			setErrstr(ERR_BAD_ENTRY_END);
+			findend(&vfpGetCurrCharPtr(cfVfp));
+			return (-1);
+		}
+	}
+
+	/*
+	 * parsing of the entry is complete
+	 */
+
+	/* copy path found to 'lpath' */
+	COPYPATH(lpath, cpath_start, cpath_len);
+
+	/* write out any skipped data before returning */
+	if (dataSkipped && (cfTmpVfp != (VFP_T *)NULL)) {
+		WRITEDATA(cfTmpVfp, firstPos, lastPos);
+	}
+
+	/* if not at the end of the entry, make it so */
+
+	if ((c != '\n') && (c != '\0')) {
+		if (getend(&vfpGetCurrCharPtr(cfVfp)) && ept->pinfo) {
+			setErrstr(ERR_EXTRA_TOKENS);
+			return (-1);
+		}
+	}
+
+	return (1);
+}
+
+static int
+getstr(char **cp, int n, char *str, int separator[])
+{
+	int	c;
+	char	*p = *cp;
+	char	*p1;
+	size_t	len;
+
+	if (*p == '\0') {
+		return (1);
+	}
+
+	/* leading white space ignored */
+
+	while (((c = *p) != '\0') && (isspace(*p++)))
+		;
+	if ((c == '\0') || (c == '\n')) {
+		p--;
+		*cp = p;
+		return (1); /* nothing there */
+	}
+
+	p--;
+
+	/* compute length based on delimiter found or not */
+
+	p1 = p;
+	while (separator[(int)*p1] == 0) {
+		p1++;
+	}
+
+	len = (ptrdiff_t)p1 - (ptrdiff_t)p;
+
+	/* if string will fit in result buffer copy string and return success */
+
+	if (len < n) {
+		(void) memcpy(str, p, len);
+		str[len] = '\0';
+		p += len;
+		*cp = p;
+		return (0);
+	}
+
+	/* result buffer too small; copy partial string, return error */
+	(void) memcpy(str, p, n-1);
+	str[n-1] = '\0';
+	p += n;
+	*cp = p;
+	return (-1);
+}
+
+static int
+getend(char **cp)
+{
+	int	n;
+	char	*p = *cp;
+
+	n = 0;
+
+	/* if at end of buffer return no more characters left */
+
+	if (*p == '\0') {
+		return (0);
+	}
+
+	while ((*p != '\0') && (*p != '\n')) {
+		if (n == 0) {
+			if (!isspace(*p)) {
+				n++;
+			}
+		}
+		p++;
+	}
+
+	*cp = ++p;
+	return (n);
+}
+
+static void
+findend(char **cp)
+{
+	char	*p1;
+	char	*p = *cp;
+
+	/* if at end of buffer return no more characters left */
+
+	if (*p == '\0') {
+		return;
+	}
+
+	/* find the end of the line */
+
+	p1 = strchr(p, '\n');
+
+	if (p1 != (char *)NULL) {
+		*cp = ++p1;
+		return;
+	}
+
+	*cp = strchr(p, '\0');
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/usr/src/lib/libpkg/common/tputcfent.c	Fri Jun 05 10:28:40 2009 -0400
@@ -0,0 +1,191 @@
+/*
+ * 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
+ * or http://www.opensolaris.org/os/licensing.
+ * 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 2009 Sun Microsystems, Inc.  All rights reserved.
+ * Use is subject to license terms.
+ */
+
+/* Copyright (c) 1984, 1986, 1987, 1988, 1989 AT&T */
+/* All Rights Reserved */
+
+
+
+#include <stdio.h>
+#include <string.h>
+#include <limits.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <time.h>
+#include <sys/types.h>
+#include "pkgstrct.h"
+#include "pkglocale.h"
+
+#define	MSG_INVALID	"invalid entry"
+
+void
+tputcfent(struct cfent *ept, FILE *fp)
+{
+	int	count, status;
+	char	*pt;
+	struct pinfo *pinfo;
+	struct	tm	*timep;
+	char	timeb[BUFSIZ];
+
+	if (ept->path == NULL)
+		return;
+
+	(void) fprintf(fp, pkg_gt("Pathname: %s\n"), ept->path);
+	(void) fprintf(fp, pkg_gt("Type: "));
+
+	switch (ept->ftype) {
+	    case 'f':
+		(void) fputs(pkg_gt("regular file\n"), fp);
+		break;
+
+	    case 'd':
+		(void) fputs(pkg_gt("directory\n"), fp);
+		break;
+
+	    case 'x':
+		(void) fputs(pkg_gt("exclusive directory\n"), fp);
+		break;
+
+	    case 'v':
+		(void) fputs(pkg_gt("volatile file\n"), fp);
+		break;
+
+	    case 'e':
+		(void) fputs(pkg_gt("editted file\n"), fp);
+		break;
+
+	    case 'p':
+		(void) fputs(pkg_gt("named pipe\n"), fp);
+		break;
+
+	    case 'i':
+		(void) fputs(pkg_gt("installation file\n"), fp);
+		break;
+
+	    case 'c':
+	    case 'b':
+		(void) fprintf(fp, pkg_gt("%s special device\n"),
+		    (ept->ftype == 'b') ? pkg_gt("block") :
+		    pkg_gt("character"));
+
+		if (ept->ainfo.major == BADMAJOR)
+			(void) fprintf(fp, pkg_gt("Major device number: %s\n"),
+			    MSG_INVALID);
+		else
+			(void) fprintf(fp, pkg_gt("Major device number: %d\n"),
+			    ept->ainfo.major);
+
+		if (ept->ainfo.minor == BADMINOR)
+			(void) fprintf(fp, pkg_gt("Minor device number: %s\n"),
+			    MSG_INVALID);
+		else
+			(void) fprintf(fp, pkg_gt("Minor device number: %d\n"),
+			    ept->ainfo.minor);
+
+		break;
+
+	    case 'l':
+		(void) fputs(pkg_gt("linked file\n"), fp);
+		pt = (ept->ainfo.local ? ept->ainfo.local :
+		    (char *)pkg_gt("(unknown)"));
+		(void) fprintf(fp, pkg_gt("Source of link: %s\n"), pt);
+		break;
+
+	    case 's':
+		(void) fputs(pkg_gt("symbolic link\n"), fp);
+		pt = (ept->ainfo.local ? ept->ainfo.local :
+		    (char *)pkg_gt("(unknown)"));
+		(void) fprintf(fp, pkg_gt("Source of link: %s\n"), pt);
+		break;
+
+	    default:
+		(void) fputs(pkg_gt("unknown\n"), fp);
+		break;
+	}
+
+	if (!strchr("lsin", ept->ftype)) {
+		if (ept->ainfo.mode == BADMODE)
+			(void) fprintf(fp, pkg_gt("Expected mode: %s\n"),
+			    "?");
+		else
+			(void) fprintf(fp, pkg_gt("Expected mode: %04o\n"),
+			    ept->ainfo.mode);
+
+		(void) fprintf(fp, pkg_gt("Expected owner: %s\n"),
+		    ept->ainfo.owner);
+		(void) fprintf(fp, pkg_gt("Expected group: %s\n"),
+		    ept->ainfo.group);
+	}
+	if (strchr("?infv", ept->ftype)) {
+		(void) fprintf(fp,
+		    pkg_gt("Expected file size (bytes): %llu\n"),
+		    ept->cinfo.size);
+		(void) fprintf(fp,
+		    pkg_gt("Expected sum(1) of contents: %ld\n"),
+		    ept->cinfo.cksum);
+		if (ept->cinfo.modtime > 0) {
+			timep = localtime(&(ept->cinfo.modtime));
+			strftime(timeb, sizeof (timeb),
+			    pkg_gt("Expected last modification: %b %d %X %Y\n"),
+			    timep);
+			(void) fprintf(fp, timeb);
+		} else
+			(void) fprintf(fp,
+			    pkg_gt("Expected last modification: ?\n"));
+	}
+	if (ept->ftype == 'i') {
+		(void) fputc('\n', fp);
+		return;
+	}
+
+	status = count = 0;
+	if ((pinfo = ept->pinfo) != NULL) {
+		(void) fprintf(fp,
+		    pkg_gt("Referenced by the following packages:\n\t"));
+		while (pinfo) {
+			/*
+			 * Check for partially installed object.  Need
+			 * to explicitly check for '!', because objects
+			 * that are provided by a server will have a
+			 * different status character.
+			 */
+			if (pinfo->status == '!')
+				status++;
+			(void) fprintf(fp, "%-15s", pinfo->pkg);
+			if ((++count % 5) == 0) {
+				(void) fputc('\n', fp);
+				(void) fputc('\t', fp);
+				count = 0;
+			}
+			pinfo = pinfo->next;
+		}
+		(void) fputc('\n', fp);
+	}
+	(void) fprintf(fp, pkg_gt("Current status: %s\n"),
+	    status ? pkg_gt("partially installed") :
+	    pkg_gt("installed"));
+	(void) fputc('\n', fp);
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/usr/src/lib/libpkg/common/verify.c	Fri Jun 05 10:28:40 2009 -0400
@@ -0,0 +1,989 @@
+/*
+ * 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
+ * or http://www.opensolaris.org/os/licensing.
+ * 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 2009 Sun Microsystems, Inc.  All rights reserved.
+ * Use is subject to license terms.
+ */
+
+/* Copyright (c) 1984, 1986, 1987, 1988, 1989 AT&T */
+/* All Rights Reserved */
+
+
+
+#include <stdio.h>
+#include <limits.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <utime.h>
+#include <sys/types.h>
+#include <sys/param.h>
+#include <sys/stat.h>
+#include <sys/statvfs.h>
+#include <grp.h>
+#include <pwd.h>
+#include <errno.h>
+#include <string.h>
+#include <stdarg.h>
+#include <fcntl.h>
+#include <sys/mkdev.h>
+#include "pkgstrct.h"
+#include "pkglib.h"
+#include "pkglibmsgs.h"
+#include "pkglocale.h"
+
+#define	WDMSK	0xFFFF
+#define	DATEFMT	"%D %r"
+#define	LONG_BOUNDARY	((sizeof (unsigned long))-1)
+#define	CHUNK	1024*1024
+
+static char	theErrBuf[PATH_MAX+512] = {'\0'};
+static char	*theErrStr = NULL;
+
+/* checksum disable switch */
+static int	enable_checksum = 1;
+
+/* attribute disable flag */
+static int	disable_attributes = 0;
+
+/* non-ABI symlinks supported */
+static int	nonabi_symlinks;
+
+/*
+ * forward declarations
+ */
+
+static int	clear_target(char *path, char *ftype, int is_a_dir);
+
+unsigned	long compute_checksum(int *r_err, char *path);
+
+/* union used to generate checksum */
+typedef union hilo {
+	struct part {
+		uint16_t hi;
+		uint16_t lo;
+	} hl;
+	uint32_t	lg;
+} CHECKSUM_T;
+
+/*PRINTFLIKE1*/
+static void
+reperr(char *fmt, ...)
+{
+	char	*pt;
+	ssize_t	ptln;
+	va_list	ap;
+	int	n;
+
+	if (fmt == (char *)NULL) {
+		theErrBuf[0] = '\0';
+	} else {
+		if (n = strlen(theErrBuf)) {
+			pt = theErrBuf + n;
+			*pt++ = '\n';
+			*pt = '\0';
+			ptln = sizeof (theErrBuf)-n;
+		} else {
+			pt = theErrBuf;
+			ptln = sizeof (theErrBuf);
+		}
+		va_start(ap, fmt);
+		/* LINTED variable format specifier to vsnprintf() */
+		(void) vsnprintf(pt, ptln, fmt, ap);
+		va_end(ap);
+	}
+}
+
+/*
+ * Name:	cverify
+ * Description:	This function verifies and (if fix > 0) fixes the contents
+ *		of the file at the path provided
+ * Arguments:	fix - 0 - do not fix entries, 1 - fix entries
+ *		ftype - single character "type" the entry is supposed to be
+ *		path - path to file
+ *		cinfo - content info structure representing the contents
+ *			the entry is supposed to contain
+ *		allow_checksum - determine if checksumming should be disabled:
+ *		 == 0 - do not perform checksum ever - override enable_checksum.
+ *		 != 0 - use the default checksum flag "enable_checksum" to
+ *			determine if checksumming should be done.
+ * NOTE:	modification and creation times can be repaired; the contents
+ *		of the file cannot be corrected if the checksum indicates that
+ *		the contents are not correct - VE_CONT will be returned in this
+ *		case.
+ * Possible return values:
+ * - 0 = successful
+ * - VE_EXIST = path name does not exist
+ * - VE_FTYPE = path file type is not recognized, is not supported,
+ *		or is not what was expected
+ * - VE_ATTR = path mode/group/user is not what was expected
+ * - VE_CONT = mod time/link target/major/minor/size/file system type/current
+ *		directory is not what was expected
+ * - VE_FAIL = utime/target directory/link/stat/symlink/mknod/chmod/statvfs/
+ *		chown failed
+ */
+
+int
+cverify(int fix, char *ftype, char *path, struct cinfo *cinfo,
+	int allow_checksum)
+{
+	struct stat	status; 	/* file status buffer */
+	struct utimbuf	times;
+	unsigned long	mycksum;
+	int		setval, retcode;
+	char		tbuf1[512];
+	char		tbuf2[512];
+	int		cksumerr;
+
+	setval = (*ftype == '?');
+	retcode = 0;
+	reperr(NULL);
+
+	if (stat(path, &status) < 0) {
+		reperr(pkg_gt(ERR_EXIST));
+		return (VE_EXIST);
+	}
+
+	/* -1	requires modtimes to be the same */
+	/*  0   reports modtime failure */
+	/*  1   fixes modtimes */
+
+	if (setval || (cinfo->modtime == BADCONT)) {
+		cinfo->modtime = status.st_mtime;
+	} else if (status.st_mtime != cinfo->modtime) {
+		if (fix > 0) {
+			/* reset times on the file */
+			times.actime = cinfo->modtime;
+			times.modtime = cinfo->modtime;
+			if (utime(path, &times)) {
+				reperr(pkg_gt(ERR_MODFAIL));
+				retcode = VE_FAIL;
+			}
+		} else if (fix < 0) {
+			/* modtimes must be the same */
+			if (strftime(tbuf1, sizeof (tbuf1), DATEFMT,
+				localtime(&cinfo->modtime)) == 0) {
+				reperr(pkg_gt(ERR_MEM));
+			}
+			if (strftime(tbuf2, sizeof (tbuf2), DATEFMT,
+				localtime(&status.st_mtime)) == 0) {
+				reperr(pkg_gt(ERR_MEM));
+			}
+			reperr(pkg_gt(ERR_MTIME), tbuf1, tbuf2);
+			retcode = VE_CONT;
+		}
+	}
+
+	if (setval || (cinfo->size == (fsblkcnt_t)BADCONT)) {
+		cinfo->size = status.st_size;
+	} else if (status.st_size != cinfo->size) {
+		if (!retcode) {
+			retcode = VE_CONT;
+		}
+		reperr(pkg_gt(ERR_SIZE), cinfo->size, status.st_size);
+	}
+
+	cksumerr = 0;
+
+	/*
+	 * see if checksumming should be done: if checksumming is allowed,
+	 * and checksumming is enabled, then checksum the file.
+	 */
+
+	/* return if no need to compute checksum */
+
+	if ((allow_checksum == 0) || (enable_checksum == 0)) {
+		return (retcode);
+	}
+
+	/* compute checksum */
+
+	mycksum = compute_checksum(&cksumerr, path);
+
+	/* set value if not set or if checksum cannot be computed */
+
+	if (setval || (cinfo->cksum == BADCONT)) {
+		cinfo->cksum = mycksum;
+		return (retcode);
+	}
+
+	/* report / return error if checksums mismatch or there is an error */
+
+	if ((mycksum != cinfo->cksum) || cksumerr) {
+		if (!retcode) {
+			retcode = VE_CONT;
+		}
+		if (!cksumerr) {
+			reperr(pkg_gt(ERR_CKSUM), cinfo->cksum, mycksum);
+		}
+	}
+
+	return (retcode);
+}
+
+/*
+ * Name:	compute_checksum
+ * Description:	generate checksum for specified file
+ * Arguments:	r_cksumerr (int *) [RO, *RW]
+ *			- pointer to integer that is set on return to:
+ *				== 0 - no error occurred
+ *				!= 0 - error occurred
+ *		a_path (char *) [RO, *RO]
+ *			- pointer to string representing path to file to
+ *			  generate checksum of
+ * Returns:	unsigned long - results:
+ *			- If *r_cksumerr == 0, checksum of specified file
+ *			- If *r_cksumerr != 0, undefined
+ */
+unsigned long
+compute_checksum(int *r_cksumerr, char *a_path)
+{
+	CHECKSUM_T	suma;	/* to split four-bytes into 2 two-byte values */
+	CHECKSUM_T	tempa;
+	int		fd;
+	uint32_t	lg;	/* running checksum value */
+	uint32_t	buf[CHUNK/4]; /* to read CHUNK bytes */
+	uint32_t	lsavhi;	/* high order two-bytes of four-byte checksum */
+	uint32_t	lsavlo;	/* low order two-bytes of four-byte checksum */
+	int		leap = sizeof (uint32_t);
+	int		notyet = 0;
+	int		nread;
+	struct stat64	sbuf;
+
+	/* reset error flag */
+	*r_cksumerr = 0;
+
+	/* open file and obtain -> where file is mapped/read */
+	if ((fd = open(a_path, O_RDONLY)) < 0) {
+		*r_cksumerr = 1;
+		reperr(pkg_gt(ERR_NO_CKSUM));
+		perror(ERR_NO_CKSUM);
+		return (0);
+	}
+
+	if (fstat64(fd, &sbuf) != 0) {
+		*r_cksumerr = 1;
+		reperr(pkg_gt(ERR_NO_CKSUM));
+		perror(ERR_NO_CKSUM);
+		return (0);
+	}
+
+	/* initialize checksum value */
+	lg = 0;
+
+	/*
+	 * Read CHUNK bytes off the file at a time; Read size of long bytes
+	 * from memory at a time and process them.
+	 * If last read, then read remnant bytes and process individually.
+	 */
+	errno = 0;
+	while ((nread = read(fd, (void*)buf,
+		    (sbuf.st_size < CHUNK) ? sbuf.st_size : CHUNK)) > 0) {
+		uchar_t *s;
+		uint32_t *p = buf;
+
+		notyet = nread % leap;
+		nread -= notyet;
+
+		for (; nread > 0; nread -= leap) {
+			lg += ((((*p)>>24)&0xFF) & WDMSK);
+			lg += ((((*p)>>16)&0xFF) & WDMSK);
+			lg += ((((*p)>>8)&0xFF) & WDMSK);
+			lg += (((*p)&0xFF) & WDMSK);
+			p++;
+		}
+		s = (uchar_t *)p;
+		/* leftover bytes less than four in number */
+		while (notyet--)
+			lg += (((uint32_t)(*s++)) & WDMSK);
+	}
+
+	/* wind up */
+	(void) close(fd);
+
+	/* compute checksum components */
+	suma.lg = lg;
+	tempa.lg = (suma.hl.lo & WDMSK) + (suma.hl.hi & WDMSK);
+	lsavhi = (uint32_t)tempa.hl.hi;
+	lsavlo = (uint32_t)tempa.hl.lo;
+
+	/* return final checksum value */
+	return (lsavhi+lsavlo);
+}
+
+static 	struct stat	status; 	/* file status buffer */
+static  struct statvfs	vfsstatus;	/* filesystem status buffer */
+
+/*
+ * Remove the thing that's currently in place so we can put down the package
+ * object. If we're replacing a directory with a directory, leave it alone.
+ * Returns 1 if all OK and 0 if failed.
+ */
+static int
+clear_target(char *path, char *ftype, int is_a_dir)
+{
+	int retcode = 1;
+
+	if (is_a_dir) {	/* if there's a directory there already ... */
+		/* ... and this isn't, ... */
+		if ((*ftype != 'd') && (*ftype != 'x')) {
+			if (rmdir(path)) {	/* try to remove it. */
+				reperr(pkg_gt(ERR_RMDIR), path);
+				retcode = 0;
+			}
+		}
+	} else {
+		if (remove(path)) {
+			if (errno != ENOENT) {
+				retcode = 0;	/* It didn't work. */
+			}
+		}
+	}
+
+	return (retcode);
+}
+
+/*
+ * Name:	averify
+ * Description:	This function verifies and (if fix > 0) fixes the attributes
+ *		of the file at the path provided.
+ * Arguments:	fix - 0 - do not fix entries, 1 - fix entries
+ *		ftype - single character "type" the entry is supposed to be
+ *		path - path to file
+ *		ainfo - attribute info structure representing the attributes
+ *			the entry is supposed to be
+ * NOTE:	attributes are links and permissions
+ * Possible return values:
+ * - 0 = successful
+ * - VE_EXIST = path name does not exist
+ * - VE_FTYPE = path file type is not recognized, is not supported,
+ *		or is not what was expected
+ * - VE_ATTR = path mode/group/user is not what was expected
+ * - VE_CONT = mod time/link target/major/minor/size/file system type/current
+ *		directory is not what was expected
+ * - VE_FAIL = utime/target directory/link/stat/symlink/mknod/chmod/statvfs/
+ *		chown failed
+ */
+int
+averify(int fix, char *ftype, char *path, struct ainfo *ainfo)
+{
+	struct group	*grp; 	/* group entry buffer */
+	struct passwd	*pwd;
+	int		n;
+	int		setval;
+	int		uid, gid;
+	int		dochown;
+	int		retcode;
+	int		statError = 0;
+	int		targ_is_dir = 0;	/* replacing a directory */
+	char		myftype;
+	char		buf[PATH_MAX];
+	ino_t		my_ino;
+	dev_t		my_dev;
+	char 		cwd[MAXPATHLEN];
+	char 		*cd;
+	char 		*c;
+
+	setval = (*ftype == '?');
+	retcode = 0;
+	reperr(NULL);
+
+	if (get_disable_attribute_check()) {
+		return (0);
+	}
+
+	if (*ftype == 'l') {
+		if (stat(path, &status) < 0) {
+			retcode = VE_EXIST;
+			reperr(pkg_gt(ERR_EXIST));
+		}
+
+		my_ino = status.st_ino;
+		my_dev = status.st_dev;
+
+		/* Get copy of the current working directory */
+		if (getcwd(cwd, MAXPATHLEN) == NULL) {
+			reperr(pkg_gt(ERR_GETWD), ainfo->local);
+			return (VE_FAIL);
+		}
+
+		/*
+		 * Change to the directory in which the hard
+		 * link is to be created.
+		 */
+		cd = strdup(path);
+		c = strrchr(cd, '/');
+		if (c) {
+			/* bugid 4247895 */
+			if (strcmp(cd, c) == 0)
+				strcpy(cd, "/");
+			else
+				*c = NULL;
+
+			if (chdir(cd) != 0) {
+				reperr(pkg_gt(ERR_CHDIR), cd);
+				return (VE_FAIL);
+			}
+		}
+		free(cd);
+
+		if (retcode || (status.st_nlink < 2) ||
+		    (stat(ainfo->local, &status) < 0) ||
+		    (my_dev != status.st_dev) || (my_ino != status.st_ino)) {
+			if (fix) {
+				/*
+				 * Don't want to do a hard link to a
+				 * directory.
+				 */
+				if (!isdir(ainfo->local)) {
+					chdir(cwd);
+					reperr(pkg_gt(ERR_LINKISDIR),
+					    ainfo->local);
+					return (VE_FAIL);
+				}
+				/* Now do the link. */
+				if (!clear_target(path, ftype, targ_is_dir))
+					return (VE_FAIL);
+
+				if (link(ainfo->local, path)) {
+					chdir(cwd);
+					reperr(pkg_gt(ERR_LINKFAIL),
+					    ainfo->local);
+					return (VE_FAIL);
+				}
+				retcode = 0;
+			} else {
+				/* Go back to previous working directory */
+				if (chdir(cwd) != 0)
+					reperr(pkg_gt(ERR_CHDIR), cwd);
+
+				reperr(pkg_gt(ERR_LINK), ainfo->local);
+				return (VE_CONT);
+			}
+		}
+
+		/* Go back to previous working directory */
+		if (chdir(cwd) != 0) {
+			reperr(pkg_gt(ERR_CHDIR), cwd);
+			return (VE_CONT);
+		}
+
+		return (retcode);
+	}
+
+	retcode = 0;
+
+	/* If we are to process symlinks the old way then we follow the link */
+	if (nonABI_symlinks()) {
+		if ((*ftype == 's') ? lstat(path, &status) :
+			stat(path, &status)) {
+			reperr(pkg_gt(ERR_EXIST));
+			retcode = VE_EXIST;
+			myftype = '?';
+			statError++;
+		}
+	/* If not then we inspect the target of the link */
+	} else {
+		if ((n = lstat(path, &status)) == -1) {
+			reperr(pkg_gt(ERR_EXIST));
+			retcode = VE_EXIST;
+			myftype = '?';
+			statError++;
+		}
+	}
+	if (!statError) {
+		/* determining actual type of existing object */
+		switch (status.st_mode & S_IFMT) {
+		    case S_IFLNK:
+			myftype = 's';
+			break;
+
+		    case S_IFIFO:
+			myftype = 'p';
+			break;
+
+		    case S_IFCHR:
+			myftype = 'c';
+			break;
+
+		    case S_IFDIR:
+			myftype = 'd';
+			targ_is_dir = 1;
+			break;
+
+		    case S_IFBLK:
+			myftype = 'b';
+			break;
+
+		    case S_IFREG:
+		    case 0:
+			myftype = 'f';
+			break;
+
+		    case S_IFDOOR:
+			myftype = 'D';
+			break;
+
+		    default:
+			reperr(pkg_gt(ERR_UNKNOWN));
+			return (VE_FTYPE);
+		}
+	}
+
+	if (setval) {
+		/*
+		 * Check to make sure that a package or an installf that uses
+		 * wild cards '?' to assume the ftype of an object on the
+		 * system is not assuming a door ftype. Doors are not supported
+		 * but should be ignored.
+		 */
+		if (myftype == 'D') {
+			reperr(pkg_gt(ERR_FTYPED), path);
+			retcode = VE_FTYPE;
+			return (VE_FTYPE);
+		} else {
+			*ftype = myftype;
+		}
+	} else if (!retcode && (*ftype != myftype) &&
+	    ((myftype != 'f') || !strchr("ilev", *ftype)) &&
+	    ((myftype != 'd') || (*ftype != 'x'))) {
+		reperr(pkg_gt(ERR_FTYPE), *ftype, myftype);
+		retcode = VE_FTYPE;
+	}
+
+	if (!retcode && (*ftype == 's')) {
+		/* make sure that symbolic link is correct */
+		n = readlink(path, buf, PATH_MAX);
+		if (n < 0) {
+			reperr(pkg_gt(ERR_SLINK), ainfo->local);
+			retcode = VE_CONT;
+		} else if (ainfo->local != NULL) {
+			buf[n] = '\0';
+			if (strcmp(buf, ainfo->local)) {
+				reperr(pkg_gt(ERR_SLINK), ainfo->local);
+				retcode = VE_CONT;
+			}
+		} else if (ainfo->local == NULL) {
+			/*
+			 * Since a sym link target exists, insert it
+			 * into the ainfo structure
+			 */
+			buf[n] = '\0';
+			ainfo->local = strdup(buf);
+		}
+	}
+
+	if (retcode) {
+		/* The path doesn't exist or is different than it should be. */
+		if (fix) {
+			/*
+			 * Clear the way for the write. If it won't clear,
+			 * there's nothing we can do.
+			 */
+			if (!clear_target(path, ftype, targ_is_dir))
+				return (VE_FAIL);
+
+			if ((*ftype == 'd') || (*ftype == 'x')) {
+				char	*pt, *p;
+
+				/* Try to make it the easy way */
+				if (mkdir(path, ainfo->mode)) {
+					/*
+					 * Failing that, walk through the
+					 * parent directories creating
+					 * whatever is needed.
+					 */
+					p = strdup(path);
+					pt = (*p == '/') ? p+1 : p;
+					do {
+						if (pt = strchr(pt, '/'))
+							*pt = '\0';
+						if (access(p, 0) &&
+						    mkdir(p, ainfo->mode))
+							break;
+						if (pt)
+							*pt++ = '/';
+					} while (pt);
+					free(p);
+				}
+				if (stat(path, &status) < 0) {
+					reperr(pkg_gt(ERR_DIRFAIL));
+					return (VE_FAIL);
+				}
+			} else if (*ftype == 's') {
+				if (symlink(ainfo->local, path)) {
+					reperr(pkg_gt(ERR_SLINKFAIL),
+					    ainfo->local);
+					return (VE_FAIL);
+				}
+
+			} else if (*ftype == 'c') {
+				int wilddevno = 0;
+				/*
+				 * The next three if's support 2.4 and older
+				 * packages that use "?" as device numbers.
+				 * This should be considered for removal by
+				 * release 2.7 or so.
+				 */
+				if (ainfo->major == BADMAJOR) {
+					ainfo->major = 0;
+					wilddevno = 1;
+				}
+
+				if (ainfo->minor == BADMINOR) {
+					ainfo->minor = 0;
+					wilddevno = 1;
+				}
+
+				if (wilddevno) {
+					wilddevno = 0;
+					logerr(MSG_WLDDEVNO, path,
+					    ainfo->major, ainfo->minor);
+				}
+
+				if (mknod(path, ainfo->mode | S_IFCHR,
+#ifdef SUNOS41
+				    makedev(ainfo->xmajor, ainfo->xminor)) ||
+#else
+				    makedev(ainfo->major, ainfo->minor)) ||
+#endif
+				    (stat(path, &status) < 0)) {
+					reperr(pkg_gt(ERR_CDEVFAIL));
+					return (VE_FAIL);
+				}
+			} else if (*ftype == 'b') {
+				int wilddevno = 0;
+				/*
+				 * The next three if's support 2.4 and older
+				 * packages that use "?" as device numbers.
+				 * This should be considered for removal by
+				 * release 2.7 or so.
+				 */
+				if (ainfo->major == BADMAJOR) {
+					ainfo->major = 0;
+					wilddevno = 1;
+				}
+
+				if (ainfo->minor == BADMINOR) {
+					ainfo->minor = 0;
+					wilddevno = 1;
+				}
+
+				if (wilddevno) {
+					wilddevno = 0;
+					logerr(MSG_WLDDEVNO, path,
+					    ainfo->major, ainfo->minor);
+				}
+
+				if (mknod(path, ainfo->mode | S_IFBLK,
+#ifdef SUNOS41
+				    makedev(ainfo->xmajor, ainfo->xminor)) ||
+#else
+				    makedev(ainfo->major, ainfo->minor)) ||
+#endif
+				    (stat(path, &status) < 0)) {
+					reperr(pkg_gt(ERR_BDEVFAIL));
+					return (VE_FAIL);
+				}
+			} else if (*ftype == 'p') {
+				if (mknod(path, ainfo->mode | S_IFIFO, NULL) ||
+				    (stat(path, &status) < 0)) {
+					reperr(pkg_gt(ERR_PIPEFAIL));
+					return (VE_FAIL);
+				}
+			} else
+				return (retcode);
+
+		} else
+			return (retcode);
+	}
+
+	if (*ftype == 's')
+		return (0); /* don't check anything else */
+	if (*ftype == 'i')
+		return (0); /* don't check anything else */
+
+	retcode = 0;
+	if ((myftype == 'c') || (myftype == 'b')) {
+#ifdef SUNOS41
+		if (setval || (ainfo->xmajor < 0))
+			ainfo->xmajor = ((status.st_rdev>>8)&0377);
+		if (setval || (ainfo->xminor < 0))
+			ainfo->xminor = (status.st_rdev&0377);
+		/* check major & minor */
+		if (status.st_rdev != makedev(ainfo->xmajor, ainfo->xminor)) {
+			reperr(pkg_gt(ERR_MAJMIN), ainfo->xmajor,
+			    ainfo->xminor,
+				(status.st_rdev>>8)&0377, status.st_rdev&0377);
+			retcode = VE_CONT;
+		}
+#else
+		if (setval || (ainfo->major == BADMAJOR))
+			ainfo->major = major(status.st_rdev);
+		if (setval || (ainfo->minor == BADMINOR))
+			ainfo->minor = minor(status.st_rdev);
+		/* check major & minor */
+		if (status.st_rdev != makedev(ainfo->major, ainfo->minor)) {
+			reperr(pkg_gt(ERR_MAJMIN), ainfo->major, ainfo->minor,
+			    major(status.st_rdev), minor(status.st_rdev));
+			retcode = VE_CONT;
+		}
+#endif
+	}
+
+	/* compare specified mode w/ actual mode excluding sticky bit */
+	if (setval || (ainfo->mode == BADMODE) || (ainfo->mode == WILDCARD))
+		ainfo->mode = status.st_mode & 07777;
+	else if ((ainfo->mode & 06777) != (status.st_mode & 06777)) {
+		if (fix) {
+			if ((ainfo->mode == BADMODE) ||
+			    (chmod(path, ainfo->mode) < 0))
+				retcode = VE_FAIL;
+		} else {
+			reperr(pkg_gt(ERR_PERM), ainfo->mode,
+				status.st_mode & 07777);
+			if (!retcode)
+				retcode = VE_ATTR;
+		}
+	}
+
+	dochown = 0;
+
+	/* get group entry for specified group */
+	if (setval || strcmp(ainfo->group, BADGROUP) == 0) {
+		grp = cgrgid(status.st_gid);
+		if (grp)
+			(void) strcpy(ainfo->group, grp->gr_name);
+		else {
+			if (!retcode)
+				retcode = VE_ATTR;
+			reperr(pkg_gt(ERR_BADGRPID), status.st_gid);
+		}
+		gid = status.st_gid;
+	} else if ((grp = cgrnam(ainfo->group)) == NULL) {
+		reperr(pkg_gt(ERR_BADGRPNM), ainfo->group);
+		if (!retcode)
+			retcode = VE_ATTR;
+	} else if ((gid = grp->gr_gid) != status.st_gid) {
+		if (fix) {
+			/* save specified GID */
+			gid = grp->gr_gid;
+			dochown++;
+		} else {
+			if ((grp = cgrgid((int)status.st_gid)) ==
+			    (struct group *)NULL) {
+				reperr(pkg_gt(ERR_GROUP), ainfo->group,
+				    "(null)");
+			} else {
+				reperr(pkg_gt(ERR_GROUP), ainfo->group,
+				    grp->gr_name);
+			}
+			if (!retcode)
+				retcode = VE_ATTR;
+		}
+	}
+
+	/* get password entry for specified owner */
+	if (setval || strcmp(ainfo->owner, BADOWNER) == 0) {
+		pwd = cpwuid((int)status.st_uid);
+		if (pwd)
+			(void) strcpy(ainfo->owner, pwd->pw_name);
+		else {
+			if (!retcode)
+				retcode = VE_ATTR;
+			reperr(pkg_gt(ERR_BADUSRID), status.st_uid);
+		}
+		uid = status.st_uid;
+	} else if ((pwd = cpwnam(ainfo->owner)) == NULL) {
+		/* UID does not exist in password file */
+		reperr(pkg_gt(ERR_BADUSRNM), ainfo->owner);
+		if (!retcode)
+			retcode = VE_ATTR;
+	} else if ((uid = pwd->pw_uid) != status.st_uid) {
+		/* get owner name for actual UID */
+		if (fix) {
+			uid = pwd->pw_uid;
+			dochown++;
+		} else {
+			pwd = cpwuid((int)status.st_uid);
+			if (pwd == NULL)
+				reperr(pkg_gt(ERR_BADUSRID),
+				    (int)status.st_uid);
+			else
+				reperr(pkg_gt(ERR_OWNER), ainfo->owner,
+				    pwd->pw_name);
+
+			if (!retcode)
+				retcode = VE_ATTR;
+		}
+	}
+
+	if (statvfs(path, &vfsstatus) < 0) {
+		reperr(pkg_gt(ERR_EXIST));
+		retcode = VE_FAIL;
+	} else {
+		if (dochown) {
+			/* pcfs doesn't support file ownership */
+			if (strcmp(vfsstatus.f_basetype, "pcfs") != 0 &&
+			    chown(path, uid, gid) < 0) {
+				retcode = VE_FAIL; /* chown failed */
+			}
+		}
+	}
+
+	if (retcode == VE_FAIL)
+		reperr(pkg_gt(ERR_ATTRFAIL));
+	return (retcode);
+}
+
+/*
+ * This is a special fast verify which basically checks the attributes
+ * and then, if all is OK, checks the size and mod time using the same
+ * stat and statvfs structures.
+ */
+int
+fverify(int fix, char *ftype, char *path, struct ainfo *ainfo,
+    struct cinfo *cinfo)
+{
+	int retval;
+
+	/* return success if attribute checks are disabled */
+
+	if (get_disable_attribute_check()) {
+		return (0);
+	}
+
+	if ((retval = averify(fix, ftype, path, ainfo)) == 0) {
+		if (*ftype == 'f' || *ftype == 'i') {
+			if (cinfo->size != status.st_size) {
+				reperr(pkg_gt(WRN_QV_SIZE), path);
+				retval = VE_CONT;
+			}
+			/* pcfs doesn't support modification times */
+			if (strcmp(vfsstatus.f_basetype, "pcfs") != 0) {
+				if (cinfo->modtime != status.st_mtime) {
+					reperr(pkg_gt(WRN_QV_MTIME), path);
+					retval = VE_CONT;
+				}
+			}
+		}
+	}
+
+	return (retval);
+}
+
+/*
+ * This function determines whether or not non-ABI symlinks are supported.
+ */
+
+int
+nonABI_symlinks(void)
+{
+	return (nonabi_symlinks);
+}
+
+void
+set_nonABI_symlinks(void)
+{
+	nonabi_symlinks	= 1;
+}
+
+/*
+ * Disable attribute checking. Only disable attribute checking if files
+ * are guaranteed to exist in the FS.
+ */
+void
+disable_attribute_check(void)
+{
+	disable_attributes = 1;
+}
+
+/*
+ * This function determines whether or not to do attribute checking.
+ * Returns:  0 - Do attribute checking
+ *          !0 - Don't do attribute checking
+ */
+int
+get_disable_attribute_check(void)
+{
+	return (disable_attributes);
+}
+
+/*
+ * This function returns the address of the "global" error buffer that
+ * is populated by the various functions in this module.
+ */
+
+char *
+getErrbufAddr(void)
+{
+	return (theErrBuf);
+}
+
+/*
+ * This function returns the size of the buffer returned by getErrbufAddr()
+ */
+
+int
+getErrbufSize(void)
+{
+	return (sizeof (theErrBuf));
+}
+
+/*
+ * This function returns the current global "error string"
+ */
+
+char *
+getErrstr(void)
+{
+	return (theErrStr);
+}
+
+/*
+ * This function sets the global "error string"
+ */
+
+void
+setErrstr(char *a_errstr)
+{
+	theErrStr = a_errstr;
+}
+
+/*
+ * This function enables checksumming
+ */
+
+void
+checksum_on(void)
+{
+	enable_checksum = 1;
+}
+
+/*
+ * This function disables checksumming
+ */
+
+void
+checksum_off(void)
+{
+	enable_checksum = 0;
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/usr/src/lib/libpkg/common/vfpops.c	Fri Jun 05 10:28:40 2009 -0400
@@ -0,0 +1,1283 @@
+/*
+ * 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
+ * or http://www.opensolaris.org/os/licensing.
+ * 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 2009 Sun Microsystems, Inc.  All rights reserved.
+ * Use is subject to license terms.
+ */
+
+
+
+/*
+ * Module:	vfpops.c
+ * Synopsis:	Implements virtual file protocol operations
+ * Description:
+ *
+ * This module implements the "Virtual File protocol" operations. These
+ * operations are intended to provide very fast access to file data,
+ * allowing a file to be accessed in very efficient ways with extremely
+ * low-cpu intensive operations. If possible file data is mapped directly
+ * into memory allowing the data to be accessed directly. If the data
+ * cannot be mapped directly into memory, memory will be allocated and
+ * the file data read directly into memory. If that fails currently the
+ * file data is not accessible. Other methods of making the file could
+ * be implemented in the future (e.g. stdio if all else fails).
+ *
+ * In general any code that uses stdio to access a file can be changed
+ * to use the various "vfp" operations to access a file, with a resulting
+ * increase in performance and decrease in cpu time required to access
+ * the file contents.
+ *
+ * Public Methods:
+ *
+ *   vfpCheckpointFile - Create new VFP that checkpoints existing VFP
+ *   vfpCheckpointOpen - open file, allocate storage, return pointer to VFP_T
+ *   vfpClose - close file associated with vfp
+ *   vfpDecCurrPtr - decrement current character pointer
+ *   vfpGetBytesRemaining - get number of bytes remaining to read
+ *   vfpGetCurrCharPtr - get pointer to current character
+ *   vfpGetCurrPtrDelta - get number of bytes between current and specified char
+ *   vfpGetFirstCharPtr - get pointer to first character
+ *   vfpGetLastCharPtr - get pointer to last character
+ *   vfpGetModifiedLen - get highest modified byte (length) contained in vfp
+ *   vfpGetPath - get the path associated with the vfp
+ *   vfpGetc - get current character and increment to next
+ *   vfpGetcNoInc - get current character - do not increment
+ *   vfpGets - get a string from the vfp into a fixed size buffer
+ *   vfpIncCurrPtr - increment current character pointer
+ *   vfpIncCurrPtrBy - increment current pointer by specified delta
+ *   vfpOpen - open file on vfp
+ *   vfpPutBytes - put fixed number of bytes to current character and increment
+ *   vfpPutFormat - put format one arg to current character and increment
+ *   vfpPutInteger - put integer to current character and increment
+ *   vfpPutLong - put long to current character and increment
+ *   vfpPutc - put current character and increment to next
+ *   vfpPuts - put string to current character and increment
+ *   vfpRewind - rewind file to first byte
+ *   vfpSeekToEnd - seek to end of file
+ *   vfpSetCurrCharPtr - set pointer to current character
+ *   vfpSetFlags - set flags that affect file access
+ *   vfpSetSize - set size of file (for writing)
+ *   vfpTruncate - truncate file
+ *   vfpWriteToFile - write data contained in vfp to specified file
+ */
+
+#include <stdio.h>
+#include <limits.h>
+#include <stdlib.h>
+#include <string.h>
+#include <strings.h>
+#include <unistd.h>
+#include <ctype.h>
+#include <fcntl.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <sys/mman.h>
+#include <errno.h>
+#include <libintl.h>
+#include "pkglib.h"
+#include "pkgstrct.h"
+#include "pkglocale.h"
+
+/*
+ * These are internal flags that occupy the high order byte of the VFPFLAGS_T
+ * flags element of the vfp. These flags may only occupy the high order order
+ * 16 bits of the 32-bit unsigned vfp "flags" object.
+ */
+
+#define	_VFP_MMAP	0x00010000	/* mmap used */
+#define	_VFP_MALLOC	0x00020000	/* malloc used */
+#define	_VFP_WRITE	0x00040000	/* file opened for write */
+#define	_VFP_READ	0x00080000	/* file opened for reading */
+#define	_VFP_MODIFIED	0x00100000	/* contents are marked modified */
+
+/* path name given to "anonymous" (string) vfp */
+
+#define	VFP_ANONYMOUS_PATH	"<<string>>"
+
+/* minimum size file to mmap (64mb) */
+
+#define	MIN_MMAP_SIZE	(64*1024)
+
+/*
+ * *****************************************************************************
+ * global external (public) functions
+ * *****************************************************************************
+ */
+
+/*
+ * Name:	vfpOpen
+ * Description:	Open file on vfp, allocate storage, return pointer to VFP_T
+ *		that can be used to access/modify file contents.
+ * Arguments:	VFP_T **r_vfp - pointer to pointer to VFP_T
+ *		char *a_path - path of file to open and associate with this VFP.
+ *			- if the path is (char *)NULL then no file is associated
+ *			  with this VFP - this is a way to create a fixed length
+ *			  string that can be manipulated with the VFP operators.
+ *			  Before the VFP can be used "vfpSetSize" must be called
+ *			  to set the size of the string buffer.
+ *		char *a_mode - fopen mode to open the file with
+ *		VFPFLAGS_T a_flags - one or more flags to control the operation:
+ *			- VFP_NONE - no special flags
+ *			- VFP_NEEDNOW - file data needed in memory now
+ *			- VFP_SEQUENTIAL - memory will be sequentially accessed
+ *			- VFP_RANDOM - memory will be randomly accessed
+ *			- VFP_NOMMAP - do not use mmap to access file
+ *			- VFP_NOMALLOC - do not use malloc to buffer file
+ * Returns:	int	== 0 - operation was successful
+ *			!= 0 - operation failed, errno contains reason
+ * Side Effects: r_vfp -- filled in with a pointer to a newly allocated vfp
+ *			which can be used with the various vfp functions.
+ *		errno -- contains system error number if return is != 0
+ */
+
+int
+vfpOpen(VFP_T **r_vfp, char *a_path, char *a_mode, VFPFLAGS_T a_flags)
+{
+	FILE		*fp = (FILE *)NULL;
+	VFP_T		*vfp;
+	int		lerrno;
+	struct stat	statbuf;
+	int		pagesize = getpagesize();
+
+	/* reset return VFP/FILE pointers */
+
+	(*r_vfp) = (VFP_T *)NULL;
+
+	/* allocate pre-zeroed vfp object */
+
+	vfp = (VFP_T *)calloc(sizeof (VFP_T), 1);
+	if (vfp == (VFP_T *)NULL) {
+		return (-1);
+	}
+
+	/* create "string" vfp if no path specified */
+
+	if (a_path == (char *)NULL) {
+		/*
+		 * no path specified - no open file associated with vfp
+		 * The vfp is initialized to all zeros - initialize just those
+		 * values that need to be non-zero.
+		 */
+
+		vfp->_vfpFlags = _VFP_MALLOC;
+		vfp->_vfpPath = strdup(VFP_ANONYMOUS_PATH);
+		(*r_vfp) = vfp;
+		return (0);
+	}
+
+	/*
+	 * path specified - associate open file with vfp;
+	 * return an error if no path or mode specified
+	 */
+
+	if (a_mode == (char *)NULL) {
+		errno = EFAULT;		/* Bad address */
+		(void) free(vfp);
+		return (-1);
+	}
+
+	/* return an error if an empty path or mode specified */
+
+	if ((*a_path == '\0') || (*a_mode == '\0')) {
+		errno = EINVAL;		/* Invalid argument */
+		(void) free(vfp);
+		return (-1);
+	}
+
+	/* open the file */
+
+	fp = fopen(a_path, a_mode);
+	if (fp == (FILE *)NULL) {
+		lerrno = errno;
+		(void) free(vfp);
+		errno = lerrno;
+		return (-1);
+	}
+
+	/* Get the file size */
+
+	if (fstat(fileno(fp), &statbuf) != 0) {
+		lerrno = errno;
+		(void) fclose(fp);
+		(void) free(vfp);
+		errno = lerrno;
+		return (-1);
+	}
+
+	/*
+	 * Obtain access to existing file contents:
+	 *  -> plan a: map contents file into memory
+	 *  -> plan b: on failure just read into large buffer
+	 */
+
+	/* attempt to mmap file if mmap is allowed */
+
+	vfp->_vfpStart = MAP_FAILED;	/* assume map failed if not allowed */
+
+	/*
+	 * if file is a regular file, and if mmap allowed,
+	 * and (malloc not forbidden or size is > minumum size to mmap)
+	 */
+
+	if ((S_ISREG(statbuf.st_mode)) && (!(a_flags & VFP_NOMMAP)) &&
+		((a_flags & VFP_NOMALLOC) || statbuf.st_size > MIN_MMAP_SIZE)) {
+		char *p;
+		/* set size to current size of file */
+
+		vfp->_vfpMapSize = statbuf.st_size;
+
+		/*
+		 * compute proper size for mapping for the file contents;
+		 * add in one extra page so falling off end when file size is
+		 * exactly modulo page size does not cause a page fault to
+		 * guarantee that the end of the file contents will always
+		 * contain a '\0' null character.
+		 */
+
+		vfp->_vfpSize = (statbuf.st_size + pagesize +
+				(pagesize-(statbuf.st_size % pagesize)));
+
+		/*
+		 * mmap allowed: mmap file into memory
+		 * first allocate space on top of which the mapping can be done;
+		 * this way we can guarantee that if the mapping happens to be
+		 * an exact multiple of a page size, that there will be at least
+		 * one byte past the end of the mapping that can be accessed and
+		 * that is guaranteed to be zero.
+		 */
+
+		/* allocate backing space */
+
+		p = (char *)memalign(pagesize, vfp->_vfpSize);
+		if (p == (char *)NULL) {
+			vfp->_vfpStart = MAP_FAILED;
+		} else {
+			/* guarantee first byte after end of data is zero */
+
+			p[vfp->_vfpMapSize] = '\0';
+
+			/* map file on top of the backing space */
+
+			vfp->_vfpStart = mmap(p, vfp->_vfpMapSize, PROT_READ,
+				MAP_PRIVATE|MAP_FIXED, fileno(fp), (off_t)0);
+
+			/* if mmap succeeded set mmap used flag in vfp */
+
+			if (vfp->_vfpStart != MAP_FAILED) {
+				vfp->_vfpFlags |= _VFP_MMAP;
+			}
+		}
+	}
+
+	/* if map failed (or not allowed) attempt malloc (if allowed) */
+
+	if ((vfp->_vfpStart == MAP_FAILED) && (!(a_flags & VFP_NOMALLOC))) {
+		/* mmap failed - plan b: read directly into memory */
+		ssize_t	rlen;
+
+		/*
+		 * compute proper size for allocating storage for file contents;
+		 * add in one extra page so falling off end when file size is
+		 * exactly modulo page size does not cause a page fault to
+		 * guarantee that the end of the file contents will always
+		 * contain a '\0' null character.
+		 */
+
+		vfp->_vfpSize = statbuf.st_size+pagesize;
+
+		/* allocate buffer to hold file data */
+
+		vfp->_vfpStart = memalign((size_t)pagesize, vfp->_vfpSize);
+		if (vfp->_vfpStart == (char *)NULL) {
+			lerrno = errno;
+			(void) fclose(fp);
+			(void) free(vfp);
+			errno = lerrno;
+			return (-1);
+		}
+
+		/* read the file into the buffer */
+
+		if (statbuf.st_size != 0) {
+			rlen = read(fileno(fp), vfp->_vfpStart,
+							statbuf.st_size);
+			if (rlen != statbuf.st_size) {
+				lerrno = errno;
+				if (lerrno == 0) {
+					lerrno = EIO;
+				}
+				(void) free(vfp->_vfpStart);
+				(void) fclose(fp);
+				(void) free(vfp);
+				errno = lerrno;
+				return (-1);
+			}
+
+			/* assure last byte+1 is null character */
+
+			((char *)vfp->_vfpStart)[statbuf.st_size] = '\0';
+		}
+
+		/* set malloc used flag in vfp */
+
+		vfp->_vfpFlags |= _VFP_MALLOC;
+	}
+
+	/* if no starting address all read methods failed */
+
+	if (vfp->_vfpStart == MAP_FAILED) {
+		/* no mmap() - no read() - cannot allocate memory */
+		(void) fclose(fp);
+		(void) free(vfp);
+		errno = ENOMEM;
+		return (-1);
+	}
+
+	/*
+	 * initialize vfp contents
+	 */
+
+	/* _vfpCurr -> next byte to read */
+	vfp->_vfpCurr = (char *)vfp->_vfpStart;
+
+	/* _vfpEnd -> last data byte */
+	vfp->_vfpEnd = (((char *)vfp->_vfpStart) + statbuf.st_size)-1;
+
+	/* _vfpHighWater -> last byte written */
+	vfp->_vfpHighWater = (char *)vfp->_vfpEnd;
+
+	/* _vfpFile -> associated FILE* object */
+	vfp->_vfpFile = fp;
+
+	/* set flags as appropriate */
+
+	(void) vfpSetFlags(vfp, a_flags);
+
+	/* retain path name */
+
+	vfp->_vfpPath = strdup(a_path ? a_path : "");
+
+	/* set read/write flags */
+
+	if (*a_mode == 'w') {
+		vfp->_vfpFlags |= _VFP_WRITE;
+	}
+
+	if (*a_mode == 'r') {
+		vfp->_vfpFlags |= _VFP_READ;
+	}
+
+	/* set return vfp pointer */
+
+	(*r_vfp) = vfp;
+
+	/* All OK */
+
+	return (0);
+}
+
+/*
+ * Name:	vfpClose
+ * Description:	Close an open vfp, causing any modified data to be written out
+ *		to the file associated with the vfp.
+ * Arguments:	VFP_T **r_vfp - pointer to pointer to VFP_T returned by vfpOpen
+ * Returns:	int	== 0 - operation was successful
+ *			!= 0 - operation failed, errno contains reason
+ * Side Effects: r_vfp is set to (VFP_T)NULL
+ */
+
+int
+vfpClose(VFP_T **r_vfp)
+{
+	int	ret;
+	int	lerrno;
+	VFP_T	*vfp;
+
+	/* return error if NULL VFP_T** provided */
+
+	if (r_vfp == (VFP_T **)NULL) {
+		errno = EFAULT;
+		return (-1);
+	}
+
+	/* localize access to VFP_T */
+
+	vfp = *r_vfp;
+
+	/* return successful if NULL VFP_T* provided */
+
+	if (vfp == (VFP_T *)NULL) {
+		return (0);
+	}
+
+	/* reset return VFP_T* handle */
+
+	*r_vfp = (VFP_T *)NULL;
+
+	/*
+	 * if closing a file that is open for writing, commit all data if the
+	 * backing memory is volatile and if there is a file open to write
+	 * the data to.
+	 */
+
+	if (vfp->_vfpFlags & _VFP_WRITE) {
+		if ((vfp->_vfpFlags & _VFP_MALLOC) &&
+				(vfp->_vfpFile != (FILE *)NULL)) {
+			size_t	len;
+
+			/* determine number of bytes to write */
+			len = vfpGetModifiedLen(vfp);
+
+			/* if modified bytes present commit data to the file */
+			if (len > 0) {
+				(void) vfpSafePwrite(fileno(vfp->_vfpFile),
+						vfp->_vfpStart, len, (off_t)0);
+			}
+		}
+	}
+
+	/* deallocate any allocated storage/mappings/etc */
+
+	if (vfp->_vfpFlags & _VFP_MALLOC) {
+		(void) free(vfp->_vfpStart);
+	} else if (vfp->_vfpFlags & _VFP_MMAP) {
+		/* unmap the file mapping */
+
+		(void) munmap(vfp->_vfpStart, vfp->_vfpMapSize);
+
+		/* free the backing allocation */
+
+		(void) free(vfp->_vfpStart);
+	}
+
+	/* free up path */
+
+	(void) free(vfp->_vfpPath);
+
+	/* close the file */
+
+	ret = 0;
+	if (vfp->_vfpFile != (FILE *)NULL) {
+		ret = fclose(vfp->_vfpFile);
+		lerrno = errno;
+	}
+
+	/* deallocate the vfp itself */
+
+	(void) free(vfp);
+
+	/* if the fclose() failed, return error and errno */
+
+	if (ret != 0) {
+		errno = lerrno;
+		return (-1);
+	}
+
+	return (0);
+}
+
+/*
+ * Name:	vfpSetFlags
+ * Description:	Modify operation of VFP according to flags specified
+ * Arguments:	VFP_T *a_vfp - VFP_T pointer associated with file to set flags
+ *		VFPFLAGS_T a_flags - one or more flags to control the operation:
+ *			- VFP_NEEDNOW - file data needed in memory now
+ *			- VFP_SEQUENTIAL - file data sequentially accessed
+ *			- VFP_RANDOM - file data randomly accessed
+ *			Any other flags specified are silently ignored.
+ * Returns:	int	== 0 - operation was successful
+ *			!= 0 - operation failed, errno contains reason
+ */
+
+int
+vfpSetFlags(VFP_T *a_vfp, VFPFLAGS_T a_flags)
+{
+	/* return if no vfp specified */
+
+	if (a_vfp == (VFP_T *)NULL) {
+		return (0);
+	}
+
+	/* if file data mapped into memory, apply vm flags */
+
+	if ((a_vfp->_vfpSize != 0) && (a_vfp->_vfpFlags & _VFP_MMAP)) {
+		/* mmap succeeded: properly advise vm system */
+
+		if (a_flags & VFP_NEEDNOW) {
+			/* advise vm system data is needed now */
+			(void) madvise(a_vfp->_vfpStart, a_vfp->_vfpMapSize,
+							MADV_WILLNEED);
+		}
+		if (a_flags & VFP_SEQUENTIAL) {
+			/* advise vm system data access is sequential */
+			(void) madvise(a_vfp->_vfpStart, a_vfp->_vfpSize,
+							MADV_SEQUENTIAL);
+		}
+		if (a_flags & VFP_RANDOM) {
+			/* advise vm system data access is random */
+			(void) madvise(a_vfp->_vfpStart, a_vfp->_vfpSize,
+							MADV_RANDOM);
+		}
+	}
+
+	return (0);
+}
+
+/*
+ * Name:	vfpRewind
+ * Description:	Reset default pointer for next read/write to start of file data
+ * Arguments:	VFP_T *a_vfp - VFP_T pointer associated with file to rewind
+ * Returns:	void
+ *			Operation is always successful
+ */
+
+void
+vfpRewind(VFP_T *a_vfp)
+{
+	/* return if no vfp specified */
+
+	if (a_vfp == (VFP_T *)NULL) {
+		return;
+	}
+
+	/* set high water mark of last modified data */
+
+	if (a_vfp->_vfpCurr > a_vfp->_vfpHighWater) {
+		a_vfp->_vfpHighWater = a_vfp->_vfpCurr;
+	}
+
+	/* reset next character pointer to start of file data */
+
+	a_vfp->_vfpCurr = a_vfp->_vfpStart;
+}
+
+/*
+ * Name:	vfpSetSize
+ * Description:	Set size of in-memory image associated with VFP
+ * Arguments:	VFP_T *a_vfp - VFP_T pointer associated with file to set
+ *		size_t a_size - number of bytes to associatge with VFP
+ * Returns:	int	== 0 - operation was successful
+ *			!= 0 - operation failed, errno contains reason
+ * Side Effects:
+ *		Currently only a file that is in malloc()ed memory can
+ *		have its in-memory size changed.
+ *		An error is returned If the file is mapped into memory.
+ *		A file cannot be decreased in size - if the specified
+ *		size is less than the current size, the operation is
+ *		successful but no change in file size occurs.
+ *		If no file is associated with the VFP (no "name" was
+ *		given to vfpOpen) the first call to vfpSetSize allocates
+ *		the initial size of the file data - effectively calling
+ *		"malloc" to allocate the initial memory for the file data.
+ *		Once an initial allocation has been made, subsequent calls
+ *		to vfpSetSize are effectively a "realloc" of the existing
+ *		file data.
+ *		All existing file data is preserved.
+ */
+
+int
+vfpSetSize(VFP_T *a_vfp, size_t a_size)
+{
+	char	*np;
+	size_t	curSize;
+
+	/* return if no vfp specified */
+
+	if (a_vfp == (VFP_T *)NULL) {
+		return (0);
+	}
+
+	/* if malloc not used don't know how to set size right now */
+
+	if (!(a_vfp->_vfpFlags & _VFP_MALLOC)) {
+		return (-1);
+	}
+
+	/* adjust size to reflect extra page of data maintained */
+
+	a_size += getpagesize();
+
+	/* if size is not larger than current nothing to do */
+
+	if (a_size <= a_vfp->_vfpSize) {
+		return (0);
+	}
+
+	/* remember new size */
+
+	curSize = a_vfp->_vfpSize;
+	a_vfp->_vfpSize = a_size;
+
+	/* allocate/reallocate memory as appropriate */
+
+	if (a_vfp->_vfpStart != (char *)NULL) {
+		np = (char *)realloc(a_vfp->_vfpStart, a_vfp->_vfpSize+1);
+		if (np == (char *)NULL) {
+			return (-1);
+		}
+		np[curSize-1] = '\0';
+	} else {
+		np = (char *)malloc(a_vfp->_vfpSize+1);
+		if (np == (char *)NULL) {
+			return (-1);
+		}
+		np[0] = '\0';
+	}
+
+	/* make sure last allocated byte is a null */
+
+	np[a_vfp->_vfpSize] = '\0';
+
+	/*
+	 * adjust all pointers to account for buffer address change
+	 */
+
+	/* _vfpCurr -> next byte to read */
+	a_vfp->_vfpCurr = (char *)(((ptrdiff_t)a_vfp->_vfpCurr -
+					(ptrdiff_t)a_vfp->_vfpStart) + np);
+
+	/* _vfpHighWater -> last byte written */
+	a_vfp->_vfpHighWater = (char *)(((ptrdiff_t)a_vfp->_vfpHighWater -
+					(ptrdiff_t)a_vfp->_vfpStart) + np);
+
+	/* _vfpEnd -> last data byte */
+	a_vfp->_vfpEnd = (np + a_vfp->_vfpSize)-1;
+
+	/* _vfpStart -> first data byte */
+	a_vfp->_vfpStart = np;
+
+	return (0);
+}
+
+/*
+ * Name:	vfpTruncate
+ * Description:	Truncate data associated with VFP
+ * Arguments:	VFP_T *a_vfp - VFP_T pointer associated with file to truncate
+ * Returns:	void
+ *			Operation is always successful.
+ * Side Effects:
+ *		In memory data associated with file is believed to be empty.
+ *		Actual memory associated with file is not affected.
+ *		If a file is associated with the VFP, it is truncated.
+ */
+
+void
+vfpTruncate(VFP_T *a_vfp)
+{
+	/* return if no vfp specified */
+
+	if (a_vfp == (VFP_T *)NULL) {
+		return;
+	}
+
+	/*
+	 * reset all pointers so that no data is associated with file
+	 */
+
+	/* current byte is start of data area */
+
+	a_vfp->_vfpCurr = a_vfp->_vfpStart;
+
+	/* last byte written is start of data area */
+
+	a_vfp->_vfpHighWater = a_vfp->_vfpStart;
+
+	/* current character is NULL */
+
+	*a_vfp->_vfpCurr = '\0';
+
+	/* if file associated with VFP, truncate actual file */
+
+	if (a_vfp->_vfpFile != (FILE *)NULL) {
+		(void) ftruncate(fileno(a_vfp->_vfpFile), 0);
+	}
+}
+
+/*
+ * Name:	vfpWriteToFile
+ * Description:	Write data associated with VFP to specified file
+ * Arguments:	VFP_T *a_vfp - VFP_T pointer associated with file to write
+ *		char *a_path - path of file to write file data to
+ * Returns:	int	== 0 - operation was successful
+ *			!= 0 - operation failed, errno contains reason
+ */
+
+int
+vfpWriteToFile(VFP_T *a_vfp, char *a_path)
+{
+	int	fd;
+	int	lerrno = 0;
+	size_t	len;
+	ssize_t	result = 0;
+
+	/* return if no vfp specified */
+
+	if (a_vfp == (VFP_T *)NULL) {
+		errno = EFAULT;
+		return (-1);
+	}
+
+	/* on buffer overflow generate error */
+
+	if ((a_vfp->_vfpOverflow != 0) || (vfpGetBytesAvailable(a_vfp) < 1)) {
+		errno = EFBIG;
+		return (-1);
+	}
+
+	/* open file to write data to */
+
+	fd = open(a_path, O_WRONLY|O_CREAT|O_TRUNC, 0644);
+	if (fd < 0) {
+		return (-1);
+	}
+
+	/* determine number of bytes to write */
+
+	len = vfpGetModifiedLen(a_vfp);
+
+	/*
+	 * if there is data associated with the file, write it out;
+	 * if an error occurs, close the file and return failure.
+	 */
+
+	if (len > 0) {
+		result = vfpSafeWrite(fd, a_vfp->_vfpStart, len);
+		if (result != len) {
+			/* error comitting data - return failure */
+			lerrno = errno;
+			(void) close(fd);
+			errno = lerrno;
+			return (-1);
+		}
+	}
+
+	/* close the file */
+
+	(void) close(fd);
+
+	/* data committed to backing store - clear the modified flag */
+
+	(void) vfpClearModified(a_vfp);
+
+	/* return success */
+
+	return (0);
+}
+
+/*
+ * Name:	vfpCheckpointFile
+ * Description:	Create new VFP that checkpoints existing VFP, can be used by
+ *		subsequent call to vfpCheckpointOpen to open a file using the
+ *		existing in-memory cache of the contents of the file
+ * Arguments:	VFP_T **r_cpVfp - pointer to pointer to VFP_T to be filled in
+ *			with "checkpointed file" VFP (backing store)
+ *		VFP_T **a_vfp - pointer to pointer to VFP_T returned by vfpOpen
+ *			representing the VFP to checkpoint
+ *		char *a_path - path to file that is the backing store for the
+ *			in-memory data represented by a_vfp - used to verify
+ *			that the data in memory is not out of date with respect
+ *			to the backing store when vfpCheckpointOpen is called
+ *			== (char *)NULL - use path associated with a_vfp
+ *				that is, the backing store file in use
+ * Returns:	int	== 0 - operation was successful
+ *				- r_destVfp contains a pointer to a new VFP that
+ *					may be used in a subsequent call to
+ *					vfpCheckpointOpen
+ *				- the VFP referenced by *a_vfp is free()ed and
+ *					must no longer be referenced
+ *			!= 0 - operation failed, errno contains reason
+ *				- the VFP referenced by *a_vfp is not affected;
+ *					the caller may continue to use it
+ * Notes:	If the data of a VFP to checkpoint is mmap()ed then this method
+ *			returns failure - only malloc()ed data VFPs can be
+ *			checkpointed.
+ */
+
+int
+vfpCheckpointFile(VFP_T **r_cpVfp, VFP_T **a_vfp, char *a_path)
+{
+	VFP_T		*vfp;		/* newly allocated checkpointed VFP */
+	VFP_T		*avfp;		/* local -> to a_vfp */
+	struct stat	statbuf;	/* stat(2) info for backing store */
+
+	/* return error if NULL VFP_T** to checkpoint provided */
+
+	if (r_cpVfp == (VFP_T **)NULL) {
+		errno = EFAULT;
+		return (-1);
+	}
+
+	/* reset return checkpoint VFP pointer */
+
+	(*r_cpVfp) = (VFP_T *)NULL;
+
+	/* return error if no VFP to checkpoint specified */
+
+	if (a_vfp == (VFP_T **)NULL) {
+		errno = EFAULT;
+		return (-1);
+	}
+
+	/* localize reference to a_vfp */
+
+	avfp = *a_vfp;
+
+	/* return error if no VFP to checkpoint specified */
+
+	if (avfp == (VFP_T *)NULL) {
+		errno = EFAULT;
+		return (-1);
+	}
+
+	/* on buffer overflow generate error */
+
+	if ((avfp->_vfpOverflow != 0) || (vfpGetBytesAvailable(avfp) < 1)) {
+		errno = EFBIG;
+		return (-1);
+	}
+
+	/* no checkpointing is possible if the existing VFP is mmap()ed */
+
+	if (avfp->_vfpFlags & _VFP_MMAP) {
+		errno = EIO;
+		return (-1);
+	}
+
+	/* if no path specified, grab it from the VFP to checkpoint */
+
+	if ((a_path == (char *)NULL) || (*a_path == '\0')) {
+		a_path = avfp->_vfpPath;
+	}
+
+	/* backing store required: if VFP is "string" then this is an error */
+
+	if ((a_path == (char *)NULL) ||
+				strcmp(a_path, VFP_ANONYMOUS_PATH) == 0) {
+		errno = EINVAL;
+		return (-1);
+	}
+
+	/* Get the VFP to checkpoint (backing store) file size */
+
+	if (stat(a_path, &statbuf) != 0) {
+		return (-1);
+	}
+
+	/* allocate storage for checkpointed VFP (to return) */
+
+	vfp = (VFP_T *)malloc(sizeof (VFP_T));
+	if (vfp == (VFP_T *)NULL) {
+		return (-1);
+	}
+
+	/*
+	 * close any file that is on the VFP to checkpoint (backing store);
+	 * subsequent processes can modify the backing store data, and
+	 * then when vfpCheckpointOpen is called, either the in-memory
+	 * cached data will be used (if backing store unmodified) or else
+	 * the in-memory data is released and the backing store is used.
+	 */
+
+	if (avfp->_vfpFile != (FILE *)NULL) {
+		(void) fclose(avfp->_vfpFile);
+		avfp->_vfpFile = (FILE *)NULL;
+	}
+
+	/* free any path associated with VFP to checkpoint (backing store) */
+
+	if (avfp->_vfpPath != (char *)NULL) {
+		(void) free(avfp->_vfpPath);
+		avfp->_vfpPath = (char *)NULL;
+	}
+
+	/* copy contents of VFP to checkpoint to checkpointed VFP */
+
+	memcpy(vfp, avfp, sizeof (VFP_T));
+
+	/* free contents of VFP to checkpoint */
+
+	(void) free(avfp);
+
+	/* reset pointer to VFP that has been free'd */
+
+	*a_vfp = (VFP_T *)NULL;
+
+	/* remember path associated with the checkpointed VFP (backing store) */
+
+	vfp->_vfpPath = strdup(a_path);
+
+	/* save tokens that identify the backing store for the in-memory data */
+
+	vfp->_vfpCkDev = statbuf.st_dev;	/* devid holding st_ino inode */
+	vfp->_vfpCkIno = statbuf.st_ino;	/* backing store inode */
+	vfp->_vfpCkMtime = statbuf.st_mtime;	/* last data modification */
+	vfp->_vfpCkSize = statbuf.st_size;	/* backing store size (bytes) */
+	vfp->_vfpCkStBlocks = statbuf.st_blocks; /* blocks allocated to file */
+
+	/* pass checkpointed VFP to caller */
+
+	(*r_cpVfp) = vfp;
+
+	/* success! */
+
+	return (0);
+}
+
+/*
+ * Name:	vfpCheckpointOpen
+ * Description:	Open file on vfp, allocate storage, return pointer to VFP_T
+ *		that can be used to access/modify file contents. If a VFP_T to
+ *		a checkpointed VFP is passed in, and the in memory contents of
+ *		the VFP are not out of date with respect to the backing store
+ *		file, use the existing in-memory contents - otherwise, discard
+ *		the in-memory contents and reopen and reread the file.
+ * Arguments:	VFP_T **a_cpVfp - pointer to pointer to VFP_T that represents
+ *			checkpointed VFP to use to open the file IF the contents
+ *			of the backing store are identical to the in-memory data
+ *		VFP_T **r_vfp - pointer to pointer to VFP_T to open file on
+ *		char *a_path - path of file to open and associate with this VFP.
+ *			- if the path is (char *)NULL then no file is associated
+ *			  with this VFP - this is a way to create a fixed length
+ *			  string that can be manipulated with the VFP operators.
+ *			  Before the VFP can be used "vfpSetSize" must be called
+ *			  to set the size of the string buffer.
+ *		char *a_mode - fopen mode to open the file with
+ *		VFPFLAGS_T a_flags - one or more flags to control the operation:
+ *			- VFP_NONE - no special flags
+ *			- VFP_NEEDNOW - file data needed in memory now
+ *			- VFP_SEQUENTIAL - memory will be sequentially accessed
+ *			- VFP_RANDOM - memory will be randomly accessed
+ *			- VFP_NOMMAP - do not use mmap to access file
+ *			- VFP_NOMALLOC - do not use malloc to buffer file
+ * Returns:	int	== 0 - operation was successful
+ *			!= 0 - operation failed, errno contains reason
+ * Side Effects: r_vfp -- filled in with a pointer to a newly allocated vfp
+ *			which can be used with the various VFP functions.
+ *		a_cpVfp -- contents reset to zero if used to open the file
+ *		errno -- contains system error number if return is != 0
+ */
+
+int
+vfpCheckpointOpen(VFP_T **a_cpVfp, VFP_T **r_vfp, char *a_path,
+	char *a_mode, VFPFLAGS_T a_flags)
+{
+	FILE		*fp;	/* backing store */
+	VFP_T		*cpVfp;	/* local -> to a_cpVfp checkpointed VFP */
+	VFP_T		*vfp;	/* new VFP open on checkpointed backing store */
+	struct stat	statbuf; /* stat(2) info on backing store */
+
+	/*
+	 * if no source VFP, or source VFP empty,
+	 * or no backing store, just open file
+	 */
+
+	if ((a_cpVfp == (VFP_T **)NULL) || (*a_cpVfp == (VFP_T *)NULL) ||
+		((*a_cpVfp)->_vfpStart == (char *)NULL)) {
+		(void) vfpClose(a_cpVfp);
+		return (vfpOpen(r_vfp, a_path, a_mode, a_flags));
+	}
+
+	/* localize access to checkpointed VFP_T (*a_cpVfp) */
+
+	cpVfp = *a_cpVfp;
+
+	/* if no path specified, grab it from the checkpointed VFP */
+
+	if ((a_path == (char *)NULL) || (*a_path == '\0')) {
+		a_path = cpVfp->_vfpPath;
+	}
+
+	/* return error if no path specified and no path in checkpointed VFP */
+
+	if ((a_path == (char *)NULL) && (*a_path == '\0')) {
+		errno = EINVAL;
+		return (-1);
+	}
+
+	/* if no backing store path, then just open file */
+
+	if (stat(a_path, &statbuf) != 0) {
+		(void) vfpClose(a_cpVfp);
+		return (vfpOpen(r_vfp, a_path, a_mode, a_flags));
+	}
+
+	/*
+	 * if backing store tokens do not match checkpointed VFP,
+	 * the backing store has been updated since the VFP was checkpointed;
+	 * release the in-memory data, and open and read the backing store
+	 */
+
+	if ((statbuf.st_size != cpVfp->_vfpCkSize) ||
+		(statbuf.st_mtime != cpVfp->_vfpCkMtime) ||
+		(statbuf.st_blocks != cpVfp->_vfpCkStBlocks) ||
+		(statbuf.st_ino != cpVfp->_vfpCkIno) ||
+		(statbuf.st_dev != cpVfp->_vfpCkDev)) {
+		(void) vfpClose(a_cpVfp);
+		return (vfpOpen(r_vfp, a_path, a_mode, a_flags));
+	}
+
+	/*
+	 * backing store has not been updated since the VFP was checkpointed;
+	 * use the in-memory data without re-reading the backing store; open the
+	 * backing store file (if no file already open on the checkpointed VFP)
+	 * so there is an open file associated with the in-memory data
+	 */
+
+	fp = cpVfp->_vfpFile;
+	if (fp == (FILE *)NULL) {
+		fp = fopen(a_path, a_mode);
+		if (fp == (FILE *)NULL) {
+			int	lerrno;
+
+			lerrno = errno;
+			(void) vfpClose(a_cpVfp);
+			errno = lerrno;
+			return (-1);
+		}
+	}
+
+	/* allocate new VFP object to return as open VFP */
+
+	vfp = (VFP_T *)malloc(sizeof (VFP_T));
+	if (vfp == (VFP_T *)NULL) {
+		(void) vfpClose(a_cpVfp);
+		return (vfpOpen(r_vfp, a_path, a_mode, a_flags));
+	}
+
+	/* copy cached checkpointed VFP to new VFP to return */
+
+	(void) memcpy(vfp, cpVfp, sizeof (VFP_T));
+
+	/*
+	 * initialize VFP to return contents
+	 */
+
+	/* FILE -> file opened on the VFPs backing store */
+
+	vfp->_vfpFile = fp;
+
+	/* release any existing path associated with the VFP */
+
+	if (vfp->_vfpPath != (char *)NULL) {
+		(void) free(vfp->_vfpPath);
+	}
+
+	/* path associated with the backing store for this VFP */
+
+	vfp->_vfpPath = strdup(a_path);
+
+	/*
+	 * data pointers associated with in memory copy of backing store
+	 * (such as _vfpHighWater, _vfpEnd, _vfpStart, etc.)
+	 * do not need to be modified because we are using the same backing
+	 * store as was checkpointed in cpVfp that is pointed to by vfp.
+	 */
+
+	/* _vfpCurr -> next byte to read */
+	vfp->_vfpCurr = (char *)vfp->_vfpStart;
+
+	/* free checkpointed VFP as it is now open on "vfp" */
+
+	(void) free(cpVfp);
+
+	/* reset callers -> checkpointed VFP */
+
+	(*a_cpVfp) = (VFP_T *)NULL;
+
+	/* set return VFP pointer */
+
+	(*r_vfp) = vfp;
+
+	/* success! */
+
+	return (0);
+}
+
+/*
+ * Name:	vfpClearModified
+ * Description:	Clear the "data is modified" indication from the VFP
+ * Arguments:	VFP_T *a_vfp - VFP_T pointer associated with file to clear
+ *			the "data is modified" indication
+ * Returns:	int	- previous setting of "data is modified" indication
+ *			== 0 - "data is modified" was NOT previously set
+ *			!= 0 - "data is modified" WAS previously set
+ */
+
+int
+vfpClearModified(VFP_T *a_vfp)
+{
+	VFPFLAGS_T	flags;
+
+	/* save current flags settings */
+
+	flags = a_vfp->_vfpFlags;
+
+	/* clear "data is modified" flag */
+
+	a_vfp->_vfpFlags &= (~_VFP_MODIFIED);
+
+	/* return previous "data is modified" flag setting */
+
+	return ((flags & _VFP_MODIFIED) != 0);
+}
+
+/*
+ * Name:	vfpSetModified
+ * Description:	Set the "data is modified" indication from the VFP
+ * Arguments:	VFP_T *a_vfp - VFP_T pointer associated with file to set
+ *			the "data is modified" indication
+ * Returns:	int	- previous setting of "data is modified" indication
+ *			== 0 - "data is modified" was NOT previously set
+ *			!= 0 - "data is modified" WAS previously set
+ */
+
+int
+vfpSetModified(VFP_T *a_vfp)
+{
+	VFPFLAGS_T	flags;
+
+	/* save current flags settings */
+
+	flags = a_vfp->_vfpFlags;
+
+	/* set "data is modified" flag */
+
+	a_vfp->_vfpFlags |= _VFP_MODIFIED;
+
+	/* return previous "data is modified" flag setting */
+
+	return ((flags & _VFP_MODIFIED) != 0);
+}
+
+/*
+ * Name:	vfpGetModified
+ * Description:	Get the "data is modified" indication from the VFP
+ * Arguments:	VFP_T *a_vfp - VFP_T pointer associated with file to get
+ *			the "data is modified" indication
+ * Returns:	int	- current setting of "data is modified" indication
+ *			== 0 - "data is modified" is NOT set
+ *			!= 0 - "data is modified" IS set
+ */
+
+int
+vfpGetModified(VFP_T *a_vfp)
+{
+	/* return current "data is modified" flag setting */
+
+	return ((a_vfp->_vfpFlags & _VFP_MODIFIED) != 0);
+}
+
+/*
+ * Name:	vfpSafeWrite
+ * Description:	write data to open file safely
+ * Arguments:	a_fildes - file descriptor to write data to
+ *		a_buf - pointer to buffer containing data to write
+ *		a_nbyte - number of bytes to write to open file
+ * Returns:	int
+ *		< 0 - error, errno set
+ *		>= 0 - success
+ * NOTE: unlike write(2), vfpSafeWrite() handles partial writes, and will
+ * ----- restart the write() until all bytes are written, or an error occurs.
+ */
+
+ssize_t
+vfpSafeWrite(int a_fildes, void *a_buf, size_t a_nbyte)
+{
+	ssize_t	r;
+	size_t	bytes = a_nbyte;
+
+	for (;;) {
+		/* write bytes to file */
+		r = write(a_fildes, a_buf, a_nbyte);
+
+		/* return error on failure of write() */
+		if (r < 0) {
+			/* EAGAIN: try again */
+			if (errno == EAGAIN) {
+				continue;
+			}
+			/* EINTR: interrupted - try again */
+			if (errno == EINTR) {
+				continue;
+			}
+			return (r);
+		}
+
+		/* return total bytes written on success */
+		if (r >= a_nbyte) {
+			return (bytes);
+		}
+
+		/* partial write, adjust pointers, call write again */
+		a_buf = (void *)((ptrdiff_t)a_buf + (ptrdiff_t)r);
+		a_nbyte -= (size_t)r;
+	}
+}
+
+/*
+ * Name:	vfpSafePwrite
+ * Description:	write data to open file safely
+ * Arguments:	a_fildes - file descriptor to write data to
+ *		a_buf - pointer to buffer containing data to write
+ *		a_nbyte - number of bytes to write to open file
+ *		a_offset - offset into open file to write the first byte to
+ * Returns:	int
+ *		< 0 - error, errno set
+ *		>= 0 - success
+ * NOTE: unlike pwrite(2), vfpSafePwrite() handles partial writes, and will
+ * ----- restart the pwrite() until all bytes are written, or an error occurs.
+ */
+
+ssize_t
+vfpSafePwrite(int a_fildes, void *a_buf, size_t a_nbyte, off_t a_offset)
+{
+	ssize_t	r;
+	size_t	bytes = a_nbyte;
+
+	for (;;) {
+		/* write bytes to file */
+		r = pwrite(a_fildes, a_buf, a_nbyte, a_offset);
+
+		/* return error on failure of write() */
+		if (r < 0) {
+			/* EAGAIN: try again */
+			if (errno == EAGAIN) {
+				continue;
+			}
+			/* EINTR: interrupted - try again */
+			if (errno == EINTR) {
+				continue;
+			}
+			return (r);
+		}
+
+		/* return total bytes written on success */
+		if (r >= a_nbyte) {
+			return (bytes);
+		}
+
+		/* partial write, adjust pointers, call write again */
+		a_buf = (void *)((ptrdiff_t)a_buf + (ptrdiff_t)r);
+		a_nbyte -= (size_t)r;
+		a_offset += (off_t)r;
+	}
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/usr/src/lib/libpkg/i386/Makefile	Fri Jun 05 10:28:40 2009 -0400
@@ -0,0 +1,28 @@
+#
+# 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
+# or http://www.opensolaris.org/os/licensing.
+# 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 2009 Sun Microsystems, Inc.  All rights reserved.
+# Use is subject to license terms.
+#
+
+include ../Makefile.com
+
+install: all $(ROOTLIBS) $(ROOTLINKS) $(ROOTLINT)
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/usr/src/lib/libpkg/sparc/Makefile	Fri Jun 05 10:28:40 2009 -0400
@@ -0,0 +1,28 @@
+#
+# 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
+# or http://www.opensolaris.org/os/licensing.
+# 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 2009 Sun Microsystems, Inc.  All rights reserved.
+# Use is subject to license terms.
+#
+
+include ../Makefile.com
+
+install: all $(ROOTLIBS) $(ROOTLINKS) $(ROOTLINT)
--- a/usr/src/lib/librestart/common/librestart.c	Fri Jun 05 10:27:16 2009 -0400
+++ b/usr/src/lib/librestart/common/librestart.c	Fri Jun 05 10:28:40 2009 -0400
@@ -107,6 +107,16 @@
 	restarter_instance_state_t	re_next_state;
 };
 
+/*
+ * A static no memory error message mc_error_t structure
+ * to be used in cases when memory errors are to be returned
+ * This avoids the need to attempt to allocate memory for the
+ * message, therefore getting into a cycle of no memory failures.
+ */
+mc_error_t mc_nomem_err = {
+	0, ENOMEM, sizeof ("Out of memory") - 1, "Out of memory"
+};
+
 static const char * const allocfail = "Allocation failure.\n";
 static const char * const rcbroken = "Repository connection broken.\n";
 
@@ -114,6 +124,61 @@
 
 int ndebug = 1;
 
+/* PRINTFLIKE3 */
+static mc_error_t *
+mc_error_create(mc_error_t *e, int type, const char *format, ...)
+{
+	mc_error_t	*le;
+	va_list		args;
+	int		size;
+
+	/*
+	 * If the type is ENOMEM and format is NULL, then
+	 * go ahead and return the default nomem error.
+	 * Otherwise, attempt to allocate the memory and if
+	 * that fails then there is no reason to continue.
+	 */
+	if (type == ENOMEM && format == NULL)
+		return (&mc_nomem_err);
+
+	if (e == NULL && (le = malloc(sizeof (mc_error_t))) == NULL)
+		return (&mc_nomem_err);
+	else
+		le = e;
+
+	le->type = type;
+	le->destroy = 1;
+	va_start(args, format);
+	size = vsnprintf(NULL, 0, format, args) + 1;
+	if (size >= RESTARTER_ERRMSGSZ) {
+		if ((le = realloc(e, sizeof (mc_error_t) +
+		    (size - RESTARTER_ERRMSGSZ))) == NULL) {
+			size = RESTARTER_ERRMSGSZ - 1;
+			le = e;
+		}
+	}
+
+	le->size = size;
+	(void) vsnprintf(le->msg, le->size, format, args);
+	va_end(args);
+
+	return (le);
+}
+
+void
+restarter_mc_error_destroy(mc_error_t *mc_err)
+{
+	if (mc_err == NULL)
+		return;
+
+	/*
+	 * If the error messages was allocated then free.
+	 */
+	if (mc_err->destroy) {
+		free(mc_err);
+	}
+}
+
 static void
 free_restarter_event_handle(struct restarter_event_handle *h)
 {
@@ -1975,16 +2040,20 @@
 	return (0);
 }
 
+
 /*
- * Eventually, we will return a structured error in the case of
- * retryable or abortable failures such as memory allocation errors and
- * repository connection failures.  For now, these failures are just
- * encoded in the failure string.
+ * Return an error message structure containing the error message
+ * with context, and the error so the caller can make a decision
+ * on what to do next.
+ *
+ * Because get_ids uses the mc_error_create() function which can
+ * reallocate the merr, this function must return the merr pointer
+ * in case it was reallocated.
  */
-static const char *
+static mc_error_t *
 get_profile(scf_propertygroup_t *methpg, scf_propertygroup_t *instpg,
     scf_property_t *prop, scf_value_t *val, const char *cmdline,
-    struct method_context *ci)
+    struct method_context *ci, mc_error_t *merr)
 {
 	char *buf = ci->vbuf;
 	ssize_t buf_sz = ci->vbuf_sz;
@@ -1992,12 +2061,16 @@
 	char *cp, *value;
 	const char *cmdp;
 	execattr_t *eap;
-	const char *errstr = NULL;
+	mc_error_t *err = merr;
+	int r;
 
 	if (!(get_astring_val(methpg, SCF_PROPERTY_PROFILE, buf, buf_sz, prop,
 	    val) == 0 || get_astring_val(instpg, SCF_PROPERTY_PROFILE, buf,
 	    buf_sz, prop, val) == 0))
-		return ("Could not get profile property.");
+		return (mc_error_create(merr, scf_error(),
+		    "Method context requires a profile, but the  \"%s\" "
+		    "property could not be read. scf_error is %s",
+		    SCF_PROPERTY_PROFILE, scf_strerror(scf_error())));
 
 	/* Extract the command from the command line. */
 	cp = strpbrk(cmdline, " \t");
@@ -2014,23 +2087,31 @@
 
 	eap = getexecprof(buf, KV_COMMAND, cmdp, GET_ONE);
 	if (eap == NULL)
-		return ("Could not find profile.");
+		return (mc_error_create(merr, ENOENT,
+		    "Could not find the execution profile \"%s\", "
+		    "command %s.", buf, cmdp));
 
 	/* Based on pfexec.c */
 
 	/* Get the euid first so we don't override ci->pwd for the uid. */
 	if ((value = kva_match(eap->attr, EXECATTR_EUID_KW)) != NULL) {
-		if (get_uid(value, ci, &ci->euid) != 0) {
+		if ((r = get_uid(value, ci, &ci->euid)) != 0) {
 			ci->euid = (uid_t)-1;
-			errstr = "Could not interpret profile euid.";
+			err = mc_error_create(merr, r,
+			    "Could not interpret profile euid value \"%s\", "
+			    "from the execution profile \"%s\", error %d.",
+			    value, buf, r);
 			goto out;
 		}
 	}
 
 	if ((value = kva_match(eap->attr, EXECATTR_UID_KW)) != NULL) {
-		if (get_uid(value, ci, &ci->uid) != 0) {
+		if ((r = get_uid(value, ci, &ci->uid)) != 0) {
 			ci->euid = ci->uid = (uid_t)-1;
-			errstr = "Could not interpret profile uid.";
+			err = mc_error_create(merr, r,
+			    "Could not interpret profile uid value \"%s\", "
+			    "from the execution profile \"%s\", error %d.",
+			    value, buf, r);
 			goto out;
 		}
 		ci->euid = ci->uid;
@@ -2039,7 +2120,9 @@
 	if ((value = kva_match(eap->attr, EXECATTR_GID_KW)) != NULL) {
 		ci->egid = ci->gid = get_gid(value);
 		if (ci->gid == (gid_t)-1) {
-			errstr = "Could not interpret profile gid.";
+			err = mc_error_create(merr, EINVAL,
+			    "Could not interpret profile gid value \"%s\", "
+			    "from the execution profile \"%s\".", value, buf);
 			goto out;
 		}
 	}
@@ -2047,7 +2130,9 @@
 	if ((value = kva_match(eap->attr, EXECATTR_EGID_KW)) != NULL) {
 		ci->egid = get_gid(value);
 		if (ci->egid == (gid_t)-1) {
-			errstr = "Could not interpret profile egid.";
+			err = mc_error_create(merr, EINVAL,
+			    "Could not interpret profile egid value \"%s\", "
+			    "from the execution profile \"%s\".", value, buf);
 			goto out;
 		}
 	}
@@ -2056,10 +2141,14 @@
 		ci->lpriv_set = priv_str_to_set(value, ",", NULL);
 		if (ci->lpriv_set == NULL) {
 			if (errno != EINVAL)
-				errstr = ALLOCFAIL;
+				err = mc_error_create(merr, ENOMEM,
+				    ALLOCFAIL);
 			else
-				errstr = "Could not interpret profile "
-				    "limitprivs.";
+				err = mc_error_create(merr, EINVAL,
+				    "Could not interpret profile "
+				    "limitprivs value \"%s\", from "
+				    "the execution profile \"%s\".",
+				    value, buf);
 			goto out;
 		}
 	}
@@ -2068,9 +2157,13 @@
 		ci->priv_set = priv_str_to_set(value, ",", NULL);
 		if (ci->priv_set == NULL) {
 			if (errno != EINVAL)
-				errstr = ALLOCFAIL;
+				err = mc_error_create(merr, ENOMEM,
+				    ALLOCFAIL);
 			else
-				errstr = "Could not interpret profile privs.";
+				err = mc_error_create(merr, EINVAL,
+				    "Could not interpret profile privs value "
+				    "\"%s\", from the execution profile "
+				    "\"%s\".", value, buf);
 			goto out;
 		}
 	}
@@ -2078,20 +2171,23 @@
 out:
 	free_execattr(eap);
 
-	return (errstr);
+	return (err);
 }
 
 /*
- * Eventually, we will return a structured error in the case of
- * retryable or abortable failures such as memory allocation errors and
- * repository connection failures.  For now, these failures are just
- * encoded in the failure string.
+ * Return an error message structure containing the error message
+ * with context, and the error so the caller can make a decision
+ * on what to do next.
+ *
+ * Because get_ids uses the mc_error_create() function which can
+ * reallocate the merr, this function must return the merr pointer
+ * in case it was reallocated.
  */
-static const char *
+static mc_error_t *
 get_ids(scf_propertygroup_t *methpg, scf_propertygroup_t *instpg,
-    scf_property_t *prop, scf_value_t *val, struct method_context *ci)
+    scf_property_t *prop, scf_value_t *val, struct method_context *ci,
+    mc_error_t *merr)
 {
-	char *errstr = NULL;
 	char *vbuf = ci->vbuf;
 	ssize_t vbuf_sz = ci->vbuf_sz;
 	int r;
@@ -2101,23 +2197,22 @@
 	 * another path of just setting the ids to defaults, instead of
 	 * attempting to get the ids here.
 	 */
-	if (methpg == NULL && instpg == NULL) {
-		errstr = "No property groups to get ids from.";
-		goto out;
-	}
+	if (methpg == NULL && instpg == NULL)
+		return (mc_error_create(merr, ENOENT,
+		    "No property groups to get ids from."));
 
 	if (!(get_astring_val(methpg, SCF_PROPERTY_USER,
 	    vbuf, vbuf_sz, prop, val) == 0 || get_astring_val(instpg,
 	    SCF_PROPERTY_USER, vbuf, vbuf_sz, prop,
-	    val) == 0)) {
-		errstr = "Could not get user property.";
-		goto out;
-	}
-
-	if (get_uid(vbuf, ci, &ci->uid) != 0) {
+	    val) == 0))
+		return (mc_error_create(merr, ENOENT,
+		    "Could not get \"%s\" property.", SCF_PROPERTY_USER));
+
+	if ((r = get_uid(vbuf, ci, &ci->uid)) != 0) {
 		ci->uid = (uid_t)-1;
-		errstr = "Could not interpret user property.";
-		goto out;
+		return (mc_error_create(merr, r,
+		    "Could not interpret \"%s\" property value \"%s\", "
+		    "error %d.", SCF_PROPERTY_USER, vbuf, r));
 	}
 
 	if (!(get_astring_val(methpg, SCF_PROPERTY_GROUP, vbuf, vbuf_sz, prop,
@@ -2126,16 +2221,18 @@
 		if (scf_error() == SCF_ERROR_NOT_FOUND) {
 			(void) strcpy(vbuf, ":default");
 		} else {
-			errstr = "Could not get group property.";
-			goto out;
+			return (mc_error_create(merr, ENOENT,
+			    "Could not get \"%s\" property.",
+			    SCF_PROPERTY_GROUP));
 		}
 	}
 
 	if (strcmp(vbuf, ":default") != 0) {
 		ci->gid = get_gid(vbuf);
 		if (ci->gid == (gid_t)-1) {
-			errstr = "Could not interpret group property.";
-			goto out;
+			return (mc_error_create(merr, ENOENT,
+			    "Could not interpret \"%s\" property value \"%s\".",
+			    SCF_PROPERTY_GROUP, vbuf));
 		}
 	} else {
 		switch (r = lookup_pwd(ci)) {
@@ -2145,18 +2242,18 @@
 
 		case ENOENT:
 			ci->gid = (gid_t)-1;
-			errstr = "No passwd entry.";
-			goto out;
+			return (mc_error_create(merr, ENOENT,
+			    "No passwd entry for uid \"%d\".", ci->uid));
 
 		case ENOMEM:
-			errstr = "Out of memory.";
-			goto out;
+			return (mc_error_create(merr, ENOMEM,
+			    "Out of memory."));
 
 		case EIO:
 		case EMFILE:
 		case ENFILE:
-			errstr = "getpwuid_r() failed.";
-			goto out;
+			return (mc_error_create(merr, ENFILE,
+			    "getpwuid_r() failed, error %d.", r));
 
 		default:
 			bad_fail("lookup_pwd", r);
@@ -2169,8 +2266,9 @@
 		if (scf_error() == SCF_ERROR_NOT_FOUND) {
 			(void) strcpy(vbuf, ":default");
 		} else {
-			errstr = "Could not get supplemental groups property.";
-			goto out;
+			return (mc_error_create(merr, ENOENT,
+			    "Could not get supplemental groups (\"%s\") "
+			    "property.", SCF_PROPERTY_SUPP_GROUPS));
 		}
 	}
 
@@ -2180,13 +2278,15 @@
 			break;
 
 		case EINVAL:
-			errstr =
-			    "Could not interpret supplemental groups property.";
-			goto out;
+			return (mc_error_create(merr, EINVAL,
+			    "Could not interpret supplemental groups (\"%s\") "
+			    "property value \"%s\".", SCF_PROPERTY_SUPP_GROUPS,
+			    vbuf));
 
 		case E2BIG:
-			errstr = "Too many supplemental groups.";
-			goto out;
+			return (mc_error_create(merr, E2BIG,
+			    "Too many supplemental groups values in \"%s\".",
+			    vbuf));
 
 		default:
 			bad_fail("get_groups", r);
@@ -2201,8 +2301,9 @@
 		if (scf_error() == SCF_ERROR_NOT_FOUND) {
 			(void) strcpy(vbuf, ":default");
 		} else {
-			errstr = "Could not get privileges property.";
-			goto out;
+			return (mc_error_create(merr, ENOENT,
+			    "Could not get \"%s\" property.",
+			    SCF_PROPERTY_PRIVILEGES));
 		}
 	}
 
@@ -2214,12 +2315,14 @@
 		ci->priv_set = priv_str_to_set(vbuf, ",", NULL);
 		if (ci->priv_set == NULL) {
 			if (errno != EINVAL) {
-				errstr = ALLOCFAIL;
+				return (mc_error_create(merr, ENOMEM,
+				    ALLOCFAIL));
 			} else {
-				errstr =
-				    "Could not interpret privileges property.";
+				return (mc_error_create(merr, EINVAL,
+				    "Could not interpret \"%s\" "
+				    "property value \"%s\".",
+				    SCF_PROPERTY_PRIVILEGES, vbuf));
 			}
-			goto out;
 		}
 	}
 
@@ -2229,8 +2332,9 @@
 		if (scf_error() == SCF_ERROR_NOT_FOUND) {
 			(void) strcpy(vbuf, ":default");
 		} else {
-			errstr = "Could not get limit_privileges property.";
-			goto out;
+			return (mc_error_create(merr, ENOENT,
+			    "Could not get \"%s\" property.",
+			    SCF_PROPERTY_LIMIT_PRIVILEGES));
 		}
 	}
 
@@ -2244,17 +2348,16 @@
 
 	ci->lpriv_set = priv_str_to_set(vbuf, ",", NULL);
 	if (ci->lpriv_set == NULL) {
-		if (errno != EINVAL)
-			errstr = ALLOCFAIL;
-		else {
-			errstr =
-			    "Could not interpret limit_privileges property.";
+		if (errno != EINVAL) {
+			return (mc_error_create(merr, ENOMEM, ALLOCFAIL));
+		} else {
+			return (mc_error_create(merr, EINVAL,
+			    "Could not interpret \"%s\" property value \"%s\".",
+			    SCF_PROPERTY_LIMIT_PRIVILEGES, vbuf));
 		}
-		goto out;
 	}
 
-out:
-	return (errstr);
+	return (merr);
 }
 
 static int
@@ -2328,19 +2431,42 @@
 
 /*
  * Fetch method context information from the repository, allocate and fill
- * a method_context structure, return it in *mcpp, and return NULL.  On error,
- * return a human-readable string which indicates the error.
+ * a method_context structure, return it in *mcpp, and return NULL.
  *
  * If no method_context is defined, original init context is provided, where
  * the working directory is '/', and uid/gid are 0/0.  But if a method_context
  * is defined at any level the smf_method(5) method_context defaults are used.
  *
- * Eventually, we will return a structured error in the case of
- * retryable or abortable failures such as memory allocation errors and
- * repository connection failures.  For now, these failures are just
- * encoded in the failure string.
+ * Return an error message structure containing the error message
+ * with context, and the error so the caller can make a decision
+ * on what to do next.
+ *
+ * Error Types :
+ * 	E2BIG		Too many values or entry is too big
+ * 	EINVAL		Invalid value
+ * 	EIO		an I/O error has occured
+ * 	ENOENT		no entry for value
+ * 	ENOMEM		out of memory
+ * 	ENOTSUP		Version mismatch
+ * 	ERANGE		value is out of range
+ * 	EMFILE/ENFILE	out of file descriptors
+ *
+ * 	SCF_ERROR_BACKEND_ACCESS
+ * 	SCF_ERROR_CONNECTION_BROKEN
+ * 	SCF_ERROR_DELETED
+ * 	SCF_ERROR_CONSTRAINT_VIOLATED
+ * 	SCF_ERROR_HANDLE_DESTROYED
+ * 	SCF_ERROR_INTERNAL
+ * 	SCF_ERROR_INVALID_ARGUMENT
+ * 	SCF_ERROR_NO_MEMORY
+ * 	SCF_ERROR_NO_RESOURCES
+ * 	SCF_ERROR_NOT_BOUND
+ * 	SCF_ERROR_NOT_FOUND
+ * 	SCF_ERROR_NOT_SET
+ * 	SCF_ERROR_TYPE_MISMATCH
+ *
  */
-const char *
+mc_error_t *
 restarter_get_method_context(uint_t version, scf_instance_t *inst,
     scf_snapshot_t *snap, const char *mname, const char *cmdline,
     struct method_context **mcpp)
@@ -2353,22 +2479,31 @@
 	scf_value_t *val = NULL;
 	scf_type_t ty;
 	uint8_t use_profile;
-	int ret;
+	int ret = 0;
 	int mc_used = 0;
-	const char *errstr = NULL;
+	mc_error_t *err = NULL;
 	struct method_context *cip;
 
+	if ((err = malloc(sizeof (mc_error_t))) == NULL)
+		return (mc_error_create(NULL, ENOMEM, NULL));
+
+	/* Set the type to zero to track if an error occured. */
+	err->type = 0;
+
 	if (version != RESTARTER_METHOD_CONTEXT_VERSION)
-		return ("Unknown method_context version.");
+		return (mc_error_create(err, ENOTSUP,
+		    "Invalid client version %d. (Expected %d)",
+		    version, RESTARTER_METHOD_CONTEXT_VERSION));
 
 	/* Get the handle before we allocate anything. */
 	h = scf_instance_handle(inst);
 	if (h == NULL)
-		return (scf_strerror(scf_error()));
+		return (mc_error_create(err, scf_error(),
+		    scf_strerror(scf_error())));
 
 	cip = malloc(sizeof (*cip));
 	if (cip == NULL)
-		return (ALLOCFAIL);
+		return (mc_error_create(err, ENOMEM, ALLOCFAIL));
 
 	(void) memset(cip, 0, sizeof (*cip));
 	cip->uid = (uid_t)-1;
@@ -2381,14 +2516,16 @@
 	cip->vbuf = malloc(cip->vbuf_sz);
 	if (cip->vbuf == NULL) {
 		free(cip);
-		return (ALLOCFAIL);
+		return (mc_error_create(err, ENOMEM, ALLOCFAIL));
 	}
 
 	if ((instpg = scf_pg_create(h)) == NULL ||
 	    (methpg = scf_pg_create(h)) == NULL ||
 	    (prop = scf_property_create(h)) == NULL ||
 	    (val = scf_value_create(h)) == NULL) {
-		errstr = ALLOCFAIL;
+		err = mc_error_create(err, scf_error(),
+		    "Failed to create repository object: %s\n",
+		    scf_strerror(scf_error()));
 		goto out;
 	}
 
@@ -2401,14 +2538,17 @@
 
 	if (scf_instance_get_pg_composed(inst, snap, mname, methpg) !=
 	    SCF_SUCCESS) {
-		errstr = scf_strerror(scf_error());
+		err = mc_error_create(err, scf_error(), "Unable to get the "
+		    "\"%s\" method, %s", mname, scf_strerror(scf_error()));
 		goto out;
 	}
 
 	if (scf_instance_get_pg_composed(inst, snap, SCF_PG_METHOD_CONTEXT,
 	    instpg) != SCF_SUCCESS) {
 		if (scf_error() != SCF_ERROR_NOT_FOUND) {
-			errstr = scf_strerror(scf_error());
+			err = mc_error_create(err, scf_error(),
+			    "Unable to retrieve the \"%s\" property group, %s",
+			    SCF_PG_METHOD_CONTEXT, scf_strerror(scf_error()));
 			goto out;
 		}
 		scf_pg_destroy(instpg);
@@ -2429,13 +2569,14 @@
 	case ENOENT:
 		break;
 	case ENOMEM:
-		errstr = "Out of memory.";
+		err = mc_error_create(err, ret, "Out of memory.");
 		goto out;
 	case EINVAL:
-		errstr = "Invalid method environment.";
+		err = mc_error_create(err, ret, "Invalid method environment.");
 		goto out;
 	default:
-		errstr = scf_strerror(ret);
+		err = mc_error_create(err, ret,
+		    "Get method environment failed : %s\n", scf_strerror(ret));
 		goto out;
 	}
 
@@ -2457,11 +2598,14 @@
 			break;
 
 		case SCF_ERROR_CONNECTION_BROKEN:
-			errstr = RCBROKEN;
+			err = mc_error_create(err, SCF_ERROR_CONNECTION_BROKEN,
+			    RCBROKEN);
 			goto out;
 
 		case SCF_ERROR_DELETED:
-			errstr = "\"use_profile\" property deleted.";
+			err = mc_error_create(err, SCF_ERROR_NOT_FOUND,
+			    "Could not find property group \"%s\"",
+			    pg == NULL ? SCF_PG_METHOD_CONTEXT : mname);
 			goto out;
 
 		case SCF_ERROR_HANDLE_MISMATCH:
@@ -2472,47 +2616,60 @@
 		}
 	} else {
 		if (scf_property_type(prop, &ty) != SCF_SUCCESS) {
-			switch (scf_error()) {
+			ret = scf_error();
+			switch (ret) {
 			case SCF_ERROR_CONNECTION_BROKEN:
-				errstr = RCBROKEN;
+				err = mc_error_create(err,
+				    SCF_ERROR_CONNECTION_BROKEN, RCBROKEN);
 				break;
 
 			case SCF_ERROR_DELETED:
-				errstr = "\"use profile\" property deleted.";
+				err = mc_error_create(err,
+				    SCF_ERROR_NOT_FOUND,
+				    "Could not find property group \"%s\"",
+				    pg == NULL ? SCF_PG_METHOD_CONTEXT : mname);
 				break;
 
 			case SCF_ERROR_NOT_SET:
 			default:
-				bad_fail("scf_property_type", scf_error());
+				bad_fail("scf_property_type", ret);
 			}
 
 			goto out;
 		}
 
 		if (ty != SCF_TYPE_BOOLEAN) {
-			errstr = "\"use profile\" property is not boolean.";
+			err = mc_error_create(err,
+			    SCF_ERROR_TYPE_MISMATCH,
+			    "\"%s\" property is not boolean in property group "
+			    "\"%s\".", SCF_PROPERTY_USE_PROFILE,
+			    pg == NULL ? SCF_PG_METHOD_CONTEXT : mname);
 			goto out;
 		}
 
 		if (scf_property_get_value(prop, val) != SCF_SUCCESS) {
-			switch (scf_error()) {
+			ret = scf_error();
+			switch (ret) {
 			case SCF_ERROR_CONNECTION_BROKEN:
-				errstr = RCBROKEN;
+				err = mc_error_create(err,
+				    SCF_ERROR_CONNECTION_BROKEN, RCBROKEN);
 				break;
 
 			case SCF_ERROR_CONSTRAINT_VIOLATED:
-				errstr =
-				    "\"use profile\" property has multiple "
-				    "values.";
+				err = mc_error_create(err,
+				    SCF_ERROR_CONSTRAINT_VIOLATED,
+				    "\"%s\" property has multiple values.",
+				    SCF_PROPERTY_USE_PROFILE);
 				break;
 
 			case SCF_ERROR_NOT_FOUND:
-				errstr = "\"use profile\" property has no "
-				    "values.";
+				err = mc_error_create(err,
+				    SCF_ERROR_NOT_FOUND,
+				    "\"%s\" property has no values.",
+				    SCF_PROPERTY_USE_PROFILE);
 				break;
-
 			default:
-				bad_fail("scf_property_get_value", scf_error());
+				bad_fail("scf_property_get_value", ret);
 			}
 
 			goto out;
@@ -2524,12 +2681,12 @@
 
 		/* get ids & privileges */
 		if (use_profile)
-			errstr = get_profile(pg, instpg, prop, val, cmdline,
-			    cip);
+			err = get_profile(pg, instpg, prop, val, cmdline,
+			    cip, err);
 		else
-			errstr = get_ids(pg, instpg, prop, val, cip);
-
-		if (errstr != NULL)
+			err = get_ids(pg, instpg, prop, val, cip, err);
+
+		if (err->type != 0)
 			goto out;
 	}
 
@@ -2539,24 +2696,26 @@
 	    (instpg != NULL && scf_pg_get_property(instpg,
 	    SCF_PROPERTY_WORKING_DIRECTORY, prop) == SCF_SUCCESS)) {
 		if (scf_property_get_value(prop, val) != SCF_SUCCESS) {
-			switch (scf_error()) {
+			ret = scf_error();
+			switch (ret) {
 			case SCF_ERROR_CONNECTION_BROKEN:
-				errstr = RCBROKEN;
+				err = mc_error_create(err, ret, RCBROKEN);
 				break;
 
 			case SCF_ERROR_CONSTRAINT_VIOLATED:
-				errstr =
-				    "\"working directory\" property has "
-				    "multiple values.";
+				err = mc_error_create(err, ret,
+				    "\"%s\" property has multiple values.",
+				    SCF_PROPERTY_WORKING_DIRECTORY);
 				break;
 
 			case SCF_ERROR_NOT_FOUND:
-				errstr = "\"working directory\" property has "
-				    "no values.";
+				err = mc_error_create(err, ret,
+				    "\"%s\" property has no values.",
+				    SCF_PROPERTY_WORKING_DIRECTORY);
 				break;
 
 			default:
-				bad_fail("scf_property_get_value", scf_error());
+				bad_fail("scf_property_get_value", ret);
 			}
 
 			goto out;
@@ -2566,25 +2725,27 @@
 		ret = scf_value_get_astring(val, cip->vbuf, cip->vbuf_sz);
 		assert(ret != -1);
 	} else {
-		switch (scf_error()) {
+		ret = scf_error();
+		switch (ret) {
 		case SCF_ERROR_NOT_FOUND:
 			/* okay if missing. */
 			(void) strcpy(cip->vbuf, ":default");
 			break;
 
 		case SCF_ERROR_CONNECTION_BROKEN:
-			errstr = RCBROKEN;
+			err = mc_error_create(err, ret, RCBROKEN);
 			goto out;
 
 		case SCF_ERROR_DELETED:
-			errstr = "\"working_directory\" property deleted.";
+			err = mc_error_create(err, ret,
+			    "Property group could not be found");
 			goto out;
 
 		case SCF_ERROR_HANDLE_MISMATCH:
 		case SCF_ERROR_INVALID_ARGUMENT:
 		case SCF_ERROR_NOT_SET:
 		default:
-			bad_fail("scf_pg_get_property", scf_error());
+			bad_fail("scf_pg_get_property", ret);
 		}
 	}
 
@@ -2595,14 +2756,15 @@
 			break;
 
 		case ENOMEM:
-			errstr = "Out of memory.";
+			err = mc_error_create(err, ret, "Out of memory.");
 			goto out;
 
 		case ENOENT:
 		case EIO:
 		case EMFILE:
 		case ENFILE:
-			errstr = "Could not get passwd entry.";
+			err = mc_error_create(err, ret,
+			    "Could not get passwd entry.");
 			goto out;
 
 		default:
@@ -2611,13 +2773,13 @@
 
 		cip->working_dir = strdup(cip->pwd.pw_dir);
 		if (cip->working_dir == NULL) {
-			errstr = ALLOCFAIL;
+			err = mc_error_create(err, ENOMEM, ALLOCFAIL);
 			goto out;
 		}
 	} else {
 		cip->working_dir = strdup(cip->vbuf);
 		if (cip->working_dir == NULL) {
-			errstr = ALLOCFAIL;
+			err = mc_error_create(err, ENOMEM, ALLOCFAIL);
 			goto out;
 		}
 	}
@@ -2628,24 +2790,26 @@
 	    (instpg != NULL && scf_pg_get_property(instpg,
 	    SCF_PROPERTY_COREFILE_PATTERN, prop) == SCF_SUCCESS)) {
 		if (scf_property_get_value(prop, val) != SCF_SUCCESS) {
-			switch (scf_error()) {
+			ret = scf_error();
+			switch (ret) {
 			case SCF_ERROR_CONNECTION_BROKEN:
-				errstr = RCBROKEN;
+				err = mc_error_create(err, ret, RCBROKEN);
 				break;
 
 			case SCF_ERROR_CONSTRAINT_VIOLATED:
-				errstr =
-				    "\"core_pattern\" property has multiple "
-				    "values.";
+				err = mc_error_create(err, ret,
+				    "\"%s\" property has multiple values.",
+				    SCF_PROPERTY_COREFILE_PATTERN);
 				break;
 
 			case SCF_ERROR_NOT_FOUND:
-				errstr = "\"core_pattern\" property has no "
-				    "values.";
+				err = mc_error_create(err, ret,
+				    "\"%s\" property has no values.",
+				    SCF_PROPERTY_COREFILE_PATTERN);
 				break;
 
 			default:
-				bad_fail("scf_property_get_value", scf_error());
+				bad_fail("scf_property_get_value", ret);
 			}
 
 		} else {
@@ -2656,31 +2820,33 @@
 
 			cip->corefile_pattern = strdup(cip->vbuf);
 			if (cip->corefile_pattern == NULL) {
-				errstr = ALLOCFAIL;
+				err = mc_error_create(err, ENOMEM, ALLOCFAIL);
 				goto out;
 			}
 		}
 
 		mc_used++;
 	} else {
-		switch (scf_error()) {
+		ret = scf_error();
+		switch (ret) {
 		case SCF_ERROR_NOT_FOUND:
 			/* okay if missing. */
 			break;
 
 		case SCF_ERROR_CONNECTION_BROKEN:
-			errstr = RCBROKEN;
+			err = mc_error_create(err, ret, RCBROKEN);
 			goto out;
 
 		case SCF_ERROR_DELETED:
-			errstr = "\"corefile_pattern\" property deleted.";
+			err = mc_error_create(err, ret,
+			    "Property group could not be found");
 			goto out;
 
 		case SCF_ERROR_HANDLE_MISMATCH:
 		case SCF_ERROR_INVALID_ARGUMENT:
 		case SCF_ERROR_NOT_SET:
 		default:
-			bad_fail("scf_pg_get_property", scf_error());
+			bad_fail("scf_pg_get_property", ret);
 		}
 	}
 
@@ -2691,25 +2857,27 @@
 		    (instpg != NULL && scf_pg_get_property(instpg,
 		    SCF_PROPERTY_PROJECT, prop) == SCF_SUCCESS)) {
 			if (scf_property_get_value(prop, val) != SCF_SUCCESS) {
-				switch (scf_error()) {
+				ret = scf_error();
+				switch (ret) {
 				case SCF_ERROR_CONNECTION_BROKEN:
-					errstr = RCBROKEN;
+					err = mc_error_create(err, ret,
+					    RCBROKEN);
 					break;
 
 				case SCF_ERROR_CONSTRAINT_VIOLATED:
-					errstr =
-					    "\"project\" property has "
-					    "multiple values.";
+					err = mc_error_create(err, ret,
+					    "\"%s\" property has multiple "
+					    "values.", SCF_PROPERTY_PROJECT);
 					break;
 
 				case SCF_ERROR_NOT_FOUND:
-					errstr = "\"project\" property has "
-					    "no values.";
+					err = mc_error_create(err, ret,
+					    "\"%s\" property has no values.",
+					    SCF_PROPERTY_PROJECT);
 					break;
 
 				default:
-					bad_fail("scf_property_get_value",
-					    scf_error());
+					bad_fail("scf_property_get_value", ret);
 				}
 
 				(void) strcpy(cip->vbuf, ":default");
@@ -2729,36 +2897,39 @@
 			break;
 
 		case ENOMEM:
-			errstr = "Out of memory.";
+			err = mc_error_create(err, ret, "Out of memory.");
 			goto out;
 
 		case ENOENT:
-			errstr = "Missing passwd or project entry.";
+			err = mc_error_create(err, ret,
+			    "Missing passwd or project entry for \"%s\".",
+			    cip->vbuf);
 			goto out;
 
 		case EIO:
-			errstr = "I/O error.";
+			err = mc_error_create(err, ret, "I/O error.");
 			goto out;
 
 		case EMFILE:
 		case ENFILE:
-			errstr = "Out of file descriptors.";
+			err = mc_error_create(err, ret,
+			    "Out of file descriptors.");
 			goto out;
 
 		case -1:
-			errstr = "Name service switch is misconfigured.";
+			err = mc_error_create(err, ret,
+			    "Name service switch is misconfigured.");
 			goto out;
 
 		case ERANGE:
-			errstr = "Project ID too big.";
+		case E2BIG:
+			err = mc_error_create(err, ret,
+			    "Project ID \"%s\" too big.", cip->vbuf);
 			goto out;
 
 		case EINVAL:
-			errstr = "Project ID is invalid.";
-			goto out;
-
-		case E2BIG:
-			errstr = "Project entry is too big.";
+			err = mc_error_create(err, ret,
+			    "Project ID \"%s\" is invalid.", cip->vbuf);
 			goto out;
 
 		default:
@@ -2771,25 +2942,29 @@
 		    (instpg != NULL && scf_pg_get_property(instpg,
 		    SCF_PROPERTY_RESOURCE_POOL, prop) == SCF_SUCCESS)) {
 			if (scf_property_get_value(prop, val) != SCF_SUCCESS) {
-				switch (scf_error()) {
+				ret = scf_error();
+				switch (ret) {
 				case SCF_ERROR_CONNECTION_BROKEN:
-					errstr = RCBROKEN;
+					err = mc_error_create(err, ret,
+					    RCBROKEN);
 					break;
 
 				case SCF_ERROR_CONSTRAINT_VIOLATED:
-					errstr =
-					    "\"resource_pool\" property has "
-					    "multiple values.";
+					err = mc_error_create(err, ret,
+					    "\"%s\" property has multiple "
+					    "values.",
+					    SCF_PROPERTY_RESOURCE_POOL);
 					break;
 
 				case SCF_ERROR_NOT_FOUND:
-					errstr = "\"resource_pool\" property "
-					    "has no values.";
+					err = mc_error_create(err, ret,
+					    "\"%s\" property has no "
+					    "values.",
+					    SCF_PROPERTY_RESOURCE_POOL);
 					break;
 
 				default:
-					bad_fail("scf_property_get_value",
-					    scf_error());
+					bad_fail("scf_property_get_value", ret);
 				}
 
 				(void) strcpy(cip->vbuf, ":default");
@@ -2801,32 +2976,34 @@
 
 			mc_used++;
 		} else {
-			switch (scf_error()) {
+			ret = scf_error();
+			switch (ret) {
 			case SCF_ERROR_NOT_FOUND:
 				/* okay if missing. */
 				(void) strcpy(cip->vbuf, ":default");
 				break;
 
 			case SCF_ERROR_CONNECTION_BROKEN:
-				errstr = RCBROKEN;
+				err = mc_error_create(err, ret, RCBROKEN);
 				goto out;
 
 			case SCF_ERROR_DELETED:
-				errstr = "\"resource_pool\" property deleted.";
+				err = mc_error_create(err, ret,
+				    "property group could not be found.");
 				goto out;
 
 			case SCF_ERROR_HANDLE_MISMATCH:
 			case SCF_ERROR_INVALID_ARGUMENT:
 			case SCF_ERROR_NOT_SET:
 			default:
-				bad_fail("scf_pg_get_property", scf_error());
+				bad_fail("scf_pg_get_property", ret);
 			}
 		}
 
 		if (strcmp(cip->vbuf, ":default") != 0) {
 			cip->resource_pool = strdup(cip->vbuf);
 			if (cip->resource_pool == NULL) {
-				errstr = ALLOCFAIL;
+				err = mc_error_create(err, ENOMEM, ALLOCFAIL);
 				goto out;
 			}
 		}
@@ -2857,10 +3034,14 @@
 		free(cip->pwbuf);
 	free(cip->vbuf);
 
-	if (errstr != NULL)
+	if (err->type != 0) {
 		restarter_free_method_context(cip);
-
-	return (errstr);
+	} else {
+		restarter_mc_error_destroy(err);
+		err = NULL;
+	}
+
+	return (err);
 }
 
 /*
--- a/usr/src/lib/librestart/common/librestart.h	Fri Jun 05 10:27:16 2009 -0400
+++ b/usr/src/lib/librestart/common/librestart.h	Fri Jun 05 10:28:40 2009 -0400
@@ -78,6 +78,8 @@
 
 #define	RESTARTER_FLAG_DEBUG		1
 
+#define	RESTARTER_ERRMSGSZ		1024
+
 /*
  * Event types
  *	RESTARTER_EVENT_TYPE_ADD_INSTANCE
@@ -216,7 +218,7 @@
 ssize_t restarter_state_to_string(restarter_instance_state_t, char *, size_t);
 restarter_instance_state_t restarter_string_to_state(char *);
 
-#define	RESTARTER_METHOD_CONTEXT_VERSION	6
+#define	RESTARTER_METHOD_CONTEXT_VERSION	7
 
 struct method_context {
 	/* Stable */
@@ -240,10 +242,29 @@
 	ssize_t		pwbufsz;
 };
 
+/*
+ * An error structure that contains a message string, and a type
+ * that can be used to determine course of action by the reciever
+ * of the error structure.
+ *
+ * type - usually will be an errno equivalent but could contain
+ * 	defined error types for exampe SCF_ERROR_XXX
+ * msg - must be at the end of the structure as if the message is
+ * 	longer than EMSGSIZE we will reallocate the structure to
+ * 	handle the overflow
+ */
+typedef struct mc_error {
+	int	destroy;	/* Flag to indicate destruction steps */
+	int	type;		/* Type of error for decision making */
+	int	size;		/* The size of the error message string */
+	char 	msg[RESTARTER_ERRMSGSZ];
+} mc_error_t;
+
 int restarter_rm_libs_loadable(void);
 /* instance, restarter name, method name, command line, structure pointer */
-const char *restarter_get_method_context(uint_t, scf_instance_t *,
+mc_error_t *restarter_get_method_context(uint_t, scf_instance_t *,
     scf_snapshot_t *, const char *, const char *, struct method_context **);
+void restarter_mc_error_destroy(mc_error_t *);
 int restarter_set_method_context(struct method_context *, const char **);
 void restarter_free_method_context(struct method_context *);
 
--- a/usr/src/lib/librestart/common/mapfile-vers	Fri Jun 05 10:27:16 2009 -0400
+++ b/usr/src/lib/librestart/common/mapfile-vers	Fri Jun 05 10:28:40 2009 -0400
@@ -67,6 +67,7 @@
 	restarter_inst_reset_ractions_aux_fmri;
 	restarter_inst_reset_aux_fmri;
 	restarter_inst_set_aux_fmri;
+	restarter_mc_error_destroy;
     local:
 	*;
 };
--- a/usr/src/lib/libscf/common/lowlevel.c	Fri Jun 05 10:27:16 2009 -0400
+++ b/usr/src/lib/libscf/common/lowlevel.c	Fri Jun 05 10:28:40 2009 -0400
@@ -3533,12 +3533,28 @@
 	(void) pthread_mutex_unlock(&h->rh_lock);
 
 	if (ret == SCF_SUCCESS) {
-		if (!scf_is_compatible_type(base, type))
+		if (!scf_is_compatible_protocol_type(base, type))
 			return (scf_set_error(SCF_ERROR_TYPE_MISMATCH));
 	}
 	return (ret);
 }
 
+int
+scf_is_compatible_type(scf_type_t base_arg, scf_type_t type_arg)
+{
+	rep_protocol_value_type_t base = scf_type_to_protocol_type(base_arg);
+	rep_protocol_value_type_t type = scf_type_to_protocol_type(type_arg);
+
+	if (base == REP_PROTOCOL_TYPE_INVALID ||
+	    type == REP_PROTOCOL_TYPE_INVALID)
+		return (scf_set_error(SCF_ERROR_INVALID_ARGUMENT));
+
+	if (!scf_is_compatible_protocol_type(base, type))
+		return (scf_set_error(SCF_ERROR_TYPE_MISMATCH));
+
+	return (SCF_SUCCESS);
+}
+
 ssize_t
 scf_property_get_name(const scf_property_t *prop, char *out, size_t len)
 {
@@ -4187,7 +4203,8 @@
 		return (scf_set_error(SCF_ERROR_INVALID_ARGUMENT));
 	}
 
-	if (!scf_is_compatible_type(entry->entry_type, v->value_type)) {
+	if (!scf_is_compatible_protocol_type(entry->entry_type,
+	    v->value_type)) {
 		(void) pthread_mutex_unlock(&h->rh_lock);
 		return (scf_set_error(SCF_ERROR_TYPE_MISMATCH));
 	}
@@ -4360,7 +4377,7 @@
 		return (scf_set_error(SCF_ERROR_NOT_SET));
 	if (base == REP_PROTOCOL_TYPE_INVALID)
 		return (scf_set_error(SCF_ERROR_INVALID_ARGUMENT));
-	if (!scf_is_compatible_type(base, t))
+	if (!scf_is_compatible_protocol_type(base, t))
 		return (scf_set_error(SCF_ERROR_TYPE_MISMATCH));
 
 	return (SCF_SUCCESS);
@@ -4378,7 +4395,7 @@
 		(void) scf_set_error(SCF_ERROR_NOT_SET);
 		return (0);
 	}
-	if (!scf_is_compatible_type(t, val->value_type)) {
+	if (!scf_is_compatible_protocol_type(t, val->value_type)) {
 		(void) scf_set_error(SCF_ERROR_TYPE_MISMATCH);
 		return (0);
 	}
--- a/usr/src/lib/libscf/common/mapfile-vers	Fri Jun 05 10:27:16 2009 -0400
+++ b/usr/src/lib/libscf/common/mapfile-vers	Fri Jun 05 10:28:40 2009 -0400
@@ -281,6 +281,7 @@
 	scf_encode32;
 	scf_general_pg_setup;
 	_scf_handle_decorations;
+	scf_is_compatible_type;
 	_scf_notify_add_pgname;
 	_scf_notify_add_pgtype;
 	_scf_notify_wait;
--- a/usr/src/lib/libscf/common/scf_type.c	Fri Jun 05 10:27:16 2009 -0400
+++ b/usr/src/lib/libscf/common/scf_type.c	Fri Jun 05 10:28:40 2009 -0400
@@ -20,12 +20,10 @@
  */
 
 /*
- * Copyright 2008 Sun Microsystems, Inc.  All rights reserved.
+ * Copyright 2009 Sun Microsystems, Inc.  All rights reserved.
  * Use is subject to license terms.
  */
 
-#pragma ident	"%Z%%M%	%I%	%E% SMI"
-
 #include <repcache_protocol.h>
 #include "scf_type.h"
 #include <errno.h>
@@ -266,7 +264,7 @@
 }
 
 int
-scf_is_compatible_type(rep_protocol_value_type_t base,
+scf_is_compatible_protocol_type(rep_protocol_value_type_t base,
     rep_protocol_value_type_t new)
 {
 	rep_protocol_value_type_t t, cur;
--- a/usr/src/lib/libscf/common/scf_type.h	Fri Jun 05 10:27:16 2009 -0400
+++ b/usr/src/lib/libscf/common/scf_type.h	Fri Jun 05 10:28:40 2009 -0400
@@ -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,15 +19,13 @@
  * CDDL HEADER END
  */
 /*
- * Copyright 2004 Sun Microsystems, Inc.  All rights reserved.
+ * Copyright 2009 Sun Microsystems, Inc.  All rights reserved.
  * Use is subject to license terms.
  */
 
 #ifndef	_SCF_TYPE_H
 #define	_SCF_TYPE_H
 
-#pragma ident	"%Z%%M%	%I%	%E% SMI"
-
 #include <repcache_protocol.h>
 
 #ifdef	__cplusplus
@@ -39,7 +36,7 @@
 
 rep_protocol_value_type_t scf_proto_underlying_type(rep_protocol_value_type_t);
 
-int scf_is_compatible_type(rep_protocol_value_type_t,
+int scf_is_compatible_protocol_type(rep_protocol_value_type_t,
     rep_protocol_value_type_t);
 
 #ifdef	__cplusplus
--- a/usr/src/lib/libscf/inc/libscf_priv.h	Fri Jun 05 10:27:16 2009 -0400
+++ b/usr/src/lib/libscf/inc/libscf_priv.h	Fri Jun 05 10:28:40 2009 -0400
@@ -532,6 +532,13 @@
 void scf_get_boot_config(uint8_t *);
 int scf_is_fastboot_default(void);
 
+/*
+ * scf_is_compatible_type()
+ * Return true if the second type is the same type, or a subtype of the
+ * first.
+ */
+int scf_is_compatible_type(scf_type_t, scf_type_t);
+
 #ifdef	__cplusplus
 }
 #endif
--- a/usr/src/lib/libzfs/common/libzfs_pool.c	Fri Jun 05 10:27:16 2009 -0400
+++ b/usr/src/lib/libzfs/common/libzfs_pool.c	Fri Jun 05 10:28:40 2009 -0400
@@ -1504,10 +1504,10 @@
 }
 
 /*
- * Helper function for zpool_get_config_physpath().
+ * Helper function for zpool_get_physpaths().
  */
 static int
-vdev_get_physpath(nvlist_t *config, char *physpath, size_t physpath_size,
+vdev_get_one_physpath(nvlist_t *config, char *physpath, size_t physpath_size,
     size_t *bytes_written)
 {
 	size_t bytes_left, pos, rsz;
@@ -1535,6 +1535,57 @@
 	return (0);
 }
 
+static int
+vdev_get_physpaths(nvlist_t *nv, char *physpath, size_t phypath_size,
+    size_t *rsz, boolean_t is_spare)
+{
+	char *type;
+	int ret;
+
+	if (nvlist_lookup_string(nv, ZPOOL_CONFIG_TYPE, &type) != 0)
+		return (EZFS_INVALCONFIG);
+
+	if (strcmp(type, VDEV_TYPE_DISK) == 0) {
+		/*
+		 * An active spare device has ZPOOL_CONFIG_IS_SPARE set.
+		 * For a spare vdev, we only want to boot from the active
+		 * spare device.
+		 */
+		if (is_spare) {
+			uint64_t spare = 0;
+			(void) nvlist_lookup_uint64(nv, ZPOOL_CONFIG_IS_SPARE,
+			    &spare);
+			if (!spare)
+				return (EZFS_INVALCONFIG);
+		}
+
+		if (vdev_online(nv)) {
+			if ((ret = vdev_get_one_physpath(nv, physpath,
+			    phypath_size, rsz)) != 0)
+				return (ret);
+		}
+	} else if (strcmp(type, VDEV_TYPE_MIRROR) == 0 ||
+	    strcmp(type, VDEV_TYPE_REPLACING) == 0 ||
+	    (is_spare = (strcmp(type, VDEV_TYPE_SPARE) == 0))) {
+		nvlist_t **child;
+		uint_t count;
+		int i, ret;
+
+		if (nvlist_lookup_nvlist_array(nv,
+		    ZPOOL_CONFIG_CHILDREN, &child, &count) != 0)
+			return (EZFS_INVALCONFIG);
+
+		for (i = 0; i < count; i++) {
+			ret = vdev_get_physpaths(child[i], physpath,
+			    phypath_size, rsz, is_spare);
+			if (ret == EZFS_NOSPC)
+				return (ret);
+		}
+	}
+
+	return (EZFS_POOL_INVALARG);
+}
+
 /*
  * Get phys_path for a root pool config.
  * Return 0 on success; non-zero on failure.
@@ -1545,10 +1596,8 @@
 	size_t rsz;
 	nvlist_t *vdev_root;
 	nvlist_t **child;
-	nvlist_t **child2;
 	uint_t count;
 	char *type;
-	int j, ret;
 
 	rsz = 0;
 
@@ -1569,40 +1618,8 @@
 	    pool_uses_efi(vdev_root))
 		return (EZFS_POOL_INVALARG);
 
-	if (nvlist_lookup_string(child[0], ZPOOL_CONFIG_TYPE, &type) != 0)
-		return (EZFS_INVALCONFIG);
-
-	if (strcmp(type, VDEV_TYPE_DISK) == 0) {
-		if (vdev_online(child[0])) {
-			if ((ret = vdev_get_physpath(child[0], physpath,
-			    phypath_size, &rsz)) != 0)
-				return (ret);
-		}
-	} else if (strcmp(type, VDEV_TYPE_MIRROR) == 0) {
-
-		if (nvlist_lookup_nvlist_array(child[0],
-		    ZPOOL_CONFIG_CHILDREN, &child2, &count) != 0)
-			return (EZFS_INVALCONFIG);
-
-		for (j = 0; j < count; j++) {
-			if (nvlist_lookup_string(child2[j], ZPOOL_CONFIG_TYPE,
-			    &type) != 0)
-				return (EZFS_INVALCONFIG);
-
-			if (strcmp(type, VDEV_TYPE_DISK) != 0)
-				return (EZFS_POOL_INVALARG);
-
-			if (vdev_online(child2[j])) {
-				ret = vdev_get_physpath(child2[j],
-				    physpath, phypath_size, &rsz);
-
-				if (ret == EZFS_NOSPC)
-					return (ret);
-			}
-		}
-	} else {
-		return (EZFS_POOL_INVALARG);
-	}
+	(void) vdev_get_physpaths(child[0], physpath, phypath_size, &rsz,
+	    B_FALSE);
 
 	/* No online devices */
 	if (rsz == 0)
@@ -1938,6 +1955,14 @@
 			(void) fprintf(stderr, dgettext(TEXT_DOMAIN, "Please "
 			    "be sure to invoke %s to make '%s' bootable.\n"),
 			    BOOTCMD, new_disk);
+
+			/*
+			 * XXX need a better way to prevent user from
+			 * booting up a half-baked vdev.
+			 */
+			(void) fprintf(stderr, dgettext(TEXT_DOMAIN, "Make "
+			    "sure to wait until resilver is done "
+			    "before rebooting.\n"));
 		}
 		return (0);
 	}
--- a/usr/src/lib/mpapi/libmpscsi_vhci/common/MP_GetMultipathLusPlugin.c	Fri Jun 05 10:27:16 2009 -0400
+++ b/usr/src/lib/mpapi/libmpscsi_vhci/common/MP_GetMultipathLusPlugin.c	Fri Jun 05 10:28:40 2009 -0400
@@ -19,7 +19,7 @@
  * CDDL HEADER END
  */
 /*
- * Copyright 2008 Sun Microsystems, Inc.  All rights reserved.
+ * Copyright 2009 Sun Microsystems, Inc.  All rights reserved.
  * Use is subject to license terms.
  */
 
@@ -61,21 +61,27 @@
 
 		if (haveList && (numNodes < pOidList->oidCount)) {
 
+			/* skip the node which is not online */
+			if (di_state(sv_child_node) != 0) {
+				sv_child_node = di_sibling_node(sv_child_node);
+				continue;
+			}
+
 			instNum =
-			(MP_UINT64)di_instance(sv_child_node);
+			    (MP_UINT64)di_instance(sv_child_node);
 
 			log(LOG_INFO, "getOidList()",
 			    " - instance number is: %llx",
 			    instNum);
 
 			pOidList->oids[numNodes].objectType =
-			MP_OBJECT_TYPE_MULTIPATH_LU;
+			    MP_OBJECT_TYPE_MULTIPATH_LU;
 
 			pOidList->oids[numNodes].ownerId =
-			g_pluginOwnerID;
+			    g_pluginOwnerID;
 
 			pOidList->oids[numNodes].objectSequenceNumber =
-			instNum;
+			    instNum;
 		}
 
 		++numNodes;
@@ -147,10 +153,10 @@
 		}
 
 		pOidList->oids[0].objectType =
-		MP_OBJECT_TYPE_MULTIPATH_LU;
+		    MP_OBJECT_TYPE_MULTIPATH_LU;
 
 		pOidList->oids[0].ownerId =
-		g_pluginOwnerID;
+		    g_pluginOwnerID;
 
 		*ppList = pOidList;
 
--- a/usr/src/lib/pam_modules/authtok_check/dict.c	Fri Jun 05 10:27:16 2009 -0400
+++ b/usr/src/lib/pam_modules/authtok_check/dict.c	Fri Jun 05 10:28:40 2009 -0400
@@ -19,12 +19,10 @@
  * CDDL HEADER END
  */
 /*
- * Copyright 2008 Sun Microsystems, Inc.  All rights reserved.
+ * Copyright 2009 Sun Microsystems, Inc.  All rights reserved.
  * Use is subject to license terms.
  */
 
-#pragma ident	"%Z%%M%	%I%	%E% SMI"
-
 #include <sys/stat.h>
 #include <stdio.h>
 #include <syslog.h>
@@ -117,7 +115,7 @@
  * database_present()
  *
  * returns 0 if the database files are found, and the database size is
- * greater than 0
+ * greater than 0 and the database version matches the current version.
  */
 int
 database_present(char *path)
@@ -126,6 +124,7 @@
 	char dict_hwm[PATH_MAX];
 	char dict_pwd[PATH_MAX];
 	char dict_pwi[PATH_MAX];
+	PWDICT *dict;
 
 	(void) snprintf(dict_hwm, sizeof (dict_hwm), "%s/%s", path,
 	    DICT_DATABASE_HWM);
@@ -139,6 +138,13 @@
 	    stat(dict_pwi, &st) == -1)
 		return (NO_DICTDATABASE);
 
+	/* verify database version number by trying to open it */
+	if ((dict = PWOpen(path, "r")) == NULL) {
+		/* the files are there, but an outdated version */
+		PWRemove(path);
+		return (NO_DICTDATABASE);
+	}
+	(void) PWClose(dict);
 	return (0);
 }
 
--- a/usr/src/lib/pam_modules/authtok_check/fascist.c	Fri Jun 05 10:27:16 2009 -0400
+++ b/usr/src/lib/pam_modules/authtok_check/fascist.c	Fri Jun 05 10:28:40 2009 -0400
@@ -1,10 +1,8 @@
 /*
- * Copyright 2006 Sun Microsystems, Inc.  All rights reserved.
+ * Copyright 2009 Sun Microsystems, Inc.  All rights reserved.
  * Use is subject to license terms.
  */
 
-#pragma ident	"%Z%%M%	%I%	%E% SMI"
-
 /*
  * This program is copyright Alec Muffett 1993. The author disclaims all
  * responsibility or liability with respect to it's usage or its effect
@@ -388,9 +386,9 @@
 int
 FascistLook(PWDICT *pwp, char *instring)
 {
-int i;
+	int i;
 	char *password;
-	int32 notfound;
+	uint32_t notfound;
 	char rpassword[PATH_MAX];
 
 	notfound = PW_WORDS(pwp);
@@ -408,27 +406,27 @@
 	 */
 
 	for (i = 0; r_destructors[i]; i++) {
-	char *a;
+		char *a;
 
 		if (!(a = Mangle(password, r_destructors[i]))) {
-		    continue;
+			continue;
 		}
 
 		if (FindPW(pwp, a) != notfound) {
-		    return (DICTIONARY_WORD);
+			return (DICTIONARY_WORD);
 		}
 	}
 
 	(void) strlcpy(password, Reverse(password), PATH_MAX);
 
 	for (i = 0; r_destructors[i]; i++) {
-	char *a;
+		char *a;
 
 		if (!(a = Mangle(password, r_destructors[i]))) {
 			continue;
 		}
 		if (FindPW(pwp, a) != notfound) {
-		    return (REVERSE_DICTIONARY_WORD);
+			return (REVERSE_DICTIONARY_WORD);
 		}
 	}
 
--- a/usr/src/lib/pam_modules/authtok_check/packer.h	Fri Jun 05 10:27:16 2009 -0400
+++ b/usr/src/lib/pam_modules/authtok_check/packer.h	Fri Jun 05 10:28:40 2009 -0400
@@ -1,13 +1,10 @@
 /*
- * Copyright 2005 Sun Microsystems, Inc.  All rights reserved.
+ * Copyright 2009 Sun Microsystems, Inc.  All rights reserved.
  * Use is subject to license terms.
  */
-
 #ifndef _PACKER_H
 #define	_PACKER_H
 
-#pragma ident	"%Z%%M%	%I%	%E% SMI"
-
 #ifdef __cplusplus
 extern "C" {
 #endif
@@ -38,9 +35,6 @@
 #define	TRUNCSTRINGSIZE	(PATH_MAX/4)
 #define	STRINGSIZE	PATH_MAX
 
-typedef unsigned char int8;
-typedef unsigned short int int16;
-typedef unsigned long int int32;
 #ifndef NUMWORDS
 #define	NUMWORDS 	16
 #endif
@@ -49,10 +43,10 @@
 
 struct pi_header
 {
-	int32 pih_magic;
-	int32 pih_numwords;
-	int16 pih_blocklen;
-	int16 pih_pad;
+	uint32_t pih_magic;
+	uint32_t pih_numwords;
+	uint16_t pih_blocklen;
+	uint16_t pih_pad;
 };
 
 typedef struct
@@ -61,21 +55,21 @@
 	FILE *dfp;
 	FILE *wfp;
 
-	int32 flags;
+	uint32_t flags;
 #define	PFOR_WRITE	0x0001
 #define	PFOR_FLUSH	0x0002
 #define	PFOR_USEHWMS	0x0004
 
-	int32 hwms[256];
+	uint32_t hwms[256];
 
 	struct pi_header header;
 
-	int count;
+	uint32_t count;
 	char data[NUMWORDS][MAXWORDLEN];
 } PWDICT;
 
 #define	PW_WORDS(x) ((x)->header.pih_numwords)
-#define	PIH_MAGIC 0x70775631
+#define	PIH_MAGIC 0x70775632
 
 void PWRemove(char *);
 PWDICT *PWOpen(char *, char *);
@@ -86,12 +80,12 @@
 #define	STRCMP(a, b)		strcmp((a), (b))
 
 char	*Trim(register char *);
-int32	FindPW(PWDICT *, char *);
+uint32_t	FindPW(PWDICT *, char *);
 int	PWClose(PWDICT *);
 int	PutPW(PWDICT *, char *);
 char	Chop(register char *);
 char	Chomp(register char *);
-char	*GetPW(PWDICT *, int32);
+char	*GetPW(PWDICT *, uint32_t);
 
 #define	DATABASE_OPEN_FAIL		-1
 #define	DICTIONARY_WORD			2
--- a/usr/src/lib/pam_modules/authtok_check/packlib.c	Fri Jun 05 10:27:16 2009 -0400
+++ b/usr/src/lib/pam_modules/authtok_check/packlib.c	Fri Jun 05 10:28:40 2009 -0400
@@ -1,10 +1,8 @@
 /*
- * Copyright 2005 Sun Microsystems, Inc.  All rights reserved.
+ * Copyright 2009 Sun Microsystems, Inc.  All rights reserved.
  * Use is subject to license terms.
  */
 
-#pragma ident	"%Z%%M%	%I%	%E% SMI"
-
 /*
  * This program is copyright Alec Muffett 1993. The author disclaims all
  * responsibility or liability with respect to it's usage or its effect
@@ -195,10 +193,10 @@
 
 	if ((pwp->flags & PFOR_FLUSH) || !(pwp->count % NUMWORDS)) {
 		int i;
-		int32 datum;
+		uint32_t datum;
 		register char *ostr;
 
-		datum = (int32) ftell(pwp->dfp);
+		datum = (uint32_t)ftell(pwp->dfp);
 
 		(void) fwrite((char *)&datum, sizeof (datum), 1, pwp->ifp);
 
@@ -214,10 +212,10 @@
 			nstr = pwp->data[i];
 
 			if (nstr[0]) {
-				for (j = 0;
-				    ostr[j] && nstr[j] && (ostr[j] == nstr[j]);
-				    j++);
-					(void) putc(j & 0xff, pwp->dfp);
+				for (j = 0; ostr[j] && nstr[j] &&
+				    (ostr[j] == nstr[j]); j++)
+					;
+				(void) putc(j & 0xff, pwp->dfp);
 				(void) fputs(nstr + j, pwp->dfp);
 			}
 			(void) putc(0, pwp->dfp);
@@ -232,17 +230,17 @@
 }
 
 char *
-GetPW(PWDICT *pwp, int32 number)
+GetPW(PWDICT *pwp, uint32_t number)
 {
-	int32 datum;
+	uint32_t datum;
 	register int i;
 	register char *ostr;
 	register char *nstr;
 	register char *bptr;
 	char buffer[NUMWORDS * MAXWORDLEN];
 	static char data[NUMWORDS][MAXWORDLEN];
-	static int32 prevblock = 0xffffffff;
-	int32 thisblock;
+	static uint32_t prevblock = 0xffffffff;
+	uint32_t thisblock;
 
 	thisblock = number / NUMWORDS;
 
@@ -251,7 +249,7 @@
 	}
 
 	if (fseek(pwp->ifp, sizeof (struct pi_header) +
-	    (thisblock * sizeof (int32)), 0)) {
+	    (thisblock * sizeof (uint32_t)), 0)) {
 		return (NULL);
 	}
 
@@ -271,7 +269,8 @@
 
 	bptr = buffer;
 
-	for (ostr = data[0]; *(ostr++) = *(bptr++); /* nothing */);
+	for (ostr = data[0]; *(ostr++) = *(bptr++); /* nothing */)
+		;
 
 	ostr = data[0];
 
@@ -279,7 +278,8 @@
 		nstr = data[i];
 		(void) strcpy(nstr, ostr);
 		ostr = nstr + *(bptr++);
-		while (*(ostr++) = *(bptr++));
+		while (*(ostr++) = *(bptr++))
+			;
 
 		ostr = nstr;
 	}
@@ -287,7 +287,7 @@
 	return (data[number % NUMWORDS]);
 }
 
-int32
+uint32_t
 FindPW(PWDICT *pwp, char *string)
 {
 	int lwm;
--- a/usr/src/lib/pam_modules/unix_cred/unix_cred.c	Fri Jun 05 10:27:16 2009 -0400
+++ b/usr/src/lib/pam_modules/unix_cred/unix_cred.c	Fri Jun 05 10:28:40 2009 -0400
@@ -596,18 +596,23 @@
 		}
 		if (!priv_issubset(lim, tset))
 			priv_intersect(tset, lim);
-		/*
-		 * In order not to suprise certain applications, we
-		 * need to retain privilege awareness and thus we must
-		 * also set P and E.
-		 */
-		if (setppriv(PRIV_SET, PRIV_LIMIT, lim) != 0 ||
-		    setppriv(PRIV_SET, PRIV_PERMITTED, lim) != 0) {
+		if (setppriv(PRIV_SET, PRIV_LIMIT, lim) != 0) {
 			syslog(LOG_AUTH | LOG_ERR,
 			    "pam_setcred: setppriv(limitpriv) failed: %m");
 			ret = PAM_CRED_ERR;
+			goto out;
 		}
+		/*
+		 * In order not to surprise certain applications, we
+		 * need to get rid of privilege awareness and thus we must
+		 * set this flag which will cause a reset on set*uid().
+		 */
+		(void) setpflags(PRIV_AWARE_RESET, 1);
 	}
+	/*
+	 * This may fail but we do not care as this will be reset later
+	 * when the uids are set to their final values.
+	 */
 	(void) setpflags(PRIV_AWARE, 0);
 
 out:
--- a/usr/src/lib/pkcs11/pkcs11_kernel/common/kernelDigest.c	Fri Jun 05 10:27:16 2009 -0400
+++ b/usr/src/lib/pkcs11/pkcs11_kernel/common/kernelDigest.c	Fri Jun 05 10:28:40 2009 -0400
@@ -20,12 +20,10 @@
  */
 
 /*
- * Copyright 2007 Sun Microsystems, Inc.  All rights reserved.
+ * Copyright 2009 Sun Microsystems, Inc.  All rights reserved.
  * Use is subject to license terms.
  */
 
-#pragma ident	"%Z%%M%	%I%	%E% SMI"
-
 #include <pthread.h>
 #include <errno.h>
 #include <sys/crypto/ioctl.h>
@@ -40,7 +38,7 @@
 {
 	CK_RV rv;
 	kernel_session_t *session_p;
-	boolean_t ses_lock_held = B_TRUE;
+	boolean_t ses_lock_held = B_FALSE;
 	crypto_digest_init_t digest_init;
 	crypto_mech_type_t k_mech_type;
 	int r;
@@ -68,6 +66,7 @@
 
 	/* Acquire the session lock */
 	(void) pthread_mutex_lock(&session_p->session_mutex);
+	ses_lock_held = B_TRUE;
 
 	/*
 	 * This active flag will remain ON until application calls either
@@ -88,6 +87,7 @@
 
 	digest_init.di_session = session_p->k_session;
 	(void) pthread_mutex_unlock(&session_p->session_mutex);
+	ses_lock_held = B_FALSE;
 	digest_init.di_mech.cm_type = k_mech_type;
 	digest_init.di_mech.cm_param = pMechanism->pParameter;
 
@@ -112,6 +112,7 @@
 
 	if (rv != CKR_OK) {
 		(void) pthread_mutex_lock(&session_p->session_mutex);
+		ses_lock_held = B_TRUE;
 		session_p->digest.flags &= ~CRYPTO_OPERATION_ACTIVE;
 		/*
 		 * Decrement the session reference count.
@@ -126,7 +127,6 @@
 	 * Decrement the session reference count.
 	 * We do not hold the session lock.
 	 */
-	ses_lock_held = B_FALSE;
 	REFRELE(session_p, ses_lock_held);
 	return (rv);
 }
@@ -143,7 +143,7 @@
 {
 	CK_RV rv;
 	kernel_session_t *session_p;
-	boolean_t ses_lock_held = B_TRUE;
+	boolean_t ses_lock_held = B_FALSE;
 	crypto_digest_t digest;
 	int r;
 
@@ -165,6 +165,7 @@
 
 	/* Acquire the session lock */
 	(void) pthread_mutex_lock(&session_p->session_mutex);
+	ses_lock_held = B_TRUE;
 
 	/* Application must call C_DigestInit before calling C_Digest */
 	if (!(session_p->digest.flags & CRYPTO_OPERATION_ACTIVE)) {
@@ -201,14 +202,17 @@
 		CK_MECHANISM_PTR pMechanism;
 
 		opp = &(session_p->digest);
-		if (opp->context == NULL)
+		if (opp->context == NULL) {
+			REFRELE(session_p, ses_lock_held);
 			return (CKR_ARGUMENTS_BAD);
+		}
 		pMechanism = &(opp->mech);
 
 		if ((ulDataLen < SLOT_THRESHOLD(session_p)) ||
 		    (ulDataLen > SLOT_MAX_INDATA_LEN(session_p))) {
 			session_p->digest.flags |= CRYPTO_EMULATE_USING_SW;
 			(void) pthread_mutex_unlock(&session_p->session_mutex);
+			ses_lock_held = B_FALSE;
 
 			rv = do_soft_digest(get_spp(opp), pMechanism,
 			    pData, ulDataLen, pDigest, pulDigestLen,
@@ -218,15 +222,19 @@
 		    CRYPTO_EMULATE_INIT_DONE)) {
 			session_p->digest.flags |= CRYPTO_EMULATE_INIT_DONE;
 			(void) pthread_mutex_unlock(&session_p->session_mutex);
+			ses_lock_held = B_FALSE;
+
 			rv = common_digest_init(hSession, pMechanism, B_FALSE);
 			if (rv != CKR_OK)
 				goto clean_exit;
 			(void) pthread_mutex_lock(&session_p->session_mutex);
+			ses_lock_held = B_TRUE;
 		}
 	}
 
 	digest.cd_session = session_p->k_session;
 	(void) pthread_mutex_unlock(&session_p->session_mutex);
+	ses_lock_held = B_FALSE;
 	digest.cd_datalen =  ulDataLen;
 	digest.cd_databuf = (char *)pData;
 	digest.cd_digestbuf = (char *)pDigest;
@@ -257,7 +265,6 @@
 		 * Decrement the session reference count.
 		 * We do not hold the session lock.
 		 */
-		ses_lock_held = B_FALSE;
 		REFRELE(session_p, ses_lock_held);
 		return (rv);
 	}
@@ -269,6 +276,7 @@
 	 * digest operation.
 	 */
 	(void) pthread_mutex_lock(&session_p->session_mutex);
+	ses_lock_held = B_TRUE;
 
 	REINIT_OPBUF(&session_p->digest);
 	session_p->digest.flags = 0;
@@ -290,7 +298,7 @@
 
 	CK_RV rv;
 	kernel_session_t *session_p;
-	boolean_t ses_lock_held = B_TRUE;
+	boolean_t ses_lock_held = B_FALSE;
 	crypto_digest_update_t digest_update;
 	int r;
 
@@ -312,6 +320,7 @@
 
 	/* Acquire the session lock */
 	(void) pthread_mutex_lock(&session_p->session_mutex);
+	ses_lock_held = B_TRUE;
 
 	/*
 	 * Application must call C_DigestInit before calling
@@ -332,12 +341,14 @@
 
 	if (session_p->digest.flags & CRYPTO_EMULATE) {
 		(void) pthread_mutex_unlock(&session_p->session_mutex);
+		ses_lock_held = B_FALSE;
 		rv = emulate_update(session_p, pPart, ulPartLen, OP_DIGEST);
 		goto done;
 	}
 
 	digest_update.du_session = session_p->k_session;
 	(void) pthread_mutex_unlock(&session_p->session_mutex);
+	ses_lock_held = B_FALSE;
 	digest_update.du_datalen =  ulPartLen;
 	digest_update.du_databuf = (char *)pPart;
 
@@ -358,7 +369,6 @@
 		 * Decrement the session reference count.
 		 * We do not hold the session lock.
 		 */
-		ses_lock_held = B_FALSE;
 		REFRELE(session_p, ses_lock_held);
 		return (CKR_OK);
 	}
@@ -369,6 +379,7 @@
 	 * operation by resetting the active and update flags.
 	 */
 	(void) pthread_mutex_lock(&session_p->session_mutex);
+	ses_lock_held = B_TRUE;
 	REINIT_OPBUF(&session_p->digest);
 	session_p->digest.flags = 0;
 
@@ -390,7 +401,7 @@
 	CK_RV		rv;
 	kernel_session_t	*session_p;
 	kernel_object_t	*key_p;
-	boolean_t ses_lock_held = B_TRUE;
+	boolean_t ses_lock_held = B_FALSE;
 	CK_BYTE_PTR	pPart;
 	CK_ULONG	ulPartLen;
 	crypto_digest_key_t digest_key;
@@ -412,6 +423,7 @@
 	HANDLE2OBJECT(hKey, key_p, rv);
 	if (rv != CKR_OK) {
 		(void) pthread_mutex_lock(&session_p->session_mutex);
+		ses_lock_held = B_TRUE;
 		REINIT_OPBUF(&session_p->digest);
 		session_p->digest.flags = 0;
 		REFRELE(session_p, ses_lock_held);
@@ -429,6 +441,8 @@
 	 * C_DigestKey.
 	 */
 	(void) pthread_mutex_lock(&session_p->session_mutex);
+	ses_lock_held = B_TRUE;
+
 	if (!(session_p->digest.flags & CRYPTO_OPERATION_ACTIVE)) {
 		/*
 		 * Decrement the session reference count.
@@ -452,6 +466,7 @@
 		digest_key.dk_session = session_p->k_session;
 	}
 	(void) pthread_mutex_unlock(&session_p->session_mutex);
+	ses_lock_held = B_FALSE;
 
 	if (!key_p->is_lib_obj) {
 		if (session_p->digest.flags & CRYPTO_EMULATE) {
@@ -485,13 +500,16 @@
 		}
 
 		(void) pthread_mutex_lock(&session_p->session_mutex);
+		ses_lock_held = B_TRUE;
 		if (session_p->digest.flags & CRYPTO_EMULATE) {
 			(void) pthread_mutex_unlock(&session_p->session_mutex);
+			ses_lock_held = B_FALSE;
 			rv = emulate_update(session_p, pPart,
 			    ulPartLen, OP_DIGEST);
 			goto done;
 		}
 		(void) pthread_mutex_unlock(&session_p->session_mutex);
+		ses_lock_held = B_FALSE;
 
 		digest_update.du_datalen = ulPartLen;
 		digest_update.du_databuf = (char *)pPart;
@@ -516,7 +534,6 @@
 		 * We do not hold the session lock.
 		 */
 		OBJ_REFRELE(key_p);
-		ses_lock_held = B_FALSE;
 		REFRELE(session_p, ses_lock_held);
 		return (CKR_OK);
 	}
@@ -528,6 +545,7 @@
 	 * operation by resetting the active and update flags.
 	 */
 	(void) pthread_mutex_lock(&session_p->session_mutex);
+	ses_lock_held = B_TRUE;
 	REINIT_OPBUF(&session_p->digest);
 	session_p->digest.flags = 0;
 
@@ -548,7 +566,7 @@
 
 	CK_RV rv;
 	kernel_session_t *session_p;
-	boolean_t ses_lock_held = B_TRUE;
+	boolean_t ses_lock_held = B_FALSE;
 	crypto_digest_final_t digest_final;
 	int r;
 
@@ -570,6 +588,7 @@
 
 	/* Acquire the session lock */
 	(void) pthread_mutex_lock(&session_p->session_mutex);
+	ses_lock_held = B_TRUE;
 
 	/*
 	 * Application must call C_DigestInit before calling
@@ -589,6 +608,7 @@
 	if (session_p->digest.flags & CRYPTO_EMULATE_USING_SW) {
 		if (session_p->digest.flags & CRYPTO_EMULATE_UPDATE_DONE) {
 			(void) pthread_mutex_unlock(&session_p->session_mutex);
+			ses_lock_held = B_FALSE;
 			rv = do_soft_digest(get_spp(&session_p->digest),
 			    NULL, NULL, NULL, pDigest, pulDigestLen, OP_FINAL);
 		} else {
@@ -599,6 +619,7 @@
 			 */
 			digest_buf_t *bufp = session_p->digest.context;
 			(void) pthread_mutex_unlock(&session_p->session_mutex);
+			ses_lock_held = B_FALSE;
 			if (bufp == NULL || bufp->buf == NULL) {
 				rv = CKR_ARGUMENTS_BAD;
 				goto clean_exit;
@@ -628,6 +649,7 @@
 
 	digest_final.df_session = session_p->k_session;
 	(void) pthread_mutex_unlock(&session_p->session_mutex);
+	ses_lock_held = B_FALSE;
 	digest_final.df_digestlen = *pulDigestLen;
 	digest_final.df_digestbuf = (char *)pDigest;
 
@@ -656,7 +678,6 @@
 		 * Decrement the session reference count.
 		 * We do not hold the session lock.
 		 */
-		ses_lock_held = B_FALSE;
 		REFRELE(session_p, ses_lock_held);
 		return (rv);
 	}
@@ -664,6 +685,7 @@
 clean_exit:
 	/* Terminates the active digest operation */
 	(void) pthread_mutex_lock(&session_p->session_mutex);
+	ses_lock_held = B_TRUE;
 	REINIT_OPBUF(&session_p->digest);
 	session_p->digest.flags = 0;
 
--- a/usr/src/lib/pkcs11/pkcs11_kernel/common/kernelSign.c	Fri Jun 05 10:27:16 2009 -0400
+++ b/usr/src/lib/pkcs11/pkcs11_kernel/common/kernelSign.c	Fri Jun 05 10:28:40 2009 -0400
@@ -20,12 +20,10 @@
  */
 
 /*
- * Copyright 2007 Sun Microsystems, Inc.  All rights reserved.
+ * Copyright 2009 Sun Microsystems, Inc.  All rights reserved.
  * Use is subject to license terms.
  */
 
-#pragma ident	"%Z%%M%	%I%	%E% SMI"
-
 #include <errno.h>
 #include <security/cryptoki.h>
 #include <sys/crypto/ioctl.h>
@@ -226,6 +224,7 @@
 		    (ulDataLen > SLOT_MAX_INDATA_LEN(session_p))) {
 			session_p->sign.flags |= CRYPTO_EMULATE_USING_SW;
 			(void) pthread_mutex_unlock(&session_p->session_mutex);
+			ses_lock_held = B_FALSE;
 
 			rv = do_soft_hmac_sign(get_spp(&session_p->sign),
 			    pData, ulDataLen,
@@ -278,10 +277,10 @@
 	 * sign operation.
 	 */
 	(void) pthread_mutex_lock(&session_p->session_mutex);
+	ses_lock_held = B_TRUE;
 
 	REINIT_OPBUF(&session_p->sign);
 	session_p->sign.flags = 0;
-	ses_lock_held = B_TRUE;
 	REFRELE(session_p, ses_lock_held);
 
 	return (rv);
@@ -328,6 +327,7 @@
 
 	if (session_p->sign.flags & CRYPTO_EMULATE) {
 		(void) pthread_mutex_unlock(&session_p->session_mutex);
+		ses_lock_held = B_FALSE;
 		rv = emulate_update(session_p, pPart, ulPartLen, OP_SIGN);
 		goto done;
 	}
@@ -361,9 +361,9 @@
 	 * operation by resetting the active and update flags.
 	 */
 	(void) pthread_mutex_lock(&session_p->session_mutex);
+	ses_lock_held = B_TRUE;
 	REINIT_OPBUF(&session_p->sign);
 	session_p->sign.flags = 0;
-	ses_lock_held = B_TRUE;
 	REFRELE(session_p, ses_lock_held);
 
 	return (rv);
@@ -410,6 +410,7 @@
 	if (session_p->sign.flags & CRYPTO_EMULATE_USING_SW) {
 		if (session_p->sign.flags & CRYPTO_EMULATE_UPDATE_DONE) {
 			(void) pthread_mutex_unlock(&session_p->session_mutex);
+			ses_lock_held = B_FALSE;
 			rv = do_soft_hmac_sign(get_spp(&session_p->sign),
 			    NULL, 0, pSignature, pulSignatureLen, OP_FINAL);
 		} else {
@@ -420,6 +421,7 @@
 			 */
 			digest_buf_t *bufp = session_p->sign.context;
 			(void) pthread_mutex_unlock(&session_p->session_mutex);
+			ses_lock_held = B_FALSE;
 			if (bufp == NULL || bufp->buf == NULL) {
 				rv = CKR_ARGUMENTS_BAD;
 				goto clean_exit;
@@ -483,9 +485,9 @@
 clean_exit:
 	/* Terminates the active sign operation */
 	(void) pthread_mutex_lock(&session_p->session_mutex);
+	ses_lock_held = B_TRUE;
 	REINIT_OPBUF(&session_p->sign);
 	session_p->sign.flags = 0;
-	ses_lock_held = B_TRUE;
 	REFRELE(session_p, ses_lock_held);
 
 	return (rv);
@@ -673,8 +675,8 @@
 	 * sign operation.
 	 */
 	(void) pthread_mutex_lock(&session_p->session_mutex);
+	ses_lock_held = B_TRUE;
 	session_p->sign.flags = 0;
-	ses_lock_held = B_TRUE;
 	REFRELE(session_p, ses_lock_held);
 
 	return (rv);
--- a/usr/src/lib/pkcs11/pkcs11_kernel/common/kernelVerify.c	Fri Jun 05 10:27:16 2009 -0400
+++ b/usr/src/lib/pkcs11/pkcs11_kernel/common/kernelVerify.c	Fri Jun 05 10:28:40 2009 -0400
@@ -20,12 +20,10 @@
  */
 
 /*
- * Copyright 2007 Sun Microsystems, Inc.  All rights reserved.
+ * Copyright 2009 Sun Microsystems, Inc.  All rights reserved.
  * Use is subject to license terms.
  */
 
-#pragma ident	"%Z%%M%	%I%	%E% SMI"
-
 #include <stdlib.h>
 #include <errno.h>
 #include <sys/crypto/ioctl.h>
@@ -225,6 +223,7 @@
 		    (ulDataLen > SLOT_MAX_INDATA_LEN(session_p))) {
 			session_p->verify.flags |= CRYPTO_EMULATE_USING_SW;
 			(void) pthread_mutex_unlock(&session_p->session_mutex);
+			ses_lock_held = B_FALSE;
 
 			rv = do_soft_hmac_verify(get_spp(&session_p->verify),
 			    pData, ulDataLen,
@@ -260,10 +259,10 @@
 	 * verify operation.
 	 */
 	(void) pthread_mutex_lock(&session_p->session_mutex);
+	ses_lock_held = B_TRUE;
 
 	REINIT_OPBUF(&session_p->verify);
 	session_p->verify.flags = 0;
-	ses_lock_held = B_TRUE;
 	REFRELE(session_p, ses_lock_held);
 
 	return (rv);
@@ -310,6 +309,7 @@
 
 	if (session_p->verify.flags & CRYPTO_EMULATE) {
 		(void) pthread_mutex_unlock(&session_p->session_mutex);
+		ses_lock_held = B_FALSE;
 		rv = emulate_update(session_p, pPart, ulPartLen, OP_VERIFY);
 		goto done;
 	}
@@ -344,9 +344,9 @@
 	 * operation by resetting the active and update flags.
 	 */
 	(void) pthread_mutex_lock(&session_p->session_mutex);
+	ses_lock_held = B_TRUE;
 	REINIT_OPBUF(&session_p->verify);
 	session_p->verify.flags = 0;
-	ses_lock_held = B_TRUE;
 	REFRELE(session_p, ses_lock_held);
 
 	return (rv);
@@ -388,6 +388,7 @@
 	if (session_p->verify.flags & CRYPTO_EMULATE_USING_SW) {
 		if (session_p->verify.flags & CRYPTO_EMULATE_UPDATE_DONE) {
 			(void) pthread_mutex_unlock(&session_p->session_mutex);
+			ses_lock_held = B_FALSE;
 			rv = do_soft_hmac_verify(get_spp(&session_p->verify),
 			    NULL, 0, pSignature, ulSignatureLen,
 			    OP_FINAL);
@@ -397,6 +398,8 @@
 			 * C_VerifyFinal() call took the C_Verify() path as
 			 * it never returns CKR_BUFFER_TOO_SMALL.
 			 */
+			(void) pthread_mutex_unlock(&session_p->session_mutex);
+			ses_lock_held = B_FALSE;
 			rv = CKR_ARGUMENTS_BAD;
 		}
 		goto clean_exit;
@@ -438,9 +441,9 @@
 clean_exit:
 	/* Always terminate the active verify operation */
 	(void) pthread_mutex_lock(&session_p->session_mutex);
+	ses_lock_held = B_TRUE;
 	REINIT_OPBUF(&session_p->verify);
 	session_p->verify.flags = 0;
-	ses_lock_held = B_TRUE;
 	REFRELE(session_p, ses_lock_held);
 
 	return (rv);
--- a/usr/src/pkgdefs/Makefile	Fri Jun 05 10:27:16 2009 -0400
+++ b/usr/src/pkgdefs/Makefile	Fri Jun 05 10:28:40 2009 -0400
@@ -287,6 +287,7 @@
 	SUNWimac \
 	SUNWimar \
 	SUNWimacr \
+	SUNWinstallint \
 	SUNWintgige \
 	SUNWiotu  \
 	SUNWioth  \
@@ -378,12 +379,14 @@
 	SUNWpcu	\
 	SUNWpcwl \
 	SUNWpd    \
-	SUNWpolkit \
 	SUNWperl584core \
 	SUNWperl584usr \
 	SUNWpiclh \
 	SUNWpiclu \
 	SUNWpiclr \
+	SUNWpkgcmdsr \
+	SUNWpkgcmdsu \
+	SUNWpolkit \
 	SUNWpool \
 	SUNWpoold \
 	SUNWpoolr \
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/usr/src/pkgdefs/SUNWinstallint/Makefile	Fri Jun 05 10:28:40 2009 -0400
@@ -0,0 +1,38 @@
+#
+# 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
+# or http://www.opensolaris.org/os/licensing.
+# 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 2009 Sun Microsystems, Inc.  All rights reserved.
+# Use is subject to license terms.
+#
+#
+
+include $(SRC)/pkgdefs/Makefile.com
+
+DATAFILES += depend
+
+.KEEP_STATE:
+
+all: $(FILES)
+
+install: all pkg
+
+include $(SRC)/pkgdefs/Makefile.targ
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/usr/src/pkgdefs/SUNWinstallint/pkginfo.tmpl	Fri Jun 05 10:28:40 2009 -0400
@@ -0,0 +1,47 @@
+#
+# 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
+# or http://www.opensolaris.org/os/licensing.
+# 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.
+#
+# This required package information file describes characteristics of the
+# package, such as package abbreviation, full package name, package version,
+# and package architecture.
+#
+PKG="SUNWinstallint"
+NAME="Solaris Install Internal Files"
+ARCH="ISA"
+VERSION="ONVERS,REV=0.0.0"
+SUNW_PRODNAME="SunOS"
+SUNW_PRODVERS="RELEASE/VERSION"
+SUNW_PKGTYPE="usr"
+MAXINST="1000"
+CATEGORY="system"
+DESC="Solaris Install internal files"
+VENDOR="Sun Microsystems, Inc."
+HOTLINE="Please contact your local service provider"
+EMAIL=""
+CLASSES="none"
+BASEDIR=/
+SUNW_PKGVERS="1.0"
+SUNW_PKG_ALLZONES=false
+SUNW_PKG_THISZONE=false
+SUNW_PKG_HOLLOW=false
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/usr/src/pkgdefs/SUNWinstallint/prototype_com	Fri Jun 05 10:28:40 2009 -0400
@@ -0,0 +1,45 @@
+#
+# 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
+# or http://www.opensolaris.org/os/licensing.
+# 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 2009 Sun Microsystems, Inc.  All rights reserved.
+# Use is subject to license terms.
+#
+
+#
+# This required package information file contains a list of package contents.
+# The 'pkgmk' command uses this file to identify the contents of a package
+# and their location on the development machine when building the package.
+# Can be created via a text editor or through use of the 'pkgproto' command.
+
+# packaging files
+i pkginfo
+i copyright
+i depend
+
+#
+# source locations relative to the prototype file
+#
+# SUNWinstallint
+#
+d none usr 755 root sys
+d none usr/lib 755 root bin
+s none usr/lib/libinstzones.so=./libinstzones.so.1
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/usr/src/pkgdefs/SUNWinstallint/prototype_i386	Fri Jun 05 10:28:40 2009 -0400
@@ -0,0 +1,48 @@
+#
+# 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
+# or http://www.opensolaris.org/os/licensing.
+# 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 2009 Sun Microsystems, Inc.  All rights reserved.
+# Use is subject to license terms.
+#
+#
+
+#
+# This required package information file contains a list of package contents.
+# The 'pkgmk' command uses this file to identify the contents of a package
+# and their location on the development machine when building the package.
+# Can be created via a text editor or through use of the 'pkgproto' command.
+
+#!search <pathname pathname ...>        # where to find pkg objects
+#!include <filename>                    # include another 'prototype' file
+#!default <mode> <owner> <group>        # default used if not specified on entry#!<param>=<value>                       # puts parameter in pkg environment
+
+#
+# Include ISA independent files (prototype_com)
+#
+!include prototype_com
+#
+#
+#
+# List files which are i386 specific here
+#
+# source locations relative to the prototype file
+#
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/usr/src/pkgdefs/SUNWinstallint/prototype_sparc	Fri Jun 05 10:28:40 2009 -0400
@@ -0,0 +1,50 @@
+#
+# 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
+# or http://www.opensolaris.org/os/licensing.
+# 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 2009 Sun Microsystems, Inc.  All rights reserved.
+# Use is subject to license terms.
+#
+#
+
+
+#
+# This required package information file contains a list of package contents.
+# The 'pkgmk' command uses this file to identify the contents of a package
+# and their location on the development machine when building the package.
+# Can be created via a text editor or through use of the 'pkgproto' command.
+
+#!search <pathname pathname ...>        # where to find pkg objects
+#!include <filename>                    # include another 'prototype' file
+#!default <mode> <owner> <group>        # default used if not specified on entry#!<param>=<value>                       # puts parameter in pkg environment
+
+#
+# Include ISA independent files (prototype_com)
+#
+!include prototype_com
+#
+#
+#
+# List files which are SPARC specific here
+#
+# source locations relative to the prototype file
+#
+#
--- a/usr/src/pkgdefs/SUNWlsimega/postinstall	Fri Jun 05 10:27:16 2009 -0400
+++ b/usr/src/pkgdefs/SUNWlsimega/postinstall	Fri Jun 05 10:28:40 2009 -0400
@@ -1,9 +1,8 @@
 #!/sbin/sh
 #
-# Copyright 2008 Sun Microsystems, Inc.  All rights reserved.
+# Copyright 2009 Sun Microsystems, Inc.  All rights reserved.
 # Use is subject to license terms.
 #
-# ident	"%Z%%M%	%I%	%E% SMI"
 #
 
 # Function: check_add_drv()
@@ -113,4 +112,4 @@
 # "amr" driver contains entry "pci1000,532"
 # "ncrs" contains entry "pci1000,2"
 
-check_add_drv -i '"pci1028,13" "pci1000,407" "pci1000,407.1000.532" "pci1000,408" "pci1000,408.1000.2" "pci1000,1960"' -b "$BASEDIR" -c scsi lsimega
+check_add_drv -i '"pci1028,13" "pci1000,407" "pci1000,407.1000.532" "pci1000,408" "pci1000,408.1000.2" "pci1000,409" "pci1000,1960"' -b "$BASEDIR" -c scsi lsimega
--- a/usr/src/pkgdefs/SUNWmdb/prototype_i386	Fri Jun 05 10:27:16 2009 -0400
+++ b/usr/src/pkgdefs/SUNWmdb/prototype_i386	Fri Jun 05 10:28:40 2009 -0400
@@ -75,6 +75,7 @@
 f none usr/lib/mdb/kvm/amd64/mdb_kb.so 555 root sys
 f none usr/lib/mdb/kvm/amd64/mdb_ks.so 555 root sys
 f none usr/lib/mdb/kvm/amd64/mpt.so 555 root sys
+f none usr/lib/mdb/kvm/amd64/mr_sas.so 555 root sys
 f none usr/lib/mdb/kvm/amd64/nca.so 555 root sys
 f none usr/lib/mdb/kvm/amd64/nfs.so 555 root sys
 f none usr/lib/mdb/kvm/amd64/ptm.so 555 root sys
@@ -108,6 +109,7 @@
 f none usr/lib/mdb/kvm/mdb_kb.so 555 root sys
 f none usr/lib/mdb/kvm/mdb_ks.so 555 root sys
 f none usr/lib/mdb/kvm/mpt.so 555 root sys
+f none usr/lib/mdb/kvm/mr_sas.so 555 root sys
 f none usr/lib/mdb/kvm/nca.so 555 root sys
 f none usr/lib/mdb/kvm/nfs.so 555 root sys
 f none usr/lib/mdb/kvm/ptm.so 555 root sys
--- a/usr/src/pkgdefs/SUNWmdb/prototype_sparc	Fri Jun 05 10:27:16 2009 -0400
+++ b/usr/src/pkgdefs/SUNWmdb/prototype_sparc	Fri Jun 05 10:28:40 2009 -0400
@@ -55,6 +55,7 @@
 f none usr/lib/mdb/kvm/sparcv9/md.so 555 root sys
 f none usr/lib/mdb/kvm/sparcv9/mdb_ks.so 555 root sys
 f none usr/lib/mdb/kvm/sparcv9/mpt.so 555 root sys
+f none usr/lib/mdb/kvm/sparcv9/mr_sas.so 555 root sys
 f none usr/lib/mdb/kvm/sparcv9/nca.so 555 root sys
 f none usr/lib/mdb/kvm/sparcv9/nfs.so 555 root sys
 s none usr/lib/mdb/kvm/sparcv9/pcisch.so=intr.so
--- a/usr/src/pkgdefs/SUNWmdbr/prototype_i386	Fri Jun 05 10:27:16 2009 -0400
+++ b/usr/src/pkgdefs/SUNWmdbr/prototype_i386	Fri Jun 05 10:28:40 2009 -0400
@@ -43,6 +43,7 @@
 f none kernel/kmdb/amd64/md 555 root sys
 f none kernel/kmdb/amd64/mdb_ds 555 root sys
 f none kernel/kmdb/amd64/mpt 555 root sys
+f none kernel/kmdb/amd64/mr_sas 555 root sys
 f none kernel/kmdb/amd64/nca 555 root sys
 f none kernel/kmdb/amd64/neti 555 root sys
 f none kernel/kmdb/amd64/nfs 555 root sys
@@ -75,6 +76,7 @@
 f none kernel/kmdb/md 555 root sys
 f none kernel/kmdb/mdb_ds 555 root sys
 f none kernel/kmdb/mpt 555 root sys
+f none kernel/kmdb/mr_sas 555 root sys
 f none kernel/kmdb/nca 555 root sys
 f none kernel/kmdb/neti 555 root sys
 f none kernel/kmdb/nfs 555 root sys
--- a/usr/src/pkgdefs/SUNWmdbr/prototype_sparc	Fri Jun 05 10:27:16 2009 -0400
+++ b/usr/src/pkgdefs/SUNWmdbr/prototype_sparc	Fri Jun 05 10:28:40 2009 -0400
@@ -43,6 +43,7 @@
 f none kernel/kmdb/sparcv9/md 555 root sys
 f none kernel/kmdb/sparcv9/mdb_ds 555 root sys
 f none kernel/kmdb/sparcv9/mpt 555 root sys
+f none kernel/kmdb/sparcv9/mr_sas 555 root sys
 f none kernel/kmdb/sparcv9/nca 555 root sys
 f none kernel/kmdb/sparcv9/neti 555 root sys
 f none kernel/kmdb/sparcv9/nfs 555 root sys
--- a/usr/src/pkgdefs/SUNWmrsas/postinstall	Fri Jun 05 10:27:16 2009 -0400
+++ b/usr/src/pkgdefs/SUNWmrsas/postinstall	Fri Jun 05 10:28:40 2009 -0400
@@ -124,7 +124,13 @@
 
 # We should all use main PCI ID entries.
 
-check_add_drv -i \
-	'"pciex1000,78"
-	"pciex1000,79"' \
-	-c scsi-self-identifying mr_sas
+ARCH=`uname -p`
+if [ ${ARCH} = "i386" ]; then
+    DRVALIAS='"pciex1000,78" "pciex1000,79"'
+fi
+
+if [ ${ARCH} = "sparc" ]; then
+   DRVALIAS='"pci1000,78" "pci1000,79" "pciex1000,78" "pciex1000,79"'
+fi
+
+check_add_drv -i "$DRVALIAS" -c scsi-self-identifying mr_sas
--- a/usr/src/pkgdefs/SUNWmrsas/prototype_com	Fri Jun 05 10:27:16 2009 -0400
+++ b/usr/src/pkgdefs/SUNWmrsas/prototype_com	Fri Jun 05 10:28:40 2009 -0400
@@ -18,7 +18,7 @@
 #
 # CDDL HEADER END
 #
-# Copyright 2008 Sun Microsystems, Inc.  All rights reserved.
+# Copyright 2009 Sun Microsystems, Inc.  All rights reserved.
 # Use is subject to license terms.
 #
 
@@ -37,6 +37,8 @@
 i copyright
 i depend
 i pkginfo
+i postinstall
+i postremove
 
 #
 # source locations relative to the prototype file
@@ -45,3 +47,4 @@
 #
 d none kernel 0755 root sys
 d none kernel/drv 0755 root sys
+f none kernel/drv/mr_sas.conf	0644	root	sys
--- a/usr/src/pkgdefs/SUNWmrsas/prototype_i386	Fri Jun 05 10:27:16 2009 -0400
+++ b/usr/src/pkgdefs/SUNWmrsas/prototype_i386	Fri Jun 05 10:28:40 2009 -0400
@@ -39,16 +39,10 @@
 !include prototype_com
 
 #
-# List files which are i386 specific here
-i postinstall
-i postremove
-
-#
 # source locations relative to the prototype file
 #
 # SUNWmrsas
 #
 f none kernel/drv/mr_sas	0755	root	sys
-f none kernel/drv/mr_sas.conf   0644    root    sys
 d none kernel/drv/amd64		0755	root	sys
 f none kernel/drv/amd64/mr_sas	0755	root 	sys
--- a/usr/src/pkgdefs/SUNWmrsas/prototype_sparc	Fri Jun 05 10:27:16 2009 -0400
+++ b/usr/src/pkgdefs/SUNWmrsas/prototype_sparc	Fri Jun 05 10:28:40 2009 -0400
@@ -46,3 +46,4 @@
 # SUNWmrsas
 #
 d none kernel/drv/sparcv9	0755	root	sys
+f none kernel/drv/sparcv9/mr_sas 0755	root	sys
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/usr/src/pkgdefs/SUNWpkgcmdsr/Makefile	Fri Jun 05 10:28:40 2009 -0400
@@ -0,0 +1,38 @@
+#
+# 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
+# or http://www.opensolaris.org/os/licensing.
+# 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 2009 Sun Microsystems, Inc.  All rights reserved.
+# Use is subject to license terms.
+#
+#
+
+include $(SRC)/pkgdefs/Makefile.com
+
+DATAFILES += depend
+
+.KEEP_STATE:
+
+all: $(FILES)
+
+install: all pkg
+
+include $(SRC)/pkgdefs/Makefile.targ
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/usr/src/pkgdefs/SUNWpkgcmdsr/pkginfo.tmpl	Fri Jun 05 10:28:40 2009 -0400
@@ -0,0 +1,47 @@
+#
+# 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
+# or http://www.opensolaris.org/os/licensing.
+# 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.
+#
+# This required package information file describes characteristics of the
+# package, such as package abbreviation, full package name, package version,
+# and package architecture.
+#
+PKG=SUNWpkgcmdsr
+NAME=SVr4 package commands (root)
+ARCH="ISA"
+CATEGORY="system"
+BASEDIR=/
+SUNW_PKGVERS="1.0"
+SUNW_PKGTYPE="root"
+CLASSES="none"
+DESC=Solaris System V release 4 Package Commands (root)
+SUNW_PRODNAME="SunOS"
+SUNW_PRODVERS="RELEASE/VERSION"
+VERSION="ONVERS,REV=0.0.0"
+VENDOR="Sun Microsystems, Inc."
+HOTLINE="Please contact your local service provider"
+EMAIL=""
+MAXINST=1000
+SUNW_PKG_ALLZONES=false
+SUNW_PKG_THISZONE=false
+SUNW_PKG_HOLLOW=false
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/usr/src/pkgdefs/SUNWpkgcmdsr/prototype_com	Fri Jun 05 10:28:40 2009 -0400
@@ -0,0 +1,52 @@
+#
+# 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
+# or http://www.opensolaris.org/os/licensing.
+# 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 2009 Sun Microsystems, Inc.  All rights reserved.
+# Use is subject to license terms.
+#
+
+#
+# This required package information file contains a list of package contents.
+# The 'pkgmk' command uses this file to identify the contents of a package
+# and their location on the development machine when building the package.
+# Can be created via a text editor or through use of the 'pkgproto' command.
+
+# packaging files
+i pkginfo
+i copyright
+i depend
+
+#
+# source locations relative to the prototype file
+#
+# SUNWpkgcmdsr
+#
+d none var 755 root sys
+d none var/sadm 755 root sys
+d none var/sadm/install 755 root bin
+d none var/sadm/install/admin 755 root bin
+f none var/sadm/install/admin/default 444 root sys
+d none var/sadm/install/logs 555 root bin
+d none var/sadm/pkg 555 root sys
+d none var/sadm/security 555 root sys
+d none var/spool 755 root bin
+d none var/spool/pkg 1777 root bin
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/usr/src/pkgdefs/SUNWpkgcmdsr/prototype_i386	Fri Jun 05 10:28:40 2009 -0400
@@ -0,0 +1,48 @@
+#
+# 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
+# or http://www.opensolaris.org/os/licensing.
+# 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 2009 Sun Microsystems, Inc.  All rights reserved.
+# Use is subject to license terms.
+#
+#
+
+#
+# This required package information file contains a list of package contents.
+# The 'pkgmk' command uses this file to identify the contents of a package
+# and their location on the development machine when building the package.
+# Can be created via a text editor or through use of the 'pkgproto' command.
+
+#!search <pathname pathname ...>        # where to find pkg objects
+#!include <filename>                    # include another 'prototype' file
+#!default <mode> <owner> <group>        # default used if not specified on entry#!<param>=<value>                       # puts parameter in pkg environment
+
+#
+# Include ISA independent files (prototype_com)
+#
+!include prototype_com
+#
+#
+#
+# List files which are i386 specific here
+#
+# source locations relative to the prototype file
+#
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/usr/src/pkgdefs/SUNWpkgcmdsr/prototype_sparc	Fri Jun 05 10:28:40 2009 -0400
@@ -0,0 +1,50 @@
+#
+# 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
+# or http://www.opensolaris.org/os/licensing.
+# 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 2009 Sun Microsystems, Inc.  All rights reserved.
+# Use is subject to license terms.
+#
+#
+
+
+#
+# This required package information file contains a list of package contents.
+# The 'pkgmk' command uses this file to identify the contents of a package
+# and their location on the development machine when building the package.
+# Can be created via a text editor or through use of the 'pkgproto' command.
+
+#!search <pathname pathname ...>        # where to find pkg objects
+#!include <filename>                    # include another 'prototype' file
+#!default <mode> <owner> <group>        # default used if not specified on entry#!<param>=<value>                       # puts parameter in pkg environment
+
+#
+# Include ISA independent files (prototype_com)
+#
+!include prototype_com
+#
+#
+#
+# List files which are SPARC specific here
+#
+# source locations relative to the prototype file
+#
+#
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/usr/src/pkgdefs/SUNWpkgcmdsu/Makefile	Fri Jun 05 10:28:40 2009 -0400
@@ -0,0 +1,70 @@
+#
+# 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
+# or http://www.opensolaris.org/os/licensing.
+# 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 2009 Sun Microsystems, Inc.  All rights reserved.
+# Use is subject to license terms.
+#
+
+include $(SRC)/pkgdefs/Makefile.com
+
+
+DATAFILES += depend
+LICENSEFILES += $(ATT) $(SRC)/lib/libpkg/THIRDPARTYLICENSE
+CLOBBERFILES += action
+
+ACTION_SUNWpkgcmdsr=grep SUNWpkgcmdsr depend > /dev/null || \
+	( chmod 666 depend; \
+	echo "P SUNWpkgcmdsr	Package Commands (Root)" >> depend; \
+	chmod 444 depend );
+
+ACTION_SUNWwbsup=grep SUNWwbsup depend > /dev/null || \
+	( chmod 666 depend; \
+	echo "P SUNWwbsup             WAN boot support" \
+	chmod 444 depend )
+
+ACTION_SUNopenssl-libraries=grep SUNWopenssl-libraries depend > /dev/null || \
+	( chmod 666 depend; \
+	echo "P SUNWopenssl-libraries OpenSSL Libraries (Usr) >> depend; \
+	chmod 444 depend )
+
+ACTION_SUNWwsr2=grep SUNWwsr2 depend > /dev/null || \
+	( chmod 666 depend; \
+	echo "P SUNWwsr2  Solaris Product Registry & Web Start runtime support"\
+	>> depend; \
+	chmod 444 depend );
+
+
+.KEEP_STATE:
+
+all: $(FILES) action
+
+install: all pkg
+
+# action is a pseudotarget denoting completed work on the depend file
+action: depend
+	$(ACTION_SUNWpkgcmdsr)
+	$(ACTION_SUNWwbsup)
+	$(ACTION_SUNWopenssl-libraries)
+	$(ACTION_SUNWwsr2)
+	touch $@
+
+include $(SRC)/pkgdefs/Makefile.targ
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/usr/src/pkgdefs/SUNWpkgcmdsu/pkginfo.tmpl	Fri Jun 05 10:28:40 2009 -0400
@@ -0,0 +1,52 @@
+#
+# 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
+# or http://www.opensolaris.org/os/licensing.
+# 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 2009 Sun Microsystems, Inc.  All rights reserved.
+# Use is subject to license terms.
+#
+#
+
+
+#
+# This required package information file describes characteristics of the
+# package, such as package abbreviation, full package name, package version,
+# and package architecture.
+#
+PKG=SUNWpkgcmdsu
+NAME=SVr4 packaging commands (usr)
+ARCH="ISA"
+VERSION="ONVERS,REV=0.0.0"
+SUNW_PRODNAME="SunOS"
+SUNW_PRODVERS="RELEASE/VERSION"
+SUNW_PKGVERS=1.0
+SUNW_PKGTYPE=usr
+MAXINST=1000
+CATEGORY=system
+DESC=Solaris System V Release 4 Packaging Commands (usr)
+VENDOR=Sun Microsystems, Inc.
+HOTLINE=Please contact your local service provider
+EMAIL=
+CLASSES=none
+BASEDIR=/
+SUNW_PKG_ALLZONES=false
+SUNW_PKG_THISZONE=false
+SUNW_PKG_HOLLOW=false
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/usr/src/pkgdefs/SUNWpkgcmdsu/prototype_com	Fri Jun 05 10:28:40 2009 -0400
@@ -0,0 +1,76 @@
+#
+# 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
+# or http://www.opensolaris.org/os/licensing.
+# 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 2009 Sun Microsystems, Inc.  All rights reserved.
+# Use is subject to license terms.
+#
+
+#
+# This required package information file contains a list of package contents.
+# The 'pkgmk' command uses this file to identify the contents of a package
+# and their location on the development machine when building the package.
+# Can be created via a text editor or through use of the 'pkgproto' command.
+
+# packaging files
+i pkginfo
+i copyright
+i depend
+#
+# source locations relative to the prototype file
+#
+# SUNWpkgcmdsu
+#
+d none usr 755 root sys
+d none usr/bin 755 root bin
+f none usr/bin/pkgcond 555 root sys
+f none usr/bin/pkginfo 555 root sys
+f none usr/bin/pkgmk 555 root bin
+f none usr/bin/pkgparam 555 root sys
+f none usr/bin/pkgproto 555 root bin
+f none usr/bin/pkgtrans 555 root bin
+f none usr/bin/pkgadm 555 root bin
+d none usr/lib 755 root bin
+f none usr/lib/libpkg.so.1 755 root bin
+s none usr/lib/libpkg.so=./libpkg.so.1
+f none usr/lib/libinstzones.so.1 755 root bin
+d none usr/sadm 755 root bin
+d none usr/sadm/install 755 root bin
+d none usr/sadm/install/bin 755 root bin
+f none usr/sadm/install/bin/pkginstall 555 root sys
+f none usr/sadm/install/bin/pkgname 555 root sys
+f none usr/sadm/install/bin/pkgremove 555 root sys
+d none usr/sadm/install/scripts 755 root bin
+f none usr/sadm/install/scripts/cmdexec 555 root sys
+f none usr/sadm/install/scripts/i.CompCpio 555 root sys
+f none usr/sadm/install/scripts/i.awk 555 root sys
+f none usr/sadm/install/scripts/i.build 555 root sys
+f none usr/sadm/install/scripts/i.sed 555 root sys
+f none usr/sadm/install/scripts/r.awk 555 root sys
+f none usr/sadm/install/scripts/r.build 555 root sys
+f none usr/sadm/install/scripts/r.sed 555 root sys
+d none usr/sbin 755 root bin
+f none usr/sbin/pkgadd 555 root sys
+l none usr/sbin/pkgask=../../usr/sbin/pkgadd
+f none usr/sbin/pkgchk 555 root sys
+f none usr/sbin/pkgrm 555 root sys
+f none usr/sbin/installf 555 root sys
+l none usr/sbin/removef=../../usr/sbin/installf
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/usr/src/pkgdefs/SUNWpkgcmdsu/prototype_i386	Fri Jun 05 10:28:40 2009 -0400
@@ -0,0 +1,49 @@
+#
+# 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
+# or http://www.opensolaris.org/os/licensing.
+# 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 2009 Sun Microsystems, Inc.  All rights reserved.
+# Use is subject to license terms.
+#
+#
+
+#
+# This required package information file contains a list of package contents.
+# The 'pkgmk' command uses this file to identify the contents of a package
+# and their location on the development machine when building the package.
+# Can be created via a text editor or through use of the 'pkgproto' command.
+
+#!search <pathname pathname ...>        # where to find pkg objects
+#!include <filename>                    # include another 'prototype' file
+#!default <mode> <owner> <group>        # default used if not specified on entry#!<param>=<value>                       # puts parameter in pkg environment
+
+#
+# Include ISA independent files (prototype_com)
+#
+!include prototype_com
+#
+#
+#
+# List files which are i386 specific here
+#
+# source locations relative to the prototype file
+#
+#
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/usr/src/pkgdefs/SUNWpkgcmdsu/prototype_sparc	Fri Jun 05 10:28:40 2009 -0400
@@ -0,0 +1,48 @@
+#
+# 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
+# or http://www.opensolaris.org/os/licensing.
+# 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 2009 Sun Microsystems, Inc.  All rights reserved.
+# Use is subject to license terms.
+#
+
+#
+# This required package information file contains a list of package contents.
+# The 'pkgmk' command uses this file to identify the contents of a package
+# and their location on the development machine when building the package.
+# Can be created via a text editor or through use of the 'pkgproto' command.
+
+#!search <pathname pathname ...>        # where to find pkg objects
+#!include <filename>                    # include another 'prototype' file
+#!default <mode> <owner> <group>        # default used if not specified on entry#!<param>=<value>                       # puts parameter in pkg environment
+
+#
+# Include ISA independent files (prototype_com)
+#
+!include prototype_com
+#
+#
+#
+# List files which are SPARC specific here
+#
+# source locations relative to the prototype file
+#
+#
--- a/usr/src/pkgdefs/common_files/i.devpolicy	Fri Jun 05 10:27:16 2009 -0400
+++ b/usr/src/pkgdefs/common_files/i.devpolicy	Fri Jun 05 10:28:40 2009 -0400
@@ -20,7 +20,7 @@
 # CDDL HEADER END
 #
 #
-# Copyright 2008 Sun Microsystems, Inc.  All rights reserved.
+# Copyright 2009 Sun Microsystems, Inc.  All rights reserved.
 # Use is subject to license terms.
 #
 #  NOTE:  When a change is made to the source file for
@@ -50,7 +50,7 @@
 	rm -f $dest.$$
 
 	# potential additions
-	additions="aggr bge dnet keysock ibd icmp icmp6 ipnet ipsecah ipsecesp openeepr random spdsock vni ipf pfil scsi_vhci"
+	additions="keysock icmp icmp6 ipnet ipsecah ipsecesp openeepr random spdsock ipf pfil scsi_vhci"
 
 	for dev in $additions
 	do
@@ -71,7 +71,7 @@
 	done
 
 	# potential deletions
-	deletions="elx dld dld:ctl aggr:ctl vnic:ctl le"
+	deletions="aggr aggr:ctl bge ce dld dld:ctl dnet elx elxl eri ge hme ibd iprb le pcelx qfe softmac spwr vni vnic vnic:ctl"
 
 	for dev in $deletions
 	do
--- a/usr/src/pkgdefs/common_files/i.minorperm_i386	Fri Jun 05 10:27:16 2009 -0400
+++ b/usr/src/pkgdefs/common_files/i.minorperm_i386	Fri Jun 05 10:28:40 2009 -0400
@@ -204,6 +204,7 @@
 asy:[a-z]
 asy:[a-z],cu
 i2o_bs:*
+vni:*
 EOF
 }
 
@@ -281,7 +282,7 @@
 bl:*
 sctp:*
 sctp6:*
-vni:*
+dlpistub:*
 cpuid:self
 clone:bge
 clone:igb
--- a/usr/src/pkgdefs/common_files/i.minorperm_sparc	Fri Jun 05 10:27:16 2009 -0400
+++ b/usr/src/pkgdefs/common_files/i.minorperm_sparc	Fri Jun 05 10:28:40 2009 -0400
@@ -21,7 +21,7 @@
 #
 
 #
-# Copyright 2008 Sun Microsystems, Inc.  All rights reserved.
+# Copyright 2009 Sun Microsystems, Inc.  All rights reserved.
 # Use is subject to license terms.
 #
 
@@ -192,6 +192,7 @@
 SUNW,DBRId:*
 SUNW,DBRIe:*
 SUNW,DBRIf:*
+vni:*
 EOF
 }
 
@@ -324,7 +325,7 @@
 bl:*
 sctp:*
 sctp6:*
-vni:*
+dlpistub:*
 cpuid:self
 ntwdt:*
 dld:*
--- a/usr/src/pkgdefs/etc/exception_list_i386	Fri Jun 05 10:27:16 2009 -0400
+++ b/usr/src/pkgdefs/etc/exception_list_i386	Fri Jun 05 10:28:40 2009 -0400
@@ -484,6 +484,14 @@
 lib/libdhcpagent.so			i386
 lib/libdhcputil.so			i386
 #
+# These files are installed in the proto area by the build of libinstzones
+# and libpkg
+#
+usr/lib/llib-linstzones			i386
+usr/lib/llib-linstzones.ln		i386
+usr/lib/llib-lpkg			i386
+usr/lib/llib-lpkg.ln			i386
+#
 # These files are installed in the proto area by the build of libinetcfg.
 # Only the shared object is shipped.
 #
--- a/usr/src/pkgdefs/etc/exception_list_sparc	Fri Jun 05 10:27:16 2009 -0400
+++ b/usr/src/pkgdefs/etc/exception_list_sparc	Fri Jun 05 10:28:40 2009 -0400
@@ -487,6 +487,14 @@
 lib/libdhcpagent.so			sparc
 lib/libdhcputil.so			sparc
 #
+# These files are installed in the proto area by the build of libinstzones
+# and libpkg
+#
+usr/lib/llib-linstzones			sparc
+usr/lib/llib-linstzones.ln		sparc    
+usr/lib/llib-lpkg			sparc
+usr/lib/llib-lpkg.ln			sparc
+#
 # These files are installed in the proto area by the build of libinetcfg.
 # Only the shared object is shipped.
 #
--- a/usr/src/tools/opensolaris/license-list	Fri Jun 05 10:27:16 2009 -0400
+++ b/usr/src/tools/opensolaris/license-list	Fri Jun 05 10:28:40 2009 -0400
@@ -51,6 +51,8 @@
 usr/src/cmd/mdb/common/libstand/THIRDPARTYLICENSE
 usr/src/cmd/mt/THIRDPARTYLICENSE
 usr/src/cmd/ndmpd/LICENSE
+usr/src/cmd/ntfsprogs/THIRDPARTYLICENSE
+usr/src/cmd/parted/THIRDPARTYLICENSE
 usr/src/cmd/perl/5.8.4/distrib/ext/Cwd/THIRDPARTYLICENSE
 usr/src/cmd/perl/THIRDPARTYLICENSE
 usr/src/cmd/refer/THIRDPARTYLICENSE
@@ -92,6 +94,9 @@
 usr/src/lib/libkmf/THIRDPARTYLICENSE
 usr/src/lib/libldap5/THIRDPARTYLICENSE
 usr/src/lib/libmp/common/THIRDPARTYLICENSE
+usr/src/lib/libntfs/THIRDPARTYLICENSE
+usr/src/lib/libparted/THIRDPARTYLICENSE
+usr/src/lib/libpkg/THIRDPARTYLICENSE
 usr/src/lib/libpp/THIRDPARTYLICENSE
 usr/src/lib/libresolv/THIRDPARTYLICENSE
 usr/src/lib/libresolv2/THIRDPARTYLICENSE
--- a/usr/src/uts/common/Makefile.files	Fri Jun 05 10:27:16 2009 -0400
+++ b/usr/src/uts/common/Makefile.files	Fri Jun 05 10:28:40 2009 -0400
@@ -764,6 +764,9 @@
 USBA_OBJS +=	hcdi.o	usba.o	usbai.o hubdi.o parser.o genconsole.o \
 		usbai_pipe_mgmt.o usbai_req.o usbai_util.o usbai_register.o \
 		usba_devdb.o usba10_calls.o usba_ugen.o whcdi.o wa.o
+USBA_WITHOUT_WUSB_OBJS +=	hcdi.o	usba.o	usbai.o hubdi.o parser.o genconsole.o \
+		usbai_pipe_mgmt.o usbai_req.o usbai_util.o usbai_register.o \
+		usba_devdb.o usba10_calls.o usba_ugen.o
 
 USBA10_OBJS +=	usba10.o
 
--- a/usr/src/uts/common/fs/nfs/nfs3_vnops.c	Fri Jun 05 10:27:16 2009 -0400
+++ b/usr/src/uts/common/fs/nfs/nfs3_vnops.c	Fri Jun 05 10:28:40 2009 -0400
@@ -747,6 +747,12 @@
 			resid = uiop->uio_resid;
 			offset = uiop->uio_loffset;
 			error = rp->r_error;
+			/*
+			 * A close may have cleared r_error, if so,
+			 * propagate ESTALE error return properly
+			 */
+			if (error == 0)
+				error = ESTALE;
 			goto bottom;
 		}
 		bufsize = MIN(uiop->uio_resid, mi->mi_stsize);
@@ -783,6 +789,12 @@
 
 		if (rp->r_flags & RSTALE) {
 			error = rp->r_error;
+			/*
+			 * A close may have cleared r_error, if so,
+			 * propagate ESTALE error return properly
+			 */
+			if (error == 0)
+				error = ESTALE;
 			break;
 		}
 
@@ -4393,8 +4405,15 @@
 				mutex_exit(&rp->r_statelock);
 			}
 			crfree(cred);
-		} else
+		} else {
 			error = rp->r_error;
+			/*
+			 * A close may have cleared r_error, if so,
+			 * propagate ESTALE error return properly
+			 */
+			if (error == 0)
+				error = ESTALE;
+		}
 	}
 
 	if (error != 0 && error != NFS_EOF)
--- a/usr/src/uts/common/fs/nfs/nfs4_vnops.c	Fri Jun 05 10:27:16 2009 -0400
+++ b/usr/src/uts/common/fs/nfs/nfs4_vnops.c	Fri Jun 05 10:28:40 2009 -0400
@@ -2824,6 +2824,12 @@
 			resid = uiop->uio_resid;
 			offset = uiop->uio_loffset;
 			error = rp->r_error;
+			/*
+			 * A close may have cleared r_error, if so,
+			 * propagate ESTALE error return properly
+			 */
+			if (error == 0)
+				error = ESTALE;
 			goto bottom;
 		}
 
@@ -2866,6 +2872,12 @@
 
 		if (rp->r_flags & R4STALE) {
 			error = rp->r_error;
+			/*
+			 * A close may have cleared r_error, if so,
+			 * propagate ESTALE error return properly
+			 */
+			if (error == 0)
+				error = ESTALE;
 			break;
 		}
 
@@ -9573,8 +9585,15 @@
 				mutex_exit(&rp->r_statelock);
 			}
 			crfree(cred_otw);
-		} else
+		} else {
 			error = rp->r_error;
+			/*
+			 * A close may have cleared r_error, if so,
+			 * propagate ESTALE error return properly
+			 */
+			if (error == 0)
+				error = ESTALE;
+		}
 	}
 
 	if (error != 0 && error != NFS_EOF)
--- a/usr/src/uts/common/fs/nfs/nfs_vnops.c	Fri Jun 05 10:27:16 2009 -0400
+++ b/usr/src/uts/common/fs/nfs/nfs_vnops.c	Fri Jun 05 10:28:40 2009 -0400
@@ -19,7 +19,7 @@
  * CDDL HEADER END
  */
 /*
- * Copyright 2008 Sun Microsystems, Inc.  All rights reserved.
+ * Copyright 2009 Sun Microsystems, Inc.  All rights reserved.
  * Use is subject to license terms.
  *
  *	Copyright (c) 1983,1984,1985,1986,1987,1988,1989 AT&T.
@@ -646,6 +646,12 @@
 			resid = uiop->uio_resid;
 			offset = uiop->uio_loffset;
 			error = rp->r_error;
+			/*
+			 * A close may have cleared r_error, if so,
+			 * propagate ESTALE error return properly
+			 */
+			if (error == 0)
+				error = ESTALE;
 			goto bottom;
 		}
 		bufsize = MIN(uiop->uio_resid, mi->mi_curwrite);
@@ -675,6 +681,12 @@
 
 		if (rp->r_flags & RSTALE) {
 			error = rp->r_error;
+			/*
+			 * A close may have cleared r_error, if so,
+			 * propagate ESTALE error return properly
+			 */
+			if (error == 0)
+				error = ESTALE;
 			break;
 		}
 
@@ -3484,8 +3496,15 @@
 				mutex_exit(&rp->r_statelock);
 			}
 			crfree(cred);
-		} else
+		} else {
 			error = rp->r_error;
+			/*
+			 * A close may have cleared r_error, if so,
+			 * propagate ESTALE error return properly
+			 */
+			if (error == 0)
+				error = ESTALE;
+		}
 	}
 
 	if (error != 0 && error != NFS_EOF)
--- a/usr/src/uts/common/fs/zfs/spa.c	Fri Jun 05 10:27:16 2009 -0400
+++ b/usr/src/uts/common/fs/zfs/spa.c	Fri Jun 05 10:28:40 2009 -0400
@@ -2279,29 +2279,36 @@
 
 #ifdef _KERNEL
 /*
- * Build a "root" vdev for a top level vdev read in from a rootpool
- * device label.
+ * Get the root pool information from the root disk, then import the root pool
+ * during the system boot up time.
  */
-static void
-spa_build_rootpool_config(nvlist_t *config)
+extern int vdev_disk_read_rootlabel(char *, char *, nvlist_t **);
+
+static nvlist_t *
+spa_generate_rootconf(char *devpath, char *devid, uint64_t *guid)
 {
+	nvlist_t *config;
 	nvlist_t *nvtop, *nvroot;
 	uint64_t pgid;
 
+	if (vdev_disk_read_rootlabel(devpath, devid, &config) != 0)
+		return (NULL);
+
 	/*
 	 * Add this top-level vdev to the child array.
 	 */
-	VERIFY(nvlist_lookup_nvlist(config, ZPOOL_CONFIG_VDEV_TREE, &nvtop)
-	    == 0);
-	VERIFY(nvlist_lookup_uint64(config, ZPOOL_CONFIG_POOL_GUID, &pgid)
-	    == 0);
+	VERIFY(nvlist_lookup_nvlist(config, ZPOOL_CONFIG_VDEV_TREE,
+	    &nvtop) == 0);
+	VERIFY(nvlist_lookup_uint64(config, ZPOOL_CONFIG_POOL_GUID,
+	    &pgid) == 0);
+	VERIFY(nvlist_lookup_uint64(config, ZPOOL_CONFIG_GUID, guid) == 0);
 
 	/*
 	 * Put this pool's top-level vdevs into a root vdev.
 	 */
 	VERIFY(nvlist_alloc(&nvroot, NV_UNIQUE_NAME, KM_SLEEP) == 0);
-	VERIFY(nvlist_add_string(nvroot, ZPOOL_CONFIG_TYPE, VDEV_TYPE_ROOT)
-	    == 0);
+	VERIFY(nvlist_add_string(nvroot, ZPOOL_CONFIG_TYPE,
+	    VDEV_TYPE_ROOT) == 0);
 	VERIFY(nvlist_add_uint64(nvroot, ZPOOL_CONFIG_ID, 0ULL) == 0);
 	VERIFY(nvlist_add_uint64(nvroot, ZPOOL_CONFIG_GUID, pgid) == 0);
 	VERIFY(nvlist_add_nvlist_array(nvroot, ZPOOL_CONFIG_CHILDREN,
@@ -2313,129 +2320,42 @@
 	 */
 	VERIFY(nvlist_add_nvlist(config, ZPOOL_CONFIG_VDEV_TREE, nvroot) == 0);
 	nvlist_free(nvroot);
+	return (config);
 }
 
 /*
- * Get the root pool information from the root disk, then import the root pool
- * during the system boot up time.
+ * Walk the vdev tree and see if we can find a device with "better"
+ * configuration. A configuration is "better" if the label on that
+ * device has a more recent txg.
  */
-extern int vdev_disk_read_rootlabel(char *, char *, nvlist_t **);
-
-int
-spa_check_rootconf(char *devpath, char *devid, nvlist_t **bestconf,
-    uint64_t *besttxg)
-{
-	nvlist_t *config;
-	uint64_t txg;
-	int error;
-
-	if (error = vdev_disk_read_rootlabel(devpath, devid, &config))
-		return (error);
-
-	VERIFY(nvlist_lookup_uint64(config, ZPOOL_CONFIG_POOL_TXG, &txg) == 0);
-
-	if (bestconf != NULL)
-		*bestconf = config;
-	else
-		nvlist_free(config);
-	*besttxg = txg;
-	return (0);
-}
-
-boolean_t
-spa_rootdev_validate(nvlist_t *nv)
-{
-	uint64_t ival;
-
-	if (nvlist_lookup_uint64(nv, ZPOOL_CONFIG_OFFLINE, &ival) == 0 ||
-	    nvlist_lookup_uint64(nv, ZPOOL_CONFIG_FAULTED, &ival) == 0 ||
-	    nvlist_lookup_uint64(nv, ZPOOL_CONFIG_REMOVED, &ival) == 0)
-		return (B_FALSE);
-
-	return (B_TRUE);
-}
-
-
-/*
- * Given the boot device's physical path or devid, check if the device
- * is in a valid state.  If so, return the configuration from the vdev
- * label.
- */
-int
-spa_get_rootconf(char *devpath, char *devid, nvlist_t **bestconf)
+static void
+spa_alt_rootvdev(vdev_t *vd, vdev_t **avd, uint64_t *txg)
 {
-	nvlist_t *conf = NULL;
-	uint64_t txg = 0;
-	nvlist_t *nvtop, **child;
-	char *type;
-	char *bootpath = NULL;
-	uint_t children, c;
-	char *tmp;
-	int error;
-
-	if (devpath && ((tmp = strchr(devpath, ' ')) != NULL))
-		*tmp = '\0';
-	if (error = spa_check_rootconf(devpath, devid, &conf, &txg)) {
-		cmn_err(CE_NOTE, "error reading device label");
-		return (error);
-	}
-	if (txg == 0) {
-		cmn_err(CE_NOTE, "this device is detached");
-		nvlist_free(conf);
-		return (EINVAL);
-	}
-
-	VERIFY(nvlist_lookup_nvlist(conf, ZPOOL_CONFIG_VDEV_TREE,
-	    &nvtop) == 0);
-	VERIFY(nvlist_lookup_string(nvtop, ZPOOL_CONFIG_TYPE, &type) == 0);
-
-	if (strcmp(type, VDEV_TYPE_DISK) == 0) {
-		if (spa_rootdev_validate(nvtop)) {
-			goto out;
-		} else {
-			nvlist_free(conf);
-			return (EINVAL);
+	int c;
+
+	for (c = 0; c < vd->vdev_children; c++)
+		spa_alt_rootvdev(vd->vdev_child[c], avd, txg);
+
+	if (vd->vdev_ops->vdev_op_leaf) {
+		nvlist_t *label;
+		uint64_t label_txg;
+
+		if (vdev_disk_read_rootlabel(vd->vdev_physpath, vd->vdev_devid,
+		    &label) != 0)
+			return;
+
+		VERIFY(nvlist_lookup_uint64(label, ZPOOL_CONFIG_POOL_TXG,
+		    &label_txg) == 0);
+
+		/*
+		 * Do we have a better boot device?
+		 */
+		if (label_txg > *txg) {
+			*txg = label_txg;
+			*avd = vd;
 		}
+		nvlist_free(label);
 	}
-
-	ASSERT(strcmp(type, VDEV_TYPE_MIRROR) == 0);
-
-	VERIFY(nvlist_lookup_nvlist_array(nvtop, ZPOOL_CONFIG_CHILDREN,
-	    &child, &children) == 0);
-
-	/*
-	 * Go thru vdevs in the mirror to see if the given device
-	 * has the most recent txg. Only the device with the most
-	 * recent txg has valid information and should be booted.
-	 */
-	for (c = 0; c < children; c++) {
-		char *cdevid, *cpath;
-		uint64_t tmptxg;
-
-		cpath = NULL;
-		cdevid = NULL;
-		(void) nvlist_lookup_string(child[c], ZPOOL_CONFIG_PHYS_PATH,
-		    &cpath);
-		(void) nvlist_lookup_string(child[c], ZPOOL_CONFIG_DEVID,
-		    &cdevid);
-		if (cpath == NULL && cdevid == NULL)
-			return (EINVAL);
-		if ((spa_check_rootconf(cpath, cdevid, NULL,
-		    &tmptxg) == 0) && (tmptxg > txg)) {
-			txg = tmptxg;
-			VERIFY(nvlist_lookup_string(child[c],
-			    ZPOOL_CONFIG_PATH, &bootpath) == 0);
-		}
-	}
-
-	/* Does the best device match the one we've booted from? */
-	if (bootpath) {
-		cmn_err(CE_NOTE, "try booting from '%s'", bootpath);
-		return (EINVAL);
-	}
-out:
-	*bestconf = conf;
-	return (0);
 }
 
 /*
@@ -2453,24 +2373,25 @@
 int
 spa_import_rootpool(char *devpath, char *devid)
 {
-	nvlist_t *conf = NULL;
+	spa_t *spa;
+	vdev_t *rvd, *bvd, *avd = NULL;
+	nvlist_t *config, *nvtop;
+	uint64_t guid, txg;
 	char *pname;
 	int error;
-	spa_t *spa;
 
 	/*
-	 * Get the vdev pathname and configuation from the most
-	 * recently updated vdev (highest txg).
+	 * Read the label from the boot device and generate a configuration.
 	 */
-	if (error = spa_get_rootconf(devpath, devid, &conf))
-		goto msg_out;
-
-	/*
-	 * Add type "root" vdev to the config.
-	 */
-	spa_build_rootpool_config(conf);
-
-	VERIFY(nvlist_lookup_string(conf, ZPOOL_CONFIG_POOL_NAME, &pname) == 0);
+	if ((config = spa_generate_rootconf(devpath, devid, &guid)) == NULL) {
+		cmn_err(CE_NOTE, "Can not read the pool label from '%s'",
+		    devpath);
+		return (EIO);
+	}
+
+	VERIFY(nvlist_lookup_string(config, ZPOOL_CONFIG_POOL_NAME,
+	    &pname) == 0);
+	VERIFY(nvlist_lookup_uint64(config, ZPOOL_CONFIG_POOL_TXG, &txg) == 0);
 
 	mutex_enter(&spa_namespace_lock);
 	if ((spa = spa_lookup(pname)) != NULL) {
@@ -2482,24 +2403,72 @@
 	}
 
 	spa = spa_add(pname, NULL);
-
 	spa->spa_is_root = B_TRUE;
-	VERIFY(nvlist_dup(conf, &spa->spa_config, 0) == 0);
+
+	/*
+	 * Build up a vdev tree based on the boot device's label config.
+	 */
+	VERIFY(nvlist_lookup_nvlist(config, ZPOOL_CONFIG_VDEV_TREE,
+	    &nvtop) == 0);
+	spa_config_enter(spa, SCL_ALL, FTAG, RW_WRITER);
+	error = spa_config_parse(spa, &rvd, nvtop, NULL, 0,
+	    VDEV_ALLOC_ROOTPOOL);
+	spa_config_exit(spa, SCL_ALL, FTAG);
+	if (error) {
+		mutex_exit(&spa_namespace_lock);
+		nvlist_free(config);
+		cmn_err(CE_NOTE, "Can not parse the config for pool '%s'",
+		    pname);
+		return (error);
+	}
+
+	/*
+	 * Get the boot vdev.
+	 */
+	if ((bvd = vdev_lookup_by_guid(rvd, guid)) == NULL) {
+		cmn_err(CE_NOTE, "Can not find the boot vdev for guid %llu",
+		    (u_longlong_t)guid);
+		error = ENOENT;
+		goto out;
+	}
+
+	/*
+	 * Determine if there is a better boot device.
+	 */
+	avd = bvd;
+	spa_alt_rootvdev(rvd, &avd, &txg);
+	if (avd != bvd) {
+		cmn_err(CE_NOTE, "The boot device is 'degraded'. Please "
+		    "try booting from '%s'", avd->vdev_path);
+		error = EINVAL;
+		goto out;
+	}
+
+	/*
+	 * If the boot device is part of a spare vdev then ensure that
+	 * we're booting off the active spare.
+	 */
+	if (bvd->vdev_parent->vdev_ops == &vdev_spare_ops &&
+	    !bvd->vdev_isspare) {
+		cmn_err(CE_NOTE, "The boot device is currently spared. Please "
+		    "try booting from '%s'",
+		    bvd->vdev_parent->vdev_child[1]->vdev_path);
+		error = EINVAL;
+		goto out;
+	}
+
+	VERIFY(nvlist_dup(config, &spa->spa_config, 0) == 0);
+	error = 0;
+out:
+	spa_config_enter(spa, SCL_ALL, FTAG, RW_WRITER);
+	vdev_free(rvd);
+	spa_config_exit(spa, SCL_ALL, FTAG);
 	mutex_exit(&spa_namespace_lock);
 
-	nvlist_free(conf);
-	return (0);
-
-msg_out:
-	cmn_err(CE_NOTE, "\n"
-	    "  ***************************************************  \n"
-	    "  *  This device is not bootable!                   *  \n"
-	    "  *  It is either offlined or detached or faulted.  *  \n"
-	    "  *  Please try to boot from a different device.    *  \n"
-	    "  ***************************************************  ");
-
+	nvlist_free(config);
 	return (error);
 }
+
 #endif
 
 /*
--- a/usr/src/uts/common/fs/zfs/sys/spa.h	Fri Jun 05 10:27:16 2009 -0400
+++ b/usr/src/uts/common/fs/zfs/sys/spa.h	Fri Jun 05 10:28:40 2009 -0400
@@ -324,9 +324,6 @@
     char *altroot, size_t buflen);
 extern int spa_create(const char *pool, nvlist_t *config, nvlist_t *props,
     const char *history_str, nvlist_t *zplprops);
-extern int spa_check_rootconf(char *devpath, char *devid,
-    nvlist_t **bestconf, uint64_t *besttxg);
-extern boolean_t spa_rootdev_validate(nvlist_t *nv);
 extern int spa_import_rootpool(char *devpath, char *devid);
 extern int spa_import(const char *pool, nvlist_t *config, nvlist_t *props);
 extern int spa_import_verbatim(const char *, nvlist_t *, nvlist_t *);
--- a/usr/src/uts/common/fs/zfs/sys/spa_boot.h	Fri Jun 05 10:27:16 2009 -0400
+++ b/usr/src/uts/common/fs/zfs/sys/spa_boot.h	Fri Jun 05 10:28:40 2009 -0400
@@ -19,15 +19,13 @@
  * CDDL HEADER END
  */
 /*
- * Copyright 2008 Sun Microsystems, Inc.  All rights reserved.
+ * Copyright 2009 Sun Microsystems, Inc.  All rights reserved.
  * Use is subject to license terms.
  */
 
 #ifndef _SYS_SPA_BOOT_H
 #define	_SYS_SPA_BOOT_H
 
-#pragma ident	"%Z%%M%	%I%	%E% SMI"
-
 #include <sys/nvpair.h>
 
 #ifdef	__cplusplus
@@ -36,7 +34,6 @@
 
 extern char *spa_get_bootprop(char *prop);
 extern void spa_free_bootprop(char *prop);
-extern int spa_get_rootconf(char *devpath, char *devid, nvlist_t **bestconf_p);
 
 #ifdef	__cplusplus
 }
--- a/usr/src/uts/common/fs/zfs/sys/vdev_impl.h	Fri Jun 05 10:27:16 2009 -0400
+++ b/usr/src/uts/common/fs/zfs/sys/vdev_impl.h	Fri Jun 05 10:28:40 2009 -0400
@@ -239,6 +239,7 @@
 #define	VDEV_ALLOC_ADD		1
 #define	VDEV_ALLOC_SPARE	2
 #define	VDEV_ALLOC_L2CACHE	3
+#define	VDEV_ALLOC_ROOTPOOL	4
 
 /*
  * Allocate or free a vdev
--- a/usr/src/uts/common/fs/zfs/vdev.c	Fri Jun 05 10:27:16 2009 -0400
+++ b/usr/src/uts/common/fs/zfs/vdev.c	Fri Jun 05 10:28:40 2009 -0400
@@ -373,6 +373,9 @@
 	} else if (alloctype == VDEV_ALLOC_L2CACHE) {
 		if (nvlist_lookup_uint64(nv, ZPOOL_CONFIG_GUID, &guid) != 0)
 			return (EINVAL);
+	} else if (alloctype == VDEV_ALLOC_ROOTPOOL) {
+		if (nvlist_lookup_uint64(nv, ZPOOL_CONFIG_GUID, &guid) != 0)
+			return (EINVAL);
 	}
 
 	/*
@@ -475,13 +478,23 @@
 	 * If we're a leaf vdev, try to load the DTL object and other state.
 	 */
 	if (vd->vdev_ops->vdev_op_leaf &&
-	    (alloctype == VDEV_ALLOC_LOAD || alloctype == VDEV_ALLOC_L2CACHE)) {
+	    (alloctype == VDEV_ALLOC_LOAD || alloctype == VDEV_ALLOC_L2CACHE ||
+	    alloctype == VDEV_ALLOC_ROOTPOOL)) {
 		if (alloctype == VDEV_ALLOC_LOAD) {
 			(void) nvlist_lookup_uint64(nv, ZPOOL_CONFIG_DTL,
 			    &vd->vdev_dtl_smo.smo_object);
 			(void) nvlist_lookup_uint64(nv, ZPOOL_CONFIG_UNSPARE,
 			    &vd->vdev_unspare);
 		}
+
+		if (alloctype == VDEV_ALLOC_ROOTPOOL) {
+			uint64_t spare = 0;
+
+			if (nvlist_lookup_uint64(nv, ZPOOL_CONFIG_IS_SPARE,
+			    &spare) == 0 && spare)
+				spa_spare_add(vd);
+		}
+
 		(void) nvlist_lookup_uint64(nv, ZPOOL_CONFIG_OFFLINE,
 		    &vd->vdev_offline);
 
--- a/usr/src/uts/common/fs/zfs/zfs_vfsops.c	Fri Jun 05 10:27:16 2009 -0400
+++ b/usr/src/uts/common/fs/zfs/zfs_vfsops.c	Fri Jun 05 10:28:40 2009 -0400
@@ -1037,6 +1037,16 @@
 zfsvfs_free(zfsvfs_t *zfsvfs)
 {
 	int i;
+	extern krwlock_t zfsvfs_lock; /* in zfs_znode.c */
+
+	/*
+	 * This is a barrier to prevent the filesystem from going away in
+	 * zfs_znode_move() until we can safely ensure that the filesystem is
+	 * not unmounted. We consider the filesystem valid before the barrier
+	 * and invalid after the barrier.
+	 */
+	rw_enter(&zfsvfs_lock, RW_READER);
+	rw_exit(&zfsvfs_lock);
 
 	zfs_fuid_destroy(zfsvfs);
 
--- a/usr/src/uts/common/fs/zfs/zfs_vnops.c	Fri Jun 05 10:27:16 2009 -0400
+++ b/usr/src/uts/common/fs/zfs/zfs_vnops.c	Fri Jun 05 10:28:40 2009 -0400
@@ -2180,8 +2180,6 @@
 	ZFS_VERIFY_ZP(zp);
 	pzp = zp->z_phys;
 
-	mutex_enter(&zp->z_lock);
-
 	/*
 	 * If ACL is trivial don't bother looking for ACE_READ_ATTRIBUTES.
 	 * Also, if we are the owner don't bother, since owner should
@@ -2191,7 +2189,6 @@
 	    (pzp->zp_uid != crgetuid(cr))) {
 		if (error = zfs_zaccess(zp, ACE_READ_ATTRIBUTES, 0,
 		    skipaclchk, cr)) {
-			mutex_exit(&zp->z_lock);
 			ZFS_EXIT(zfsvfs);
 			return (error);
 		}
@@ -2202,6 +2199,7 @@
 	 * than to determine whether we were asked the question.
 	 */
 
+	mutex_enter(&zp->z_lock);
 	vap->va_type = vp->v_type;
 	vap->va_mode = pzp->zp_mode & MODEMASK;
 	zfs_fuid_map_ids(zp, cr, &vap->va_uid, &vap->va_gid);
--- a/usr/src/uts/common/fs/zfs/zfs_znode.c	Fri Jun 05 10:27:16 2009 -0400
+++ b/usr/src/uts/common/fs/zfs/zfs_znode.c	Fri Jun 05 10:28:40 2009 -0400
@@ -87,6 +87,12 @@
  * (such as VFS logic) that will not compile easily in userland.
  */
 #ifdef _KERNEL
+/*
+ * Needed to close a small window in zfs_znode_move() that allows the zfsvfs to
+ * be freed before it can be safely accessed.
+ */
+krwlock_t zfsvfs_lock;
+
 static kmem_cache_t *znode_cache = NULL;
 
 /*ARGSUSED*/
@@ -154,8 +160,9 @@
 #ifdef	ZNODE_STATS
 static struct {
 	uint64_t zms_zfsvfs_invalid;
+	uint64_t zms_zfsvfs_recheck1;
 	uint64_t zms_zfsvfs_unmounted;
-	uint64_t zms_zfsvfs_recheck_invalid;
+	uint64_t zms_zfsvfs_recheck2;
 	uint64_t zms_obj_held;
 	uint64_t zms_vnode_locked;
 	uint64_t zms_not_only_dnlc;
@@ -229,15 +236,32 @@
 	}
 
 	/*
-	 * Ensure that the filesystem is not unmounted during the move.
-	 * This is the equivalent to ZFS_ENTER().
+	 * Close a small window in which it's possible that the filesystem could
+	 * be unmounted and freed, and zfsvfs, though valid in the previous
+	 * statement, could point to unrelated memory by the time we try to
+	 * prevent the filesystem from being unmounted.
+	 */
+	rw_enter(&zfsvfs_lock, RW_WRITER);
+	if (zfsvfs != ozp->z_zfsvfs) {
+		rw_exit(&zfsvfs_lock);
+		ZNODE_STAT_ADD(znode_move_stats.zms_zfsvfs_recheck1);
+		return (KMEM_CBRC_DONT_KNOW);
+	}
+
+	/*
+	 * If the znode is still valid, then so is the file system. We know that
+	 * no valid file system can be freed while we hold zfsvfs_lock, so we
+	 * can safely ensure that the filesystem is not and will not be
+	 * unmounted. The next statement is equivalent to ZFS_ENTER().
 	 */
 	rrw_enter(&zfsvfs->z_teardown_lock, RW_READER, FTAG);
 	if (zfsvfs->z_unmounted) {
 		ZFS_EXIT(zfsvfs);
+		rw_exit(&zfsvfs_lock);
 		ZNODE_STAT_ADD(znode_move_stats.zms_zfsvfs_unmounted);
 		return (KMEM_CBRC_DONT_KNOW);
 	}
+	rw_exit(&zfsvfs_lock);
 
 	mutex_enter(&zfsvfs->z_znodes_lock);
 	/*
@@ -247,7 +271,7 @@
 	if (zfsvfs != ozp->z_zfsvfs) {
 		mutex_exit(&zfsvfs->z_znodes_lock);
 		ZFS_EXIT(zfsvfs);
-		ZNODE_STAT_ADD(znode_move_stats.zms_zfsvfs_recheck_invalid);
+		ZNODE_STAT_ADD(znode_move_stats.zms_zfsvfs_recheck2);
 		return (KMEM_CBRC_DONT_KNOW);
 	}
 
@@ -303,6 +327,7 @@
 	/*
 	 * Initialize zcache
 	 */
+	rw_init(&zfsvfs_lock, NULL, RW_DEFAULT, NULL);
 	ASSERT(znode_cache == NULL);
 	znode_cache = kmem_cache_create("zfs_znode_cache",
 	    sizeof (znode_t), 0, zfs_znode_cache_constructor,
@@ -324,6 +349,7 @@
 	if (znode_cache)
 		kmem_cache_destroy(znode_cache);
 	znode_cache = NULL;
+	rw_destroy(&zfsvfs_lock);
 }
 
 struct vnodeops *zfs_dvnodeops;
--- a/usr/src/uts/common/fs/zfs/zvol.c	Fri Jun 05 10:27:16 2009 -0400
+++ b/usr/src/uts/common/fs/zfs/zvol.c	Fri Jun 05 10:28:40 2009 -0400
@@ -1051,7 +1051,9 @@
 	int numerrors = 0;
 
 	for (c = 0; c < vd->vdev_children; c++) {
-		ASSERT(vd->vdev_ops == &vdev_mirror_ops);
+		ASSERT(vd->vdev_ops == &vdev_mirror_ops ||
+		    vd->vdev_ops == &vdev_replacing_ops ||
+		    vd->vdev_ops == &vdev_spare_ops);
 		int err = zvol_dumpio_vdev(vd->vdev_child[c],
 		    addr, offset, size, doread, isdump);
 		if (err != 0) {
--- a/usr/src/uts/common/io/audio/drv/audiohd/audiohd.c	Fri Jun 05 10:27:16 2009 -0400
+++ b/usr/src/uts/common/io/audio/drv/audiohd/audiohd.c	Fri Jun 05 10:28:40 2009 -0400
@@ -4867,7 +4867,7 @@
 	uint32_t		urctrl;
 	int			i;
 
-	for (i = 0; i <= AUDIOHD_CODEC_MAX; i++) {
+	for (i = 0; i < AUDIOHD_CODEC_MAX; i++) {
 		codec = statep->codec[i];
 		if (!codec)
 			continue;
--- a/usr/src/uts/common/io/cpudrv.c	Fri Jun 05 10:27:16 2009 -0400
+++ b/usr/src/uts/common/io/cpudrv.c	Fri Jun 05 10:28:40 2009 -0400
@@ -853,7 +853,8 @@
 /*
  * Marks a component busy and calls pm_raise_power().
  */
-#define	CPUDRV_MONITOR_PM_BUSY_AND_RAISE(dip, cpudsp, cpupm, new_level) { \
+#define	CPUDRV_MONITOR_PM_BUSY_AND_RAISE(dip, cpudsp, cpupm, new_spd) { \
+	int ret; \
 	/* \
 	 * Mark driver and PM framework busy first so framework doesn't try \
 	 * to bring CPU to lower speed when we need to be at higher speed. \
@@ -862,13 +863,16 @@
 	mutex_exit(&(cpudsp)->lock); \
 	DPRINTF(D_PM_MONITOR, ("cpudrv_monitor: instance %d: " \
 	    "pm_raise_power called to %d\n", ddi_get_instance((dip)), \
-		(new_level))); \
-	if (pm_raise_power((dip), CPUDRV_COMP_NUM, (new_level)) != \
-	    DDI_SUCCESS) { \
+		(new_spd->pm_level))); \
+	ret = pm_raise_power((dip), CPUDRV_COMP_NUM, (new_spd->pm_level)); \
+	if (ret != DDI_SUCCESS) { \
 		cmn_err(CE_WARN, "cpudrv_monitor: instance %d: can't " \
 		    "raise CPU power level", ddi_get_instance((dip))); \
 	} \
 	mutex_enter(&(cpudsp)->lock); \
+	if (ret == DDI_SUCCESS && cpudsp->cpudrv_pm.cur_spd == NULL) { \
+		cpudsp->cpudrv_pm.cur_spd = new_spd; \
+	} \
 }
 
 /*
@@ -974,7 +978,7 @@
 		DPRINTF(D_PM_MONITOR, ("cpudrv_monitor: instance %d: "
 		    "cur_spd is unknown\n", ddi_get_instance(dip)));
 		CPUDRV_MONITOR_PM_BUSY_AND_RAISE(dip, cpudsp, cpupm,
-		    CPUDRV_TOPSPEED(cpupm)->pm_level);
+		    CPUDRV_TOPSPEED(cpupm));
 		/*
 		 * We just changed the speed. Wait till at least next
 		 * call to this routine before proceeding ahead.
@@ -1079,7 +1083,7 @@
 		} else {
 			new_spd = cur_spd->up_spd;
 			CPUDRV_MONITOR_PM_BUSY_AND_RAISE(dip, cpudsp, cpupm,
-			    new_spd->pm_level);
+			    new_spd);
 		}
 	} else if ((user_cnt <= cur_spd->user_lwm) &&
 	    (idle_cnt >= cur_spd->idle_hwm) || !CPU_ACTIVE(cpudsp->cp)) {
--- a/usr/src/uts/common/io/e1000g/README	Fri Jun 05 10:27:16 2009 -0400
+++ b/usr/src/uts/common/io/e1000g/README	Fri Jun 05 10:28:40 2009 -0400
@@ -672,3 +672,8 @@
 ====
   This version has the following fixes:
    6680929 dladm should print POSSIBLE values for properties like mtu by contacting the driver.
+
+5.3.10
+====
+  This version has the following fixes:
+   6841089 Customer wants to be able to set MAX_NUM_MULTICAST_ADDRESSES above 256 on e1000g driver
--- a/usr/src/uts/common/io/e1000g/e1000_80003es2lan.c	Fri Jun 05 10:27:16 2009 -0400
+++ b/usr/src/uts/common/io/e1000g/e1000_80003es2lan.c	Fri Jun 05 10:28:40 2009 -0400
@@ -301,7 +301,7 @@
 	hw->mac.ops.init_params = e1000_init_mac_params_80003es2lan;
 	hw->nvm.ops.init_params = e1000_init_nvm_params_80003es2lan;
 	hw->phy.ops.init_params = e1000_init_phy_params_80003es2lan;
-	e1000_get_bus_info_pcie_generic(hw);
+	(void) e1000_get_bus_info_pcie_generic(hw);
 }
 
 /*
--- a/usr/src/uts/common/io/e1000g/e1000g_main.c	Fri Jun 05 10:27:16 2009 -0400
+++ b/usr/src/uts/common/io/e1000g/e1000g_main.c	Fri Jun 05 10:28:40 2009 -0400
@@ -46,7 +46,7 @@
 
 static char ident[] = "Intel PRO/1000 Ethernet";
 static char e1000g_string[] = "Intel(R) PRO/1000 Network Connection";
-static char e1000g_version[] = "Driver Ver. 5.3.9";
+static char e1000g_version[] = "Driver Ver. 5.3.10";
 
 /*
  * Proto types for DDI entry points
@@ -93,6 +93,7 @@
 static void e1000g_init_unicst(struct e1000g *);
 static int e1000g_unicst_set(struct e1000g *, const uint8_t *, int);
 static int e1000g_alloc_rx_data(struct e1000g *);
+static void e1000g_release_multicast(struct e1000g *);
 
 /*
  * Local routines
@@ -1011,6 +1012,8 @@
 		}
 	}
 
+	e1000g_release_multicast(Adapter);
+
 	if (Adapter->attach_progress & ATTACH_PROGRESS_REGS_MAP) {
 		if (Adapter->osdep.reg_handle != NULL)
 			ddi_regs_map_free(&Adapter->osdep.reg_handle);
@@ -2348,18 +2351,48 @@
 multicst_add(struct e1000g *Adapter, const uint8_t *multiaddr)
 {
 	struct e1000_hw *hw = &Adapter->shared;
+	struct ether_addr *newtable;
+	size_t new_len;
+	size_t old_len;
 	int res = 0;
 
 	if ((multiaddr[0] & 01) == 0) {
 		res = EINVAL;
+		e1000g_log(Adapter, CE_WARN, "Illegal multicast address");
 		goto done;
 	}
 
-	if (Adapter->mcast_count >= MAX_NUM_MULTICAST_ADDRESSES) {
+	if (Adapter->mcast_count >= Adapter->mcast_max_num) {
 		res = ENOENT;
+		e1000g_log(Adapter, CE_WARN,
+		    "Adapter requested more than %d mcast addresses",
+		    Adapter->mcast_max_num);
 		goto done;
 	}
 
+
+	if (Adapter->mcast_count == Adapter->mcast_alloc_count) {
+		old_len = Adapter->mcast_alloc_count *
+		    sizeof (struct ether_addr);
+		new_len = (Adapter->mcast_alloc_count + MCAST_ALLOC_SIZE) *
+		    sizeof (struct ether_addr);
+
+		newtable = kmem_alloc(new_len, KM_NOSLEEP);
+		if (newtable == NULL) {
+			res = ENOMEM;
+			e1000g_log(Adapter, CE_WARN,
+			    "Not enough memory to alloc mcast table");
+			goto done;
+		}
+
+		if (Adapter->mcast_table != NULL) {
+			bcopy(Adapter->mcast_table, newtable, old_len);
+			kmem_free(Adapter->mcast_table, old_len);
+		}
+		Adapter->mcast_alloc_count += MCAST_ALLOC_SIZE;
+		Adapter->mcast_table = newtable;
+	}
+
 	bcopy(multiaddr,
 	    &Adapter->mcast_table[Adapter->mcast_count], ETHERADDRL);
 	Adapter->mcast_count++;
@@ -2390,6 +2423,9 @@
 multicst_remove(struct e1000g *Adapter, const uint8_t *multiaddr)
 {
 	struct e1000_hw *hw = &Adapter->shared;
+	struct ether_addr *newtable;
+	size_t new_len;
+	size_t old_len;
 	unsigned i;
 
 	for (i = 0; i < Adapter->mcast_count; i++) {
@@ -2404,6 +2440,23 @@
 		}
 	}
 
+	if ((Adapter->mcast_alloc_count - Adapter->mcast_count) >
+	    MCAST_ALLOC_SIZE) {
+		old_len = Adapter->mcast_alloc_count *
+		    sizeof (struct ether_addr);
+		new_len = (Adapter->mcast_alloc_count - MCAST_ALLOC_SIZE) *
+		    sizeof (struct ether_addr);
+
+		newtable = kmem_alloc(new_len, KM_NOSLEEP);
+		if (newtable != NULL) {
+			bcopy(Adapter->mcast_table, newtable, new_len);
+			kmem_free(Adapter->mcast_table, old_len);
+
+			Adapter->mcast_alloc_count -= MCAST_ALLOC_SIZE;
+			Adapter->mcast_table = newtable;
+		}
+	}
+
 	/*
 	 * Update the MC table in the hardware
 	 */
@@ -2425,6 +2478,20 @@
 	return (0);
 }
 
+static void
+e1000g_release_multicast(struct e1000g *Adapter)
+{
+	rw_enter(&Adapter->chip_lock, RW_WRITER);
+
+	if (Adapter->mcast_table != NULL) {
+		kmem_free(Adapter->mcast_table,
+		    Adapter->mcast_alloc_count * sizeof (struct ether_addr));
+		Adapter->mcast_table = NULL;
+	}
+
+	rw_exit(&Adapter->chip_lock);
+}
+
 /*
  * e1000g_setup_multicast - setup multicast data structures
  *
@@ -2453,18 +2520,9 @@
 
 	mc_addr_list = (uint8_t *)Adapter->mcast_table;
 
-	if (Adapter->mcast_count > MAX_NUM_MULTICAST_ADDRESSES) {
-		E1000G_DEBUGLOG_1(Adapter, CE_WARN,
-		    "Adapter requested more than %d MC Addresses.\n",
-		    MAX_NUM_MULTICAST_ADDRESSES);
-		mc_addr_count = MAX_NUM_MULTICAST_ADDRESSES;
-	} else {
-		/*
-		 * Set the number of MC addresses that we are being
-		 * requested to use
-		 */
-		mc_addr_count = Adapter->mcast_count;
-	}
+	ASSERT(Adapter->mcast_count <= Adapter->mcast_max_num);
+
+	mc_addr_count = Adapter->mcast_count;
 	/*
 	 * The Wiseman 2.0 silicon has an errata by which the receiver will
 	 * hang  while writing to the receive address registers if the receiver
@@ -3702,6 +3760,13 @@
 	Adapter->mem_workaround_82546 =
 	    e1000g_get_prop(Adapter, "mem_workaround_82546",
 	    0, 1, DEFAULT_MEM_WORKAROUND_82546);
+
+	/*
+	 * Max number of multicast addresses
+	 */
+	Adapter->mcast_max_num =
+	    e1000g_get_prop(Adapter, "mcast_max_num",
+	    MIN_MCAST_NUM, MAX_MCAST_NUM, hw->mac.mta_reg_count * 32);
 }
 
 /*
--- a/usr/src/uts/common/io/e1000g/e1000g_sw.h	Fri Jun 05 10:27:16 2009 -0400
+++ b/usr/src/uts/common/io/e1000g/e1000g_sw.h	Fri Jun 05 10:28:40 2009 -0400
@@ -88,7 +88,7 @@
 
 #define	LAST_RAR_ENTRY			(E1000_RAR_ENTRIES - 1)
 #define	MAX_NUM_UNICAST_ADDRESSES	E1000_RAR_ENTRIES
-#define	MAX_NUM_MULTICAST_ADDRESSES	256
+#define	MCAST_ALLOC_SIZE		256
 
 /*
  * MAX_COOKIES = max_LSO_packet_size(65535 + ethernet_header_len)/page_size
@@ -121,6 +121,7 @@
 #define	MAX_INTR_THROTTLING		65535
 #define	MAX_RX_BCOPY_THRESHOLD		E1000_RX_BUFFER_SIZE_2K
 #define	MAX_TX_BCOPY_THRESHOLD		E1000_TX_BUFFER_SIZE_2K
+#define	MAX_MCAST_NUM			8192
 
 #define	MIN_NUM_TX_DESCRIPTOR		80
 #define	MIN_NUM_RX_DESCRIPTOR		80
@@ -134,6 +135,7 @@
 #define	MIN_INTR_THROTTLING		0
 #define	MIN_RX_BCOPY_THRESHOLD		0
 #define	MIN_TX_BCOPY_THRESHOLD		ETHERMIN
+#define	MIN_MCAST_NUM			8
 
 #define	DEFAULT_NUM_RX_DESCRIPTOR	2048
 #define	DEFAULT_NUM_TX_DESCRIPTOR	2048
@@ -921,7 +923,9 @@
 	e1000g_ether_addr_t unicst_addr[MAX_NUM_UNICAST_ADDRESSES];
 
 	uint32_t mcast_count;
-	struct ether_addr mcast_table[MAX_NUM_MULTICAST_ADDRESSES];
+	uint32_t mcast_max_num;
+	uint32_t mcast_alloc_count;
+	struct ether_addr *mcast_table;
 
 	ulong_t sys_page_sz;
 #ifdef __sparc
--- a/usr/src/uts/common/io/fibre-channel/fca/qlc/ql_api.c	Fri Jun 05 10:27:16 2009 -0400
+++ b/usr/src/uts/common/io/fibre-channel/fca/qlc/ql_api.c	Fri Jun 05 10:28:40 2009 -0400
@@ -1167,9 +1167,33 @@
 			cmn_err(CE_NOTE, "!%s(%d): Firmware not loaded",
 			    QL_NAME, ha->instance);
 		} else {
-			cmn_err(CE_NOTE, "!%s(%d): Firmware version %d.%d.%d",
-			    QL_NAME, ha->instance, ha->fw_major_version,
+			int	rval;
+			char	ver_fmt[256];
+
+			rval = (int)snprintf(ver_fmt, (size_t)sizeof (ver_fmt),
+			    "Firmware version %d.%d.%d", ha->fw_major_version,
 			    ha->fw_minor_version, ha->fw_subminor_version);
+
+			if (CFG_IST(ha, CFG_CTRL_81XX)) {
+				rval = (int)snprintf(ver_fmt + rval,
+				    (size_t)sizeof (ver_fmt),
+				    ", MPI fw version %d.%d.%d",
+				    ha->mpi_fw_major_version,
+				    ha->mpi_fw_minor_version,
+				    ha->mpi_fw_subminor_version);
+
+				if (ha->subsys_id == 0x17B ||
+				    ha->subsys_id == 0x17D) {
+					(void) snprintf(ver_fmt + rval,
+					    (size_t)sizeof (ver_fmt),
+					    ", PHY fw version %d.%d.%d",
+					    ha->phy_fw_major_version,
+					    ha->phy_fw_minor_version,
+					    ha->phy_fw_subminor_version);
+				}
+			}
+			cmn_err(CE_NOTE, "!%s(%d): %s",
+			    QL_NAME, ha->instance, ver_fmt);
 		}
 
 		ha->k_stats = kstat_create(QL_NAME, instance, "statistics",
@@ -1745,7 +1769,7 @@
 		    sizeof (*ha->outstanding_cmds) * MAX_OUTSTANDING_COMMANDS);
 
 		if (ha->n_port != NULL) {
-			kmem_free(&ha->n_port, sizeof (ql_n_port_info_t));
+			kmem_free(ha->n_port, sizeof (ql_n_port_info_t));
 		}
 
 		if (ha->devpath != NULL) {
--- a/usr/src/uts/common/io/fibre-channel/fca/qlc/ql_debug.c	Fri Jun 05 10:27:16 2009 -0400
+++ b/usr/src/uts/common/io/fibre-channel/fca/qlc/ql_debug.c	Fri Jun 05 10:28:40 2009 -0400
@@ -216,8 +216,8 @@
 	 * skipping the NULL.
 	 */
 	if (tracing) {
-		rval += 1;
-		ha->el_trace_desc->next = (uint16_t)rval;
+		uint16_t next = (uint16_t)(rval += 1);
+		ha->el_trace_desc->next += next;
 		TRACE_BUFFER_UNLOCK(ha);
 	}
 
--- a/usr/src/uts/common/io/fibre-channel/fca/qlc/ql_init.c	Fri Jun 05 10:27:16 2009 -0400
+++ b/usr/src/uts/common/io/fibre-channel/fca/qlc/ql_init.c	Fri Jun 05 10:28:40 2009 -0400
@@ -3073,6 +3073,18 @@
 			    mr.mb[7] & GID_FP_NPIV_SUPPORT) {
 				ha->flags |= FDISC_ENABLED;
 			}
+			/* Get VLAN ID, mac address */
+			if (CFG_IST(ha, CFG_CTRL_81XX)) {
+				ha->fabric_params = mr.mb[7];
+				ha->fcoe_vlan_id = (uint16_t)(mr.mb[9] & 0xfff);
+				ha->fcoe_fcf_idx = mr.mb[10];
+				ha->fcoe_vnport_mac[0] = MSB(mr.mb[11]);
+				ha->fcoe_vnport_mac[1] = LSB(mr.mb[11]);
+				ha->fcoe_vnport_mac[2] = MSB(mr.mb[12]);
+				ha->fcoe_vnport_mac[3] = LSB(mr.mb[12]);
+				ha->fcoe_vnport_mac[4] = MSB(mr.mb[13]);
+				ha->fcoe_vnport_mac[5] = LSB(mr.mb[13]);
+			}
 			break;
 		default:
 			QL_PRINT_2(CE_CONT, "(%d,%d): UNKNOWN topology=%xh, "
--- a/usr/src/uts/common/io/fibre-channel/fca/qlc/ql_xioctl.c	Fri Jun 05 10:27:16 2009 -0400
+++ b/usr/src/uts/common/io/fibre-channel/fca/qlc/ql_xioctl.c	Fri Jun 05 10:28:40 2009 -0400
@@ -87,6 +87,7 @@
 static void ql_send_els_rnid(ql_adapter_state_t *, EXT_IOCTL *, int);
 static void ql_set_host_data(ql_adapter_state_t *, EXT_IOCTL *, int);
 static void ql_get_host_data(ql_adapter_state_t *, EXT_IOCTL *, int);
+static void ql_qry_cna_port(ql_adapter_state_t *, EXT_IOCTL *, int);
 
 static int ql_lun_count(ql_adapter_state_t *, ql_tgt_t *);
 static int ql_report_lun(ql_adapter_state_t *, ql_tgt_t *);
@@ -143,6 +144,7 @@
 static void ql_reset_cmd(ql_adapter_state_t *, EXT_IOCTL *);
 static void ql_update_flash_caches(ql_adapter_state_t *);
 static void ql_get_dcbx_parameters(ql_adapter_state_t *, EXT_IOCTL *, int);
+static void ql_get_xgmac_statistics(ql_adapter_state_t *, EXT_IOCTL *, int);
 
 /* ******************************************************************** */
 /*			External IOCTL support.				*/
@@ -295,6 +297,7 @@
 	case EXT_CC_VPORT_CMD:
 	case EXT_CC_ACCESS_FLASH:
 	case EXT_CC_RESET_FW:
+	case EXT_CC_MENLO_MANAGE_INFO:
 		rval = ql_sdm_ioctl(ha, cmd, (void *)arg, mode);
 		break;
 	default:
@@ -725,6 +728,9 @@
 	case EXT_SC_QUERY_CHIP:
 		ql_qry_chip(ha, cmd, mode);
 		break;
+	case EXT_SC_QUERY_CNA_PORT:
+		ql_qry_cna_port(ha, cmd, mode);
+		break;
 	case EXT_SC_QUERY_DISC_LUN:
 	default:
 		/* function not supported. */
@@ -7950,7 +7956,16 @@
 
 	QL_PRINT_9(CE_CONT, "(%d): started\n", ha->instance);
 
-	if ((CFG_IST(ha, CFG_CTRL_MENLO)) == 0) {
+
+	/* The call is only supported for Schultz right now */
+	if (CFG_IST(ha, CFG_CTRL_81XX)) {
+		ql_get_xgmac_statistics(ha, cmd, mode);
+		QL_PRINT_9(CE_CONT, "(%d): CFG_CTRL_81XX done\n",
+		    ha->instance);
+		return;
+	}
+
+	if (!CFG_IST(ha, CFG_CTRL_81XX) || !CFG_IST(ha, CFG_CTRL_MENLO)) {
 		EL(ha, "failed, invalid request for HBA\n");
 		cmd->Status = EXT_STATUS_INVALID_REQUEST;
 		cmd->ResponseLen = 0;
@@ -8432,6 +8447,7 @@
 
 	QL_PRINT_9(CE_CONT, "(%d): done\n", ha->instance);
 }
+
 /*
  * ql_get_dcbx_parameters
  *	Get DCBX parameters.
@@ -8453,19 +8469,11 @@
 		EL(ha, "invalid request for HBA\n");
 		cmd->Status = EXT_STATUS_INVALID_REQUEST;
 		cmd->ResponseLen = 0;
-	}
-
-	if (cmd->ResponseLen < EXT_DEF_DCBX_PARAM_BUF_SIZE) {
-		cmd->Status = EXT_STATUS_BUFFER_TOO_SMALL;
-		cmd->DetailStatus = EXT_DEF_DCBX_PARAM_BUF_SIZE;
-		EL(ha, "failed, ResponseLen != %xh, Len=%xh\n",
-		    EXT_DEF_DCBX_PARAM_BUF_SIZE, cmd->ResponseLen);
-		cmd->ResponseLen = 0;
 		return;
 	}
 
 	/* Allocate memory for command. */
-	tmp_buf = kmem_zalloc(cmd->ResponseLen, KM_SLEEP);
+	tmp_buf = kmem_zalloc(EXT_DEF_DCBX_PARAM_BUF_SIZE, KM_SLEEP);
 	if (tmp_buf == NULL) {
 		EL(ha, "failed, kmem_zalloc\n");
 		cmd->Status = EXT_STATUS_NO_MEMORY;
@@ -8473,11 +8481,12 @@
 		return;
 	}
 	/* Send command */
-	rval = ql_get_dcbx_params(ha, cmd->ResponseLen, (caddr_t)tmp_buf);
+	rval = ql_get_dcbx_params(ha, EXT_DEF_DCBX_PARAM_BUF_SIZE,
+	    (caddr_t)tmp_buf);
 	if (rval != QL_SUCCESS) {
 		/* error */
-		EL(ha, "failed, get_rnid_params_mbx=%xh\n", rval);
-		kmem_free(tmp_buf, cmd->ResponseLen);
+		EL(ha, "failed, get_dcbx_params_mbx=%xh\n", rval);
+		kmem_free(tmp_buf, EXT_DEF_DCBX_PARAM_BUF_SIZE);
 		cmd->Status = EXT_STATUS_ERR;
 		cmd->ResponseLen = 0;
 		return;
@@ -8486,14 +8495,165 @@
 	/* Copy the response */
 	if (ql_send_buffer_data((caddr_t)tmp_buf,
 	    (caddr_t)(uintptr_t)cmd->ResponseAdr,
-	    cmd->ResponseLen, mode) != cmd->ResponseLen) {
+	    EXT_DEF_DCBX_PARAM_BUF_SIZE, mode) != EXT_DEF_DCBX_PARAM_BUF_SIZE) {
 		EL(ha, "failed, ddi_copyout\n");
-		kmem_free(tmp_buf, cmd->ResponseLen);
 		cmd->Status = EXT_STATUS_COPY_ERR;
 		cmd->ResponseLen = 0;
 	} else {
+		cmd->ResponseLen = EXT_DEF_DCBX_PARAM_BUF_SIZE;
 		QL_PRINT_9(CE_CONT, "(%d): done\n", ha->instance);
-		kmem_free(tmp_buf, cmd->ResponseLen);
-	}
+	}
+	kmem_free(tmp_buf, EXT_DEF_DCBX_PARAM_BUF_SIZE);
 
 }
+
+/*
+ * ql_qry_cna_port
+ *	Performs EXT_SC_QUERY_CNA_PORT subfunction.
+ *
+ * Input:
+ *	ha:	adapter state pointer.
+ *	cmd:	EXT_IOCTL cmd struct pointer.
+ *	mode:	flags.
+ *
+ * Returns:
+ *	None, request status indicated in cmd->Status.
+ *
+ * Context:
+ *	Kernel context.
+ */
+static void
+ql_qry_cna_port(ql_adapter_state_t *ha, EXT_IOCTL *cmd, int mode)
+{
+	EXT_CNA_PORT	cna_port = {0};
+
+	QL_PRINT_9(CE_CONT, "(%d): started\n", ha->instance);
+
+	if (!(CFG_IST(ha, CFG_CTRL_81XX))) {
+		EL(ha, "invalid request for HBA\n");
+		cmd->Status = EXT_STATUS_INVALID_REQUEST;
+		cmd->ResponseLen = 0;
+		return;
+	}
+
+	if (cmd->ResponseLen < sizeof (EXT_CNA_PORT)) {
+		cmd->Status = EXT_STATUS_BUFFER_TOO_SMALL;
+		cmd->DetailStatus = sizeof (EXT_CNA_PORT);
+		EL(ha, "failed, ResponseLen < EXT_CNA_PORT, Len=%xh\n",
+		    cmd->ResponseLen);
+		cmd->ResponseLen = 0;
+		return;
+	}
+
+	cna_port.VLanId = ha->fcoe_vlan_id;
+	cna_port.FabricParam = ha->fabric_params;
+	bcopy(ha->fcoe_vnport_mac, cna_port.VNPortMACAddress,
+	    EXT_DEF_MAC_ADDRESS_SIZE);
+
+	if (ddi_copyout((void *)&cna_port,
+	    (void *)(uintptr_t)(cmd->ResponseAdr),
+	    sizeof (EXT_CNA_PORT), mode) != 0) {
+		cmd->Status = EXT_STATUS_COPY_ERR;
+		cmd->ResponseLen = 0;
+		EL(ha, "failed, ddi_copyout\n");
+	} else {
+		cmd->ResponseLen = sizeof (EXT_CNA_PORT);
+		QL_PRINT_9(CE_CONT, "(%d): done\n", ha->instance);
+	}
+}
+
+/*
+ * ql_get_xgmac_statistics
+ *	Get XgMac information
+ *
+ * Input:
+ *	ha:	adapter state pointer.
+ *	cmd:	EXT_IOCTL cmd struct pointer.
+ *	mode:	flags.
+ *
+ * Returns:
+ *	None, request status indicated in cmd->Status.
+ *
+ * Context:
+ *	Kernel context.
+ */
+static void
+ql_get_xgmac_statistics(ql_adapter_state_t *ha, EXT_IOCTL *cmd, int mode)
+{
+	int			rval;
+	uint32_t		size;
+	int8_t			*tmp_buf;
+	EXT_MENLO_MANAGE_INFO	info;
+
+	QL_PRINT_9(CE_CONT, "(%d): started\n", ha->instance);
+
+	/*  Verify the size of request structure. */
+	if (cmd->RequestLen < sizeof (EXT_MENLO_MANAGE_INFO)) {
+		/* Return error */
+		EL(ha, "RequestLen=%d < %d\n", cmd->RequestLen,
+		    sizeof (EXT_MENLO_MANAGE_INFO));
+		cmd->Status = EXT_STATUS_INVALID_PARAM;
+		cmd->DetailStatus = EXT_DSTATUS_REQUEST_LEN;
+		cmd->ResponseLen = 0;
+		return;
+	}
+
+	/* Get manage info request. */
+	if (ddi_copyin((caddr_t)(uintptr_t)cmd->RequestAdr,
+	    (caddr_t)&info, sizeof (EXT_MENLO_MANAGE_INFO), mode) != 0) {
+		EL(ha, "failed, ddi_copyin\n");
+		cmd->Status = EXT_STATUS_COPY_ERR;
+		cmd->ResponseLen = 0;
+		return;
+	}
+
+	size = info.TotalByteCount;
+	if (!size) {
+		/* parameter error */
+		cmd->Status = EXT_STATUS_INVALID_PARAM;
+		cmd->DetailStatus = 0;
+		EL(ha, "failed, size=%xh\n", size);
+		cmd->ResponseLen = 0;
+		return;
+	}
+
+	/* Allocate memory for command. */
+	tmp_buf = kmem_zalloc(size, KM_SLEEP);
+	if (tmp_buf == NULL) {
+		EL(ha, "failed, kmem_zalloc\n");
+		cmd->Status = EXT_STATUS_NO_MEMORY;
+		cmd->ResponseLen = 0;
+		return;
+	}
+
+	if (!(info.Operation & MENLO_OP_GET_INFO)) {
+		EL(ha, "Invalid request for 81XX\n");
+		kmem_free(tmp_buf, size);
+		cmd->Status = EXT_STATUS_ERR;
+		cmd->ResponseLen = 0;
+		return;
+	}
+
+	rval = ql_get_xgmac_stats(ha, size, (caddr_t)tmp_buf);
+
+	if (rval != QL_SUCCESS) {
+		/* error */
+		EL(ha, "failed, get_xgmac_stats =%xh\n", rval);
+		kmem_free(tmp_buf, size);
+		cmd->Status = EXT_STATUS_ERR;
+		cmd->ResponseLen = 0;
+		return;
+	}
+
+	if (ql_send_buffer_data(tmp_buf, (caddr_t)(uintptr_t)info.pDataBytes,
+	    size, mode) != size) {
+		EL(ha, "failed, ddi_copyout\n");
+		cmd->Status = EXT_STATUS_COPY_ERR;
+		cmd->ResponseLen = 0;
+	} else {
+		cmd->ResponseLen = info.TotalByteCount;
+		QL_PRINT_9(CE_CONT, "(%d): done\n", ha->instance);
+	}
+	kmem_free(tmp_buf, size);
+	QL_PRINT_9(CE_CONT, "(%d): done\n", ha->instance);
+}
--- a/usr/src/uts/common/io/ib/adapters/hermon/hermon.c	Fri Jun 05 10:27:16 2009 -0400
+++ b/usr/src/uts/common/io/ib/adapters/hermon/hermon.c	Fri Jun 05 10:28:40 2009 -0400
@@ -56,8 +56,10 @@
 /* Hermon HCA State Pointer */
 void *hermon_statep;
 
-int	debug_vpd = 0;
-
+int debug_vpd = 0;
+
+/* Disable the internal error-check polling thread */
+int hermon_no_inter_err_chk = 0;
 
 /*
  * The Hermon "userland resource database" is common to instances of the
@@ -1659,8 +1661,11 @@
 	/*
 	 * Invoke a polling thread to check the error buffer periodically.
 	 */
-	state->hs_fm_poll_thread = ddi_periodic_add(hermon_inter_err_chk,
-	    (void *)state, FM_POLL_INTERVAL, DDI_IPL_0);
+	if (!hermon_no_inter_err_chk) {
+		state->hs_fm_poll_thread = ddi_periodic_add(
+		    hermon_inter_err_chk, (void *)state, FM_POLL_INTERVAL,
+		    DDI_IPL_0);
+	}
 
 	cleanup = HERMON_DRV_CLEANUP_LEVEL5;
 
@@ -4622,6 +4627,11 @@
 	/* start fastreboot */
 	state->hs_quiescing = B_TRUE;
 
+	/* If it's in maintenance mode, do nothing but return with SUCCESS */
+	if (!HERMON_IS_OPERATIONAL(state->hs_operational_mode)) {
+		return (DDI_SUCCESS);
+	}
+
 	/* suppress Hermon FM ereports */
 	if (hermon_get_state(state) & HCA_EREPORT_FM) {
 		hermon_clr_state_nolock(state, HCA_EREPORT_FM);
--- a/usr/src/uts/common/io/ib/adapters/hermon/hermon_fm.c	Fri Jun 05 10:27:16 2009 -0400
+++ b/usr/src/uts/common/io/ib/adapters/hermon/hermon_fm.c	Fri Jun 05 10:28:40 2009 -0400
@@ -926,6 +926,10 @@
 	if (!(hermon_get_state(state) & HCA_PIO_FM))
 		return (B_FALSE);
 
+	/* check if fatal errors occur during attach */
+	if (state->hs_fm_async_fatal)
+		return (B_TRUE);
+
 	hdl = hermon_get_uarhdl(state);
 	/* Get the PIO error against UAR I/O space */
 	ddi_fm_acc_err_get(hdl, &derr, DDI_FME_VERSION);
@@ -1448,7 +1452,14 @@
 
 	if (word != 0) {
 		HERMON_FMANOTE(state, HERMON_FMA_INTERNAL);
-		hermon_fm_ereport(state, HCA_IBA_ERR, HCA_ERR_FATAL);
+		/* if fm_disable is on, Hermon FM functions don't work */
+		if (state->hs_fm_disable) {
+			cmn_err(CE_PANIC,
+			    "Hermon Fatal Internal Error. "
+			    "Hermon state=0x%p", (void *)state);
+		} else {
+			hermon_fm_ereport(state, HCA_IBA_ERR, HCA_ERR_FATAL);
+		}
 	}
 
 	/* issue the ereport pended in the interrupt context */
--- a/usr/src/uts/common/io/ib/adapters/tavor/tavor.c	Fri Jun 05 10:27:16 2009 -0400
+++ b/usr/src/uts/common/io/ib/adapters/tavor/tavor.c	Fri Jun 05 10:28:40 2009 -0400
@@ -3489,6 +3489,11 @@
 	/* start fastreboot */
 	state->ts_quiescing = B_TRUE;
 
+	/* If it's in maintenance mode, do nothing but return with SUCCESS */
+	if (!TAVOR_IS_OPERATIONAL(state->ts_operational_mode)) {
+		return (DDI_SUCCESS);
+	}
+
 	/* Shutdown HCA ports */
 	if (tavor_hca_ports_shutdown(state,
 	    state->ts_cfg_profile->cp_num_ports) != TAVOR_CMD_SUCCESS) {
--- a/usr/src/uts/common/io/ib/ibnex/ibnex.c	Fri Jun 05 10:27:16 2009 -0400
+++ b/usr/src/uts/common/io/ib/ibnex/ibnex.c	Fri Jun 05 10:28:40 2009 -0400
@@ -1735,14 +1735,14 @@
 			    ndp; ndp = ndp->node_next) {
 				dip = ndp->node_dip;
 				if (dip && (ddi_driver_major(dip) == major)) {
-					ibnex_offline_childdip(dip);
+					(void) ibnex_offline_childdip(dip);
 				}
 			}
 			for (ndp = ibnex.ibnex_pseudo_node_head;
 			    ndp; ndp = ndp->node_next) {
 				dip = ndp->node_dip;
 				if (dip && (ddi_driver_major(dip) == major)) {
-					ibnex_offline_childdip(dip);
+					(void) ibnex_offline_childdip(dip);
 				}
 			}
 		}
--- a/usr/src/uts/common/io/ib/ibnex/ibnex_ioctl.c	Fri Jun 05 10:27:16 2009 -0400
+++ b/usr/src/uts/common/io/ib/ibnex/ibnex_ioctl.c	Fri Jun 05 10:28:40 2009 -0400
@@ -2625,7 +2625,7 @@
 int
 ibnex_offline_childdip(dev_info_t *dip)
 {
-	int		rval = MDI_SUCCESS;
+	int		rval = MDI_SUCCESS, rval2;
 	mdi_pathinfo_t	*path = NULL, *temp;
 
 	IBTF_DPRINTF_L4("ibnex", "\toffline_childdip; begin");
@@ -2637,15 +2637,25 @@
 	for (path = mdi_get_next_phci_path(dip, path); path; ) {
 		IBTF_DPRINTF_L4("ibnex", "\toffline_childdip: "
 		    "offling path %p", path);
-		rval = mdi_pi_offline(path, NDI_UNCONFIG);
-		if (rval != MDI_SUCCESS) {
-			IBTF_DPRINTF_L2("ibnex", "\toffline_childdip: "
-			    "mdi_pi_offline failed %p", dip);
-			break;
+		rval2 = MDI_SUCCESS;
+		if (MDI_PI_IS_ONLINE(path)) {
+			rval2 = mdi_pi_offline(path, NDI_UNCONFIG);
+			/* If it cannot be offlined, log this path and error */
+			if (rval2 != MDI_SUCCESS) {
+				rval = rval2;
+				cmn_err(CE_WARN,
+				    "!ibnex\toffline_childdip (0x%p): "
+				    "mdi_pi_offline path (0x%p) failed with %d",
+				    (void *)dip, (void *)path, rval2);
+			}
 		}
+		/* prepare the next path */
 		temp = path;
 		path = mdi_get_next_phci_path(dip, path);
-		(void) mdi_pi_free(temp, 0);
+		/* free the offline path */
+		if (rval2 == MDI_SUCCESS) {
+			(void) mdi_pi_free(temp, 0);
+		}
 	}
 	return (rval);
 }
--- a/usr/src/uts/common/io/ib/mgt/ibmf/ibmf_saa_impl.c	Fri Jun 05 10:27:16 2009 -0400
+++ b/usr/src/uts/common/io/ib/mgt/ibmf/ibmf_saa_impl.c	Fri Jun 05 10:28:40 2009 -0400
@@ -74,6 +74,8 @@
 
 int	ibmf_saa_max_wait_time = IBMF_SAA_MAX_WAIT_TIME_IN_SECS;
 int	ibmf_saa_trans_wait_time = IBMF_SAA_TRANS_WAIT_TIME_IN_SECS;
+int	ibmf_saa_max_resp_time = IBMF_SAA_MAX_RESP_TIME;
+int	ibmf_saa_max_subnet_timeout = IBMF_SAA_MAX_SUBNET_TIMEOUT;
 
 /*
  * ibmf_saa_impl_init:
@@ -1257,6 +1259,21 @@
 
 		resp_time_value = classportinfo->RespTimeValue & 0x1f;
 
+		/*
+		 * Because some subnet managers might not provide sane
+		 * value for "resp_time_value", we limit it here.  In
+		 * case this limit is too restrictive (very large fabric),
+		 * we allow the limit to be raised (/etc/system).
+		 */
+		if (resp_time_value > ibmf_saa_max_resp_time) {
+			cmn_err(CE_CONT, "!ibmf_saa_max_resp_time (%d) "
+			    "exceeded.", ibmf_saa_max_resp_time);
+			cmn_err(CE_CONT, "!Reducing subnet administrator "
+			    "resp_time value from %d to %d.",
+			    resp_time_value, ibmf_saa_max_resp_time);
+			resp_time_value = ibmf_saa_max_resp_time;
+		}
+
 		sa_cap_mask = classportinfo->CapabilityMask;
 
 		IBMF_TRACE_3(IBMF_TNF_DEBUG, DPRINT_L3,
@@ -2028,8 +2045,8 @@
 		saa_portp->saa_pt_timeout = subnet_timeout;
 
 		/* place upper bound on subnet timeout in case of faulty SM */
-		if (saa_portp->saa_pt_timeout > IBMF_SAA_MAX_SUBNET_TIMEOUT)
-			saa_portp->saa_pt_timeout = IBMF_SAA_MAX_SUBNET_TIMEOUT;
+		if (saa_portp->saa_pt_timeout > ibmf_saa_max_subnet_timeout)
+			saa_portp->saa_pt_timeout = ibmf_saa_max_subnet_timeout;
 
 		/* increment the reference count to account for the cpi call */
 		saa_portp->saa_pt_reference_count++;
@@ -2173,8 +2190,8 @@
 	saa_portp->saa_pt_timeout = subnet_timeout;
 
 	/* place upper bound on subnet timeout in case of faulty SM */
-	if (saa_portp->saa_pt_timeout > IBMF_SAA_MAX_SUBNET_TIMEOUT)
-		saa_portp->saa_pt_timeout = IBMF_SAA_MAX_SUBNET_TIMEOUT;
+	if (saa_portp->saa_pt_timeout > ibmf_saa_max_subnet_timeout)
+		saa_portp->saa_pt_timeout = ibmf_saa_max_subnet_timeout;
 
 	/* increment the reference count to account for the cpi call */
 	saa_portp->saa_pt_reference_count++;
@@ -3508,10 +3525,8 @@
 	/* place upper bound on subnet timeout in case of faulty SM */
 	saa_portp->saa_pt_timeout = portinfop->p_subnet_timeout;
 
-	if (saa_portp->saa_pt_timeout > IBMF_SAA_MAX_SUBNET_TIMEOUT) {
-
-		saa_portp->saa_pt_timeout = IBMF_SAA_MAX_SUBNET_TIMEOUT;
-	}
+	if (saa_portp->saa_pt_timeout > ibmf_saa_max_subnet_timeout)
+		saa_portp->saa_pt_timeout = ibmf_saa_max_subnet_timeout;
 
 	IBMF_TRACE_4(IBMF_TNF_DEBUG, DPRINT_L3,
 	    ibmf_saa_impl_set_transaction_params,
--- a/usr/src/uts/common/io/igb/igb_main.c	Fri Jun 05 10:27:16 2009 -0400
+++ b/usr/src/uts/common/io/igb/igb_main.c	Fri Jun 05 10:28:40 2009 -0400
@@ -29,7 +29,7 @@
 #include "igb_sw.h"
 
 static char ident[] = "Intel 1Gb Ethernet";
-static char igb_version[] = "igb 1.1.6";
+static char igb_version[] = "igb 1.1.7";
 
 /*
  * Local function protoypes
@@ -112,7 +112,7 @@
     const void *);
 static void igb_fm_init(igb_t *);
 static void igb_fm_fini(igb_t *);
-
+static void igb_release_multicast(igb_t *);
 
 static struct cb_ops igb_cb_ops = {
 	nulldev,		/* cb_open */
@@ -691,6 +691,11 @@
 	}
 
 	/*
+	 * Free multicast table
+	 */
+	igb_release_multicast(igb);
+
+	/*
 	 * Free register handle
 	 */
 	if (igb->attach_progress & ATTACH_PROGRESS_REGS_MAP) {
@@ -2438,16 +2443,44 @@
 int
 igb_multicst_add(igb_t *igb, const uint8_t *multiaddr)
 {
+	struct ether_addr *new_table;
+	size_t new_len;
+	size_t old_len;
+
 	ASSERT(mutex_owned(&igb->gen_lock));
 
 	if ((multiaddr[0] & 01) == 0) {
+		igb_error(igb, "Illegal multicast address");
 		return (EINVAL);
 	}
 
-	if (igb->mcast_count >= MAX_NUM_MULTICAST_ADDRESSES) {
+	if (igb->mcast_count >= igb->mcast_max_num) {
+		igb_error(igb, "Adapter requested more than %d mcast addresses",
+		    igb->mcast_max_num);
 		return (ENOENT);
 	}
 
+	if (igb->mcast_count == igb->mcast_alloc_count) {
+		old_len = igb->mcast_alloc_count *
+		    sizeof (struct ether_addr);
+		new_len = (igb->mcast_alloc_count + MCAST_ALLOC_COUNT) *
+		    sizeof (struct ether_addr);
+
+		new_table = kmem_alloc(new_len, KM_NOSLEEP);
+		if (new_table == NULL) {
+			igb_error(igb,
+			    "Not enough memory to alloc mcast table");
+			return (ENOMEM);
+		}
+
+		if (igb->mcast_table != NULL) {
+			bcopy(igb->mcast_table, new_table, old_len);
+			kmem_free(igb->mcast_table, old_len);
+		}
+		igb->mcast_alloc_count += MCAST_ALLOC_COUNT;
+		igb->mcast_table = new_table;
+	}
+
 	bcopy(multiaddr,
 	    &igb->mcast_table[igb->mcast_count], ETHERADDRL);
 	igb->mcast_count++;
@@ -2471,6 +2504,9 @@
 int
 igb_multicst_remove(igb_t *igb, const uint8_t *multiaddr)
 {
+	struct ether_addr *new_table;
+	size_t new_len;
+	size_t old_len;
 	int i;
 
 	ASSERT(mutex_owned(&igb->gen_lock));
@@ -2487,6 +2523,22 @@
 		}
 	}
 
+	if ((igb->mcast_alloc_count - igb->mcast_count) >
+	    MCAST_ALLOC_COUNT) {
+		old_len = igb->mcast_alloc_count *
+		    sizeof (struct ether_addr);
+		new_len = (igb->mcast_alloc_count - MCAST_ALLOC_COUNT) *
+		    sizeof (struct ether_addr);
+
+		new_table = kmem_alloc(new_len, KM_NOSLEEP);
+		if (new_table != NULL) {
+			bcopy(igb->mcast_table, new_table, new_len);
+			kmem_free(igb->mcast_table, old_len);
+			igb->mcast_alloc_count -= MCAST_ALLOC_COUNT;
+			igb->mcast_table = new_table;
+		}
+	}
+
 	/*
 	 * Update the multicast table in the hardware
 	 */
@@ -2500,6 +2552,16 @@
 	return (0);
 }
 
+static void
+igb_release_multicast(igb_t *igb)
+{
+	if (igb->mcast_table != NULL) {
+		kmem_free(igb->mcast_table,
+		    igb->mcast_alloc_count * sizeof (struct ether_addr));
+		igb->mcast_table = NULL;
+	}
+}
+
 /*
  * igb_setup_multicast - setup multicast data structures
  *
@@ -2514,8 +2576,7 @@
 	struct e1000_hw *hw = &igb->hw;
 
 	ASSERT(mutex_owned(&igb->gen_lock));
-
-	ASSERT(igb->mcast_count <= MAX_NUM_MULTICAST_ADDRESSES);
+	ASSERT(igb->mcast_count <= igb->mcast_max_num);
 
 	mc_addr_list = (uint8_t *)igb->mcast_table;
 	mc_addr_count = igb->mcast_count;
@@ -2731,6 +2792,13 @@
 	    igb->capab->min_intr_throttle,
 	    igb->capab->max_intr_throttle,
 	    igb->capab->def_intr_throttle);
+
+	/*
+	 * Max number of multicast addresses
+	 */
+	igb->mcast_max_num =
+	    igb_get_prop(igb, PROP_MCAST_MAX_NUM,
+	    MIN_MCAST_NUM, MAX_MCAST_NUM, DEFAULT_MCAST_NUM);
 }
 
 /*
--- a/usr/src/uts/common/io/igb/igb_sw.h	Fri Jun 05 10:27:16 2009 -0400
+++ b/usr/src/uts/common/io/igb/igb_sw.h	Fri Jun 05 10:28:40 2009 -0400
@@ -94,7 +94,7 @@
 #define	IGB_NO_FREE_SLOT		-1
 
 #define	MAX_NUM_UNICAST_ADDRESSES	E1000_RAR_ENTRIES
-#define	MAX_NUM_MULTICAST_ADDRESSES	256
+#define	MCAST_ALLOC_COUNT		256
 #define	MAX_COOKIE			18
 #define	MIN_NUM_TX_DESC			2
 
@@ -124,6 +124,7 @@
 #define	MAX_TX_RECYCLE_THRESHOLD	DEFAULT_TX_RING_SIZE
 #define	MAX_TX_OVERLOAD_THRESHOLD	DEFAULT_TX_RING_SIZE
 #define	MAX_TX_RESCHED_THRESHOLD	DEFAULT_TX_RING_SIZE
+#define	MAX_MCAST_NUM			8192
 
 /*
  * Minimum values for user configurable parameters
@@ -143,6 +144,7 @@
 #define	MIN_TX_RECYCLE_THRESHOLD	MIN_NUM_TX_DESC
 #define	MIN_TX_OVERLOAD_THRESHOLD	MIN_NUM_TX_DESC
 #define	MIN_TX_RESCHED_THRESHOLD	MIN_NUM_TX_DESC
+#define	MIN_MCAST_NUM			8
 
 /*
  * Default values for user configurable parameters
@@ -162,6 +164,7 @@
 #define	DEFAULT_TX_RECYCLE_THRESHOLD	(MAX_COOKIE + 1)
 #define	DEFAULT_TX_OVERLOAD_THRESHOLD	MIN_NUM_TX_DESC
 #define	DEFAULT_TX_RESCHED_THRESHOLD	128
+#define	DEFAULT_MCAST_NUM		4096
 
 #define	IGB_LSO_MAXLEN			65535
 
@@ -220,6 +223,7 @@
 #define	PROP_RX_COPY_THRESHOLD		"rx_copy_threshold"
 #define	PROP_RX_LIMIT_PER_INTR		"rx_limit_per_intr"
 #define	PROP_INTR_THROTTLING		"intr_throttling"
+#define	PROP_MCAST_MAX_NUM		"mcast_max_num"
 
 #define	IGB_LB_NONE			0
 #define	IGB_LB_EXTERNAL			1
@@ -730,7 +734,9 @@
 	uint32_t		unicst_total;
 	igb_ether_addr_t	unicst_addr[MAX_NUM_UNICAST_ADDRESSES];
 	uint32_t		mcast_count;
-	struct ether_addr	mcast_table[MAX_NUM_MULTICAST_ADDRESSES];
+	uint32_t		mcast_alloc_count;
+	uint32_t		mcast_max_num;
+	struct ether_addr	*mcast_table;
 
 	/*
 	 * Kstat definitions
--- a/usr/src/uts/common/io/mr_sas/mr_sas.c	Fri Jun 05 10:27:16 2009 -0400
+++ b/usr/src/uts/common/io/mr_sas/mr_sas.c	Fri Jun 05 10:28:40 2009 -0400
@@ -64,6 +64,7 @@
 #include <sys/sunddi.h>
 #include <sys/atomic.h>
 #include <sys/signal.h>
+#include <sys/byteorder.h>
 #include <sys/fs/dv_node.h>	/* devfs_clean */
 
 #include "mr_sas.h"
@@ -81,6 +82,7 @@
  */
 static void	*mrsas_state = NULL;
 static int 	debug_level_g = CL_NONE;
+boolean_t mrsas_relaxed_ordering = B_TRUE;
 
 #pragma weak scsi_hba_open
 #pragma weak scsi_hba_close
@@ -583,6 +585,11 @@
 			tran->tran_sync_pkt	= mrsas_tran_sync_pkt;
 			tran->tran_bus_config	= mrsas_tran_bus_config;
 
+			if (mrsas_relaxed_ordering)
+				mrsas_generic_dma_attr.dma_attr_flags |=
+				    DDI_DMA_RELAXED_ORDERING;
+
+
 			tran_dma_attr = mrsas_generic_dma_attr;
 			tran_dma_attr.dma_attr_sgllen = instance->max_num_sge;
 
@@ -937,7 +944,6 @@
 	struct mrsas_instance	*instance;
 	struct mrsas_ioctl	*ioctl;
 	struct mrsas_aen	aen;
-	int i;
 	con_log(CL_ANN1, (CE_NOTE, "chkpnt:%s:%d", __func__, __LINE__));
 
 	instance = ddi_get_soft_state(mrsas_state, MINOR2INST(getminor(dev)));
@@ -954,56 +960,44 @@
 
 	switch ((uint_t)cmd) {
 		case MRSAS_IOCTL_FIRMWARE:
-			for (i = 0; i < sizeof (struct mrsas_ioctl); i++) {
-				if (ddi_copyin((uint8_t *)arg+i,
-				    (uint8_t *)ioctl+i, 1, mode)) {
-					con_log(CL_ANN, (CE_WARN, "mrsas_ioctl "
-					    "ERROR IOCTL copyin"));
-					kmem_free(ioctl,
-					    sizeof (struct mrsas_ioctl));
-					return (EFAULT);
-				}
+			if (ddi_copyin((void *)arg, ioctl,
+			    sizeof (struct mrsas_ioctl), mode)) {
+				con_log(CL_ANN, (CE_WARN, "mrsas_ioctl: "
+				    "ERROR IOCTL copyin"));
+				kmem_free(ioctl, sizeof (struct mrsas_ioctl));
+				return (EFAULT);
 			}
+
 			if (ioctl->control_code == MRSAS_DRIVER_IOCTL_COMMON) {
 				rval = handle_drv_ioctl(instance, ioctl, mode);
 			} else {
 				rval = handle_mfi_ioctl(instance, ioctl, mode);
 			}
-			for (i = 0; i < sizeof (struct mrsas_ioctl) - 1; i++) {
-				if (ddi_copyout((uint8_t *)ioctl+i,
-				    (uint8_t *)arg+i, 1, mode)) {
-					con_log(CL_ANN, (CE_WARN,
-					    "mrsas_ioctl: ddi_copyout "
-					    "failed"));
-					rval = 1;
-					break;
-				}
+
+			if (ddi_copyout((void *)ioctl, (void *)arg,
+			    (sizeof (struct mrsas_ioctl) - 1), mode)) {
+				con_log(CL_ANN, (CE_WARN,
+				    "mrsas_ioctl: copy_to_user failed"));
+				rval = 1;
 			}
 
 			break;
 		case MRSAS_IOCTL_AEN:
-			for (i = 0; i < sizeof (struct mrsas_aen); i++) {
-				if (ddi_copyin((uint8_t *)arg+i,
-				    (uint8_t *)&aen+i, 1, mode)) {
-					con_log(CL_ANN, (CE_WARN,
-					    "mrsas_ioctl: "
-					    "ERROR AEN copyin"));
-					kmem_free(ioctl,
-					    sizeof (struct mrsas_ioctl));
-					return (EFAULT);
-				}
+			if (ddi_copyin((void *) arg, &aen,
+			    sizeof (struct mrsas_aen), mode)) {
+				con_log(CL_ANN, (CE_WARN,
+				    "mrsas_ioctl: ERROR AEN copyin"));
+				kmem_free(ioctl, sizeof (struct mrsas_ioctl));
+				return (EFAULT);
 			}
 
 			rval = handle_mfi_aen(instance, &aen);
-			for (i = 0; i < sizeof (struct mrsas_aen); i++) {
-				if (ddi_copyout((uint8_t *)&aen + i,
-				    (uint8_t *)arg + i, 1, mode)) {
-					con_log(CL_ANN, (CE_WARN,
-					    "mrsas_ioctl: "
-					    "ddi_copyout failed"));
-					rval = 1;
-					break;
-				}
+
+			if (ddi_copyout((void *) &aen, (void *)arg,
+			    sizeof (struct mrsas_aen), mode)) {
+				con_log(CL_ANN, (CE_WARN,
+				    "mrsas_ioctl: copy_to_user failed"));
+				rval = 1;
 			}
 
 			break;
@@ -1562,7 +1556,7 @@
 	    != DDI_SUCCESS) {
 		mrsas_fm_ereport(instance, DDI_FM_DEVICE_NO_RESPONSE);
 		ddi_fm_service_impact(instance->dip, DDI_SERVICE_LOST);
-		return (DDI_INTR_UNCLAIMED);
+		return (DDI_INTR_CLAIMED);
 	}
 
 	producer = ddi_get32(instance->mfi_internal_dma_obj.acc_handle,
@@ -1574,7 +1568,7 @@
 	    producer, consumer));
 	if (producer == consumer) {
 		con_log(CL_ANN1, (CE_WARN, "producer = consumer case"));
-		return (DDI_INTR_UNCLAIMED);
+		return (DDI_INTR_CLAIMED);
 	}
 	mutex_enter(&instance->completed_pool_mtx);
 
@@ -1706,7 +1700,6 @@
 	uint16_t	sge_sz;
 	uint32_t	sgl_sz;
 	uint32_t	tot_frame_size;
-
 	struct mrsas_cmd	*cmd;
 
 	max_cmd = instance->max_fw_cmds;
@@ -2015,9 +2008,15 @@
 
 	if (!instance->func_ptr->issue_cmd_in_poll_mode(instance, cmd)) {
 		ret = 0;
+		ctrl_info->max_request_size = ddi_get32(
+		    cmd->frame_dma_obj.acc_handle, &ci->max_request_size);
+		ctrl_info->ld_present_count = ddi_get16(
+		    cmd->frame_dma_obj.acc_handle, &ci->ld_present_count);
 		ddi_rep_get8(cmd->frame_dma_obj.acc_handle,
-		    (uint8_t *)ctrl_info, (uint8_t *)ci,
-		    sizeof (struct mrsas_ctrl_info), DDI_DEV_AUTOINCR);
+		    (uint8_t *)(ctrl_info->product_name),
+		    (uint8_t *)(ci->product_name), 80 * sizeof (char),
+		    DDI_DEV_AUTOINCR);
+		/* should get more members of ci with ddi_get when needed */
 	} else {
 		con_log(CL_ANN, (CE_WARN, "get_ctrl_info: Ctrl info failed"));
 		ret = -1;
@@ -2382,7 +2381,7 @@
 	dma_obj_t			dcmd_dma_obj;
 	struct mrsas_cmd		*cmd;
 	struct mrsas_dcmd_frame		*dcmd;
-
+	struct mrsas_evt_log_info *eli_tmp;
 	cmd = get_mfi_pkt(instance);
 
 	if (!cmd) {
@@ -2439,10 +2438,9 @@
 		    "failed to issue MRSAS_DCMD_CTRL_EVENT_GET_INFO");
 		ret = DDI_FAILURE;
 	} else {
-		/* copy the data back into callers buffer */
-		ddi_rep_get8(cmd->frame_dma_obj.acc_handle, (uint8_t *)eli,
-		    (uint8_t *)dcmd_dma_obj.buffer,
-		    sizeof (struct mrsas_evt_log_info), DDI_DEV_AUTOINCR);
+		eli_tmp = (struct mrsas_evt_log_info *)dcmd_dma_obj.buffer;
+		eli->newest_seq_num = ddi_get32(cmd->frame_dma_obj.acc_handle,
+		    &eli_tmp->newest_seq_num);
 		ret = DDI_SUCCESS;
 	}
 
@@ -2477,8 +2475,9 @@
 
 	/* register AEN with FW for latest sequence number plus 1 */
 	class_locale.members.reserved	= 0;
-	class_locale.members.locale	= MR_EVT_LOCALE_ALL;
+	class_locale.members.locale	= LE_16(MR_EVT_LOCALE_ALL);
 	class_locale.members.class	= MR_EVT_CLASS_INFO;
+	class_locale.word	= LE_32(class_locale.word);
 	ret = register_mfi_aen(instance, eli.newest_seq_num + 1,
 	    class_locale.word);
 
@@ -2679,7 +2678,7 @@
 
 	if (mlist_empty(&instance->completed_pool_list)) {
 		mutex_exit(&instance->completed_pool_mtx);
-		return (DDI_INTR_UNCLAIMED);
+		return (DDI_INTR_CLAIMED);
 	}
 
 	instance->softint_running = 1;
@@ -2702,7 +2701,7 @@
 		    DDI_SUCCESS) {
 			mrsas_fm_ereport(instance, DDI_FM_DEVICE_NO_RESPONSE);
 			ddi_fm_service_impact(instance->dip, DDI_SERVICE_LOST);
-			return (DDI_INTR_UNCLAIMED);
+			return (DDI_INTR_CLAIMED);
 		}
 
 		hdr = &cmd->frame->hdr;
@@ -2822,7 +2821,7 @@
 					    acmd->cmd_scblen -
 					    offsetof(struct scsi_arq_status,
 					    sts_sensedata), DDI_DEV_AUTOINCR);
-				}
+			}
 				break;
 			case MFI_STAT_LD_OFFLINE:
 			case MFI_STAT_DEVICE_NOT_FOUND:
@@ -3896,7 +3895,7 @@
 	model = ddi_model_convert_from(mode & FMODELS);
 	if (model == DDI_MODEL_ILP32) {
 		con_log(CL_ANN1, (CE_NOTE,
-		    "handle_drv_ioctl: DDI_MODEL_ILP32"));
+		    "issue_mfi_smp: DDI_MODEL_ILP32"));
 
 		sge32 = &smp->sgl[0].sge32[0];
 		ddi_put32(acc_handle, &sge32[0].length, response_xferlen);
@@ -3908,7 +3907,7 @@
 	} else {
 #ifdef _ILP32
 		con_log(CL_ANN1, (CE_NOTE,
-		    "handle_drv_ioctl: DDI_MODEL_ILP32"));
+		    "issue_mfi_smp: DDI_MODEL_ILP32"));
 		sge32 = &smp->sgl[0].sge32[0];
 		ddi_put32(acc_handle, &sge32[0].length, response_xferlen);
 		ddi_put32(acc_handle, &sge32[0].phys_addr,
@@ -4252,19 +4251,16 @@
 		    "MRSAS_DRIVER_IOCTL_DRIVER_VERSION"));
 
 		fill_up_drv_ver(&dv);
-		for (i = 0; i < xferlen; i++) {
-			if (ddi_copyout((uint8_t *)&dv + i, (uint8_t *)ubuf + i,
-			    1, mode)) {
-				con_log(CL_ANN, (CE_WARN, "handle_drv_ioctl: "
-				    "MRSAS_DRIVER_IOCTL_DRIVER_VERSION"
-				    " : copy to user space failed"));
-				kdcmd->cmd_status = 1;
-				rval = DDI_FAILURE;
-				break;
-			}
+
+		if (ddi_copyout(&dv, ubuf, xferlen, mode)) {
+			con_log(CL_ANN, (CE_WARN, "handle_drv_ioctl: "
+			    "MRSAS_DRIVER_IOCTL_DRIVER_VERSION : "
+			    "copy to user space failed"));
+			kdcmd->cmd_status = 1;
+			rval = 1;
+		} else {
+			kdcmd->cmd_status = 0;
 		}
-		if (i == xferlen)
-			kdcmd->cmd_status = 0;
 		break;
 	case MRSAS_DRIVER_IOCTL_PCI_INFORMATION:
 		con_log(CL_ANN1, (CE_NOTE, "handle_drv_ioctl: "
@@ -4292,21 +4288,16 @@
 			pci_conf_buf[i] =
 			    pci_config_get8(instance->pci_handle, i);
 		}
-		for (i = 0; i < xferlen; i++) {
-			if (ddi_copyout((uint8_t *)&pi + i, (uint8_t *)ubuf + i,
-			    1, mode)) {
-				con_log(CL_ANN, (CE_WARN, "handle_drv_ioctl: "
-				    "MRSAS_DRIVER_IOCTL_PCI_INFORMATION"
-				    " : copy to user space failed"));
-				kdcmd->cmd_status = 1;
-				rval = DDI_FAILURE;
-				break;
-			}
+
+		if (ddi_copyout(&pi, ubuf, xferlen, mode)) {
+			con_log(CL_ANN, (CE_WARN, "handle_drv_ioctl: "
+			    "MRSAS_DRIVER_IOCTL_PCI_INFORMATION : "
+			    "copy to user space failed"));
+			kdcmd->cmd_status = 1;
+			rval = 1;
+		} else {
+			kdcmd->cmd_status = 0;
 		}
-
-		if (i == xferlen)
-			kdcmd->cmd_status = 0;
-
 		break;
 	default:
 		con_log(CL_ANN, (CE_WARN, "handle_drv_ioctl: "
@@ -4347,7 +4338,7 @@
 
 	hdr = (struct mrsas_header *)&ioctl->frame[0];
 
-	switch (hdr->cmd) {
+	switch (ddi_get8(cmd->frame_dma_obj.acc_handle, &hdr->cmd)) {
 	case MFI_CMD_OP_DCMD:
 		rval = issue_mfi_dcmd(instance, ioctl, cmd, mode);
 		break;
@@ -4415,12 +4406,14 @@
 	 * old and current and re-issue to the FW
 	 */
 
-	curr_aen.word = class_locale_word;
+	curr_aen.word = LE_32(class_locale_word);
+	curr_aen.members.locale = LE_16(curr_aen.members.locale);
 	aen_cmd = instance->aen_cmd;
 	if (aen_cmd) {
 		prev_aen.word = ddi_get32(aen_cmd->frame_dma_obj.acc_handle,
 		    &aen_cmd->frame->dcmd.mbox.w[1]);
-
+		prev_aen.word = LE_32(prev_aen.word);
+		prev_aen.members.locale = LE_16(prev_aen.members.locale);
 		/*
 		 * A class whose enum value is smaller is inclusive of all
 		 * higher values. If a PROGRESS (= -1) was previously
@@ -4488,6 +4481,8 @@
 	ddi_put32(cmd->frame_dma_obj.acc_handle, &dcmd->opcode,
 	    MR_DCMD_CTRL_EVENT_WAIT);
 	ddi_put32(cmd->frame_dma_obj.acc_handle, &dcmd->mbox.w[0], seq_num);
+	curr_aen.members.locale = LE_16(curr_aen.members.locale);
+	curr_aen.word = LE_32(curr_aen.word);
 	ddi_put32(cmd->frame_dma_obj.acc_handle, &dcmd->mbox.w[1],
 	    curr_aen.word);
 	ddi_put32(cmd->frame_dma_obj.acc_handle, &dcmd->sgl.sge32[0].phys_addr,
@@ -4723,6 +4718,7 @@
 intr_ack_ppc(struct mrsas_instance *instance)
 {
 	uint32_t	status;
+	int ret = DDI_INTR_CLAIMED;
 
 	con_log(CL_ANN1, (CE_NOTE, "intr_ack_ppc: called"));
 
@@ -4732,9 +4728,17 @@
 	con_log(CL_ANN1, (CE_NOTE, "intr_ack_ppc: status = 0x%x", status));
 
 	if (!(status & MFI_REPLY_2108_MESSAGE_INTR)) {
-		return (DDI_INTR_UNCLAIMED);
-	}
-
+		ret = DDI_INTR_UNCLAIMED;
+	}
+
+	if (mrsas_check_acc_handle(instance->regmap_handle) != DDI_SUCCESS) {
+		ddi_fm_service_impact(instance->dip, DDI_SERVICE_UNAFFECTED);
+		ret = DDI_INTR_UNCLAIMED;
+	}
+
+	if (ret == DDI_INTR_UNCLAIMED) {
+		return (ret);
+	}
 	/* clear the interrupt by writing back the same value */
 	WR_OB_DOORBELL_CLEAR(status, instance);
 
@@ -4743,7 +4747,7 @@
 
 	con_log(CL_ANN1, (CE_NOTE, "intr_ack_ppc: interrupt cleared"));
 
-	return (DDI_INTR_CLAIMED);
+	return (ret);
 }
 
 static int
--- a/usr/src/uts/common/io/mr_sas/mr_sas.h	Fri Jun 05 10:27:16 2009 -0400
+++ b/usr/src/uts/common/io/mr_sas/mr_sas.h	Fri Jun 05 10:28:40 2009 -0400
@@ -37,7 +37,6 @@
  * Copyright 2009 Sun Microsystems, Inc.  All rights reserved.
  * Use is subject to license terms.
  */
-
 #ifndef	_MR_SAS_H_
 #define	_MR_SAS_H_
 
@@ -51,8 +50,8 @@
 /*
  * MegaRAID SAS2.0 Driver meta data
  */
-#define	MRSAS_VERSION				"LSIv2.0"
-#define	MRSAS_RELDATE				"Jan 9, 2009"
+#define	MRSAS_VERSION				"LSIv2.1"
+#define	MRSAS_RELDATE				"May 11, 2009"
 
 #define	MRSAS_TRUE				1
 #define	MRSAS_FALSE				0
@@ -1648,6 +1647,7 @@
 #define	DDI_VENDOR_LSI		"LSI"
 #endif /* DDI_VENDOR_LSI */
 
+#ifndef	KMDB_MODULE
 static int	mrsas_getinfo(dev_info_t *, ddi_info_cmd_t,  void *, void **);
 static int	mrsas_attach(dev_info_t *, ddi_attach_cmd_t);
 static int	mrsas_reset(dev_info_t *, ddi_reset_cmd_t);
@@ -1758,6 +1758,7 @@
 static int	mrsas_service_evt(struct mrsas_instance *, int, int, int,
 			uint64_t);
 static int	mrsas_mode_sense_build(struct scsi_pkt *);
+#endif	/* KMDB_MODULE */
 
 #ifdef	__cplusplus
 }
--- a/usr/src/uts/common/io/mr_sas/mr_sas_list.h	Fri Jun 05 10:27:16 2009 -0400
+++ b/usr/src/uts/common/io/mr_sas/mr_sas_list.h	Fri Jun 05 10:28:40 2009 -0400
@@ -70,7 +70,7 @@
 	(ptr)->next = (ptr); (ptr)->prev = (ptr); \
 }
 
-
+#ifndef	KMDB_MODULE
 /*
  * Insert a new entry between two known consecutive entries.
  *
@@ -173,7 +173,7 @@
 		at->prev = last;
 	}
 }
-
+#endif /* KMDB_MODULE */
 
 /*
  * mlist_entry - get the struct for this entry
--- a/usr/src/uts/common/io/nxge/nxge_send.c	Fri Jun 05 10:27:16 2009 -0400
+++ b/usr/src/uts/common/io/nxge/nxge_send.c	Fri Jun 05 10:28:40 2009 -0400
@@ -203,7 +203,7 @@
 int
 nxge_start(p_nxge_t nxgep, p_tx_ring_t tx_ring_p, p_mblk_t mp)
 {
-	int 			status = 0;
+	int 			dma_status, status = 0;
 	p_tx_desc_t 		tx_desc_ring_vp;
 	npi_handle_t		npi_desc_handle;
 	nxge_os_dma_handle_t 	tx_desc_dma_handle;
@@ -708,11 +708,11 @@
 			}
 
 			dma_handle = tx_msg_p->dma_handle;
-			status = ddi_dma_addr_bind_handle(dma_handle, NULL,
+			dma_status = ddi_dma_addr_bind_handle(dma_handle, NULL,
 			    (caddr_t)b_rptr, len, dma_flags,
 			    DDI_DMA_DONTWAIT, NULL,
 			    &dma_cookie, &ncookies);
-			if (status == DDI_DMA_MAPPED) {
+			if (dma_status == DDI_DMA_MAPPED) {
 				dma_ioaddr = dma_cookie.dmac_laddress;
 				len = (int)dma_cookie.dmac_size;
 				clen = (uint32_t)dma_cookie.dmac_size;
@@ -802,6 +802,7 @@
 					mp = nmp;
 					goto nxge_start_fail_lso;
 				} else {
+					status = 1;
 					goto nxge_start_fail2;
 				}
 			}
@@ -886,14 +887,15 @@
 			    "==> nxge_start(14): pull msg - "
 			    "len %d pkt_len %d ngathers %d",
 			    len, pkt_len, ngathers));
-			/* Pull all message blocks from b_cont */
+
+			/*
+			 * Just give up on this packet.
+			 */
 			if (is_lso) {
 				mp = nmp_lso_save;
 				goto nxge_start_fail_lso;
 			}
-			if ((msgpullup(mp, -1)) == NULL) {
-				goto nxge_start_fail2;
-			}
+			status = 0;
 			goto nxge_start_fail2;
 		}
 	} /* while (nmp) */
@@ -1147,12 +1149,11 @@
 nxge_start_fail_lso:
 	status = 0;
 	good_packet = B_FALSE;
-	if (mp != NULL) {
+	if (mp != NULL)
 		freemsg(mp);
-	}
-	if (mp_chain != NULL) {
-		freemsg(mp_chain);
-	}
+	if (mp_chain != NULL)
+		freemsgchain(mp_chain);
+
 	if (!lso_again && !ngathers) {
 		if (isLDOMservice(nxgep)) {
 			tx_ring_p->tx_ring_busy = B_FALSE;
--- a/usr/src/uts/common/io/scsi/adapters/iscsi/iscsi.c	Fri Jun 05 10:27:16 2009 -0400
+++ b/usr/src/uts/common/io/scsi/adapters/iscsi/iscsi.c	Fri Jun 05 10:28:40 2009 -0400
@@ -1068,7 +1068,7 @@
 static int
 iscsi_tran_setcap(struct scsi_address *ap, char *cap, int value, int whom)
 {
-	return (iscsi_i_commoncap(ap, cap, 0, whom, 1));
+	return (iscsi_i_commoncap(ap, cap, value, whom, 1));
 }
 
 
--- a/usr/src/uts/common/io/scsi/adapters/iscsi/iscsi.h	Fri Jun 05 10:27:16 2009 -0400
+++ b/usr/src/uts/common/io/scsi/adapters/iscsi/iscsi.h	Fri Jun 05 10:28:40 2009 -0400
@@ -380,6 +380,16 @@
 #define	ISCSI_CMD_MISCFLAG_FREE		0x2
 #define	ISCSI_CMD_MISCFLAG_STUCK	0x4
 #define	ISCSI_CMD_MISCFLAG_XARQ 	0x8
+#define	ISCSI_CMD_MISCFLAG_SENT		0x10
+#define	ISCSI_CMD_MISCFLAG_FLUSH	0x20
+
+/*
+ * 1/2 of a 32 bit number, used for checking CmdSN
+ * wrapped.
+ */
+#define	ISCSI_CMD_SN_WRAP		0x80000000
+
+#define	ISCSI_CMD_PKT_STAT_INIT		0
 
 /*
  * iSCSI cmd/pkt Structure
@@ -430,6 +440,10 @@
 			 * another R2T to handle.
 			 */
 			boolean_t		r2t_more;
+			/*
+			 * It is used to record pkt_statistics temporarily.
+			 */
+			uint_t			pkt_stat;
 		} scsi;
 		/* ISCSI_CMD_TYPE_ABORT */
 		struct {
@@ -439,6 +453,7 @@
 		/* ISCSI_CMD_TYPE_RESET */
 		struct {
 			int			level;
+			uint8_t			response;
 		} reset;
 		/* ISCSI_CMD_TYPE_NOP */
 		struct {
@@ -497,6 +512,8 @@
 	idm_pdu_t		cmd_pdu;
 
 	sm_audit_buf_t		cmd_state_audit;
+
+	uint32_t		cmd_sn;
 } iscsi_cmd_t;
 
 
@@ -528,12 +545,15 @@
 	uchar_t			lun_type;
 } iscsi_lun_t;
 
-#define	ISCSI_LUN_STATE_CLEAR	0		/* used to clear all states */
-#define	ISCSI_LUN_STATE_OFFLINE	1
-#define	ISCSI_LUN_STATE_ONLINE	2
-#define	ISCSI_LUN_STATE_INVALID	4		/* offline failed */
+#define	ISCSI_LUN_STATE_CLEAR	    0		/* used to clear all states */
+#define	ISCSI_LUN_STATE_OFFLINE	    1
+#define	ISCSI_LUN_STATE_ONLINE	    2
+#define	ISCSI_LUN_STATE_INVALID	    4		/* offline failed */
+#define	ISCSI_LUN_STATE_BUSY	    8		/* logic unit is in reset */
 
-#define	ISCSI_LUN_CAP_RESET   0x01
+#define	ISCSI_LUN_CAP_RESET	    0x01
+
+#define	ISCSI_SCSI_RESET_SENSE_CODE 0x29
 
 /*
  *
@@ -959,6 +979,10 @@
 	iscsi_thread_t		*sess_wd_thread;
 
 	sm_audit_buf_t		sess_state_audit;
+
+	kmutex_t		sess_reset_mutex;
+
+	boolean_t		sess_reset_in_progress;
 } iscsi_sess_t;
 
 /*
--- a/usr/src/uts/common/io/scsi/adapters/iscsi/iscsi_cmd.c	Fri Jun 05 10:27:16 2009 -0400
+++ b/usr/src/uts/common/io/scsi/adapters/iscsi/iscsi_cmd.c	Fri Jun 05 10:28:40 2009 -0400
@@ -336,6 +336,9 @@
 				    iscsi_cmd_timeout_factor);
 			else
 				icmdp->cmd_lbolt_timeout = 0;
+
+			icmdp->cmd_un.scsi.pkt_stat &=
+			    ISCSI_CMD_PKT_STAT_INIT;
 		} else {
 			icmdp->cmd_lbolt_timeout = icmdp->cmd_lbolt_pending +
 			    SEC_TO_TICK(ISCSI_INTERNAL_CMD_TIMEOUT *
@@ -636,7 +639,7 @@
 				    CMD_TIMEOUT, STAT_TIMEOUT);
 			} else {
 				ISCSI_CMD_SET_REASON_STAT(icmdp,
-				    CMD_TRAN_ERR, 0);
+				    CMD_TRAN_ERR, icmdp->cmd_un.scsi.pkt_stat);
 			}
 			iscsi_enqueue_completed_cmd(isp, icmdp);
 			break;
@@ -792,7 +795,7 @@
 				 * when we complete the command.
 				 */
 				ISCSI_CMD_SET_REASON_STAT(
-				    t_icmdp, CMD_TIMEOUT, STAT_TIMEOUT);
+				    t_icmdp, CMD_TIMEOUT, STAT_ABORTED);
 				idm_task_abort(icp->conn_ic, t_icmdp->cmd_itp,
 				    AT_TASK_MGMT_ABORT);
 			} else {
@@ -811,9 +814,17 @@
 			mutex_exit(&isp->sess_cmdsn_mutex);
 
 			/*
-			 * Complete the abort/reset successfully.
+			 * Complete the abort/reset command.
 			 */
-			ISCSI_CMD_ISSUE_CALLBACK(icmdp, ISCSI_STATUS_SUCCESS);
+			if (icmdp->cmd_un.reset.response !=
+			    SCSI_TCP_TM_RESP_COMPLETE) {
+				ISCSI_CMD_ISSUE_CALLBACK(icmdp,
+				    ISCSI_STATUS_CMD_FAILED);
+			} else {
+				ISCSI_CMD_ISSUE_CALLBACK(icmdp,
+				    ISCSI_STATUS_SUCCESS);
+			}
+
 			break;
 
 		case ISCSI_CMD_TYPE_LOGOUT:
@@ -1014,8 +1025,8 @@
 			mutex_enter(&icp->conn_queue_idm_aborting.mutex);
 			iscsi_enqueue_idm_aborting_cmd(icmdp->cmd_conn, icmdp);
 			mutex_exit(&icp->conn_queue_idm_aborting.mutex);
-
-			ISCSI_CMD_SET_REASON_STAT(icmdp, CMD_TRAN_ERR, 0);
+			ISCSI_CMD_SET_REASON_STAT(icmdp,
+			    CMD_TRAN_ERR, icmdp->cmd_un.scsi.pkt_stat);
 			idm_task_abort(icp->conn_ic, icmdp->cmd_itp,
 			    AT_TASK_MGMT_ABORT);
 			break;
@@ -1111,7 +1122,8 @@
 		iscsi_task_cleanup(ISCSI_OP_SCSI_RSP, icmdp);
 		iscsi_sess_release_scsi_itt(icmdp);
 
-		ISCSI_CMD_SET_REASON_STAT(icmdp, CMD_TRAN_ERR, 0);
+		ISCSI_CMD_SET_REASON_STAT(icmdp, CMD_TRAN_ERR,
+		    icmdp->cmd_un.scsi.pkt_stat);
 		iscsi_enqueue_completed_cmd(isp, icmdp);
 		break;
 
@@ -1193,7 +1205,9 @@
 			    ISCSI_CMD_EVENT_E10, arg);
 		}
 
-		ISCSI_CMD_SET_REASON_STAT(icmdp, CMD_TRAN_ERR, 0);
+		ISCSI_CMD_SET_REASON_STAT(icmdp,
+		    CMD_TRAN_ERR, icmdp->cmd_un.scsi.pkt_stat);
+
 		idm_task_abort(icmdp->cmd_conn->conn_ic, icmdp->cmd_itp,
 		    AT_TASK_MGMT_ABORT);
 		break;
@@ -1205,7 +1219,8 @@
 		iscsi_task_cleanup(ISCSI_OP_SCSI_RSP, icmdp);
 		iscsi_sess_release_scsi_itt(icmdp);
 
-		ISCSI_CMD_SET_REASON_STAT(icmdp, CMD_TRAN_ERR, 0);
+		ISCSI_CMD_SET_REASON_STAT(icmdp, CMD_TRAN_ERR,
+		    icmdp->cmd_un.scsi.pkt_stat);
 		iscsi_enqueue_completed_cmd(isp, icmdp);
 		break;
 
@@ -1261,8 +1276,10 @@
 	case ISCSI_CMD_EVENT_E7:
 		/*
 		 * We have already requested IDM to stop processing this
-		 * command so ignore this request.
+		 * command so just update the pkt_statistics.
 		 */
+		ISCSI_CMD_SET_REASON_STAT(icmdp,
+		    CMD_TRAN_ERR, icmdp->cmd_un.scsi.pkt_stat);
 		break;
 
 	/* -E9: IDM is no longer processing this command */
--- a/usr/src/uts/common/io/scsi/adapters/iscsi/iscsi_conn.c	Fri Jun 05 10:27:16 2009 -0400
+++ b/usr/src/uts/common/io/scsi/adapters/iscsi/iscsi_conn.c	Fri Jun 05 10:28:40 2009 -0400
@@ -913,6 +913,13 @@
 	/* Flush active queue */
 	icmdp = icp->conn_queue_active.head;
 	while (icmdp != NULL) {
+
+		mutex_enter(&icmdp->cmd_mutex);
+		if (icmdp->cmd_type == ISCSI_CMD_TYPE_SCSI) {
+			icmdp->cmd_un.scsi.pkt_stat |= STAT_ABORTED;
+		}
+		mutex_exit(&icmdp->cmd_mutex);
+
 		iscsi_cmd_state_machine(icmdp,
 		    ISCSI_CMD_EVENT_E7, isp);
 		icmdp = icp->conn_queue_active.head;
--- a/usr/src/uts/common/io/scsi/adapters/iscsi/iscsi_io.c	Fri Jun 05 10:27:16 2009 -0400
+++ b/usr/src/uts/common/io/scsi/adapters/iscsi/iscsi_io.c	Fri Jun 05 10:28:40 2009 -0400
@@ -33,6 +33,7 @@
 #include <netinet/tcp.h>	/* TCP_NODELAY */
 #include <sys/socketvar.h>	/* _ALLOC_SLEEP */
 #include <sys/strsun.h>		/* DB_TYPE() */
+#include <sys/scsi/generic/sense.h>
 
 #include "iscsi.h"		/* iscsi driver */
 #include <sys/iscsi_protocol.h>	/* iscsi protocol */
@@ -106,6 +107,9 @@
 
 static void iscsi_timeout_checks(iscsi_sess_t *isp);
 static void iscsi_nop_checks(iscsi_sess_t *isp);
+static boolean_t iscsi_decode_sense(uint8_t *sense_data);
+static void iscsi_flush_cmd_after_reset(uint32_t cmd_sn, uint16_t lun_num,
+    iscsi_conn_t *icp);
 
 /*
  * This file contains the main guts of the iSCSI protocol layer.
@@ -424,7 +428,7 @@
 	}
 }
 
-static void
+static boolean_t
 iscsi_cmd_rsp_cmd_status(iscsi_cmd_t *icmdp, iscsi_scsi_rsp_hdr_t *issrhp,
     uint8_t *data)
 {
@@ -434,6 +438,7 @@
 	int32_t			statuslen	= 0;
 	int32_t			senselen_to	= 0;
 	struct scsi_pkt		*pkt;
+	boolean_t		affect		= B_FALSE;
 
 	pkt = icmdp->cmd_un.scsi.pkt;
 	dlength = n2h24(issrhp->dlength);
@@ -542,6 +547,9 @@
 			if (dlength > 0) {
 				bcopy(&data[2], (uchar_t *)&arqstat->
 				    sts_sensedata, dlength);
+
+				affect = iscsi_decode_sense(
+				    (uint8_t *)&arqstat->sts_sensedata);
 			}
 			break;
 		}
@@ -570,6 +578,8 @@
 			pkt->pkt_scbp[0] = issrhp->cmd_status;
 		}
 	}
+
+	return (affect);
 }
 
 /*
@@ -628,6 +638,9 @@
 	struct scsi_pkt		*pkt	= NULL;
 	idm_status_t		rval;
 	struct buf		*bp;
+	boolean_t		flush	= B_FALSE;
+	uint32_t		cmd_sn	= 0;
+	uint16_t		lun_num = 0;
 
 	/* make sure we get status in order */
 	mutex_enter(&icp->conn_queue_active.mutex);
@@ -725,10 +738,18 @@
 	} else {
 		/* success */
 		iscsi_cmd_rsp_chk(icmdp, issrhp);
-		iscsi_cmd_rsp_cmd_status(icmdp, issrhp, data);
+		flush = iscsi_cmd_rsp_cmd_status(icmdp, issrhp, data);
+		if (flush == B_TRUE) {
+			cmd_sn = icmdp->cmd_sn;
+			ASSERT(icmdp->cmd_lun != NULL);
+			lun_num = icmdp->cmd_lun->lun_num;
+		}
 	}
 
 	iscsi_cmd_state_machine(icmdp, ISCSI_CMD_EVENT_E3, isp);
+	if (flush == B_TRUE) {
+		iscsi_flush_cmd_after_reset(cmd_sn, lun_num, icp);
+	}
 	mutex_exit(&icp->conn_queue_active.mutex);
 	return (IDM_STATUS_SUCCESS);
 }
@@ -1205,11 +1226,24 @@
 				break;
 			}
 			/* FALLTHRU */
+		case SCSI_TCP_TM_RESP_REJECTED:
+			/*
+			 * If the target rejects our reset task,
+			 * we should record the response and complete
+			 * this command with the result.
+			 */
+			if (icmdp->cmd_type == ISCSI_CMD_TYPE_RESET) {
+				icmdp->cmd_un.reset.response =
+				    istmrhp->response;
+				iscsi_cmd_state_machine(icmdp,
+				    ISCSI_CMD_EVENT_E3, isp);
+				break;
+			}
+			/* FALLTHRU */
 		case SCSI_TCP_TM_RESP_NO_LUN:
 		case SCSI_TCP_TM_RESP_TASK_ALLEGIANT:
 		case SCSI_TCP_TM_RESP_NO_FAILOVER:
 		case SCSI_TCP_TM_RESP_IN_PRGRESS:
-		case SCSI_TCP_TM_RESP_REJECTED:
 		default:
 			/*
 			 * Something is out of sync.  Flush
@@ -1801,11 +1835,12 @@
 
 static void
 iscsi_tx_init_hdr(iscsi_sess_t *isp, iscsi_conn_t *icp,
-    iscsi_text_hdr_t *ihp, int opcode, uint32_t cmd_itt)
+    iscsi_text_hdr_t *ihp, int opcode, iscsi_cmd_t *icmdp)
 {
 	ihp->opcode		= opcode;
-	ihp->itt		= cmd_itt;
+	ihp->itt		= icmdp->cmd_itt;
 	mutex_enter(&isp->sess_cmdsn_mutex);
+	icmdp->cmd_sn		= isp->sess_cmdsn;
 	ihp->cmdsn		= htonl(isp->sess_cmdsn);
 	isp->sess_cmdsn++;
 	mutex_exit(&isp->sess_cmdsn_mutex);
@@ -2075,7 +2110,7 @@
 	}
 
 	iscsi_tx_init_hdr(isp, icp, (iscsi_text_hdr_t *)ihp,
-	    ISCSI_OP_SCSI_CMD, icmdp->cmd_itt);
+	    ISCSI_OP_SCSI_CMD, icmdp);
 
 	idm_pdu_init(pdu, icp->conn_ic, (void *)icmdp, &iscsi_tx_done);
 	idm_pdu_init_hdr(pdu, (uint8_t *)ihp, len);
@@ -2099,6 +2134,8 @@
 
 	idm_pdu_tx(pdu);
 
+	icmdp->cmd_misc_flags |= ISCSI_CMD_MISCFLAG_SENT;
+
 	return (rval);
 }
 
@@ -2132,6 +2169,7 @@
 
 	mutex_exit(&icp->conn_sess->sess_queue_pending.mutex);
 	idm_pdu_tx(tx_pdu);
+	icmdp->cmd_misc_flags |= ISCSI_CMD_MISCFLAG_SENT;
 }
 
 
@@ -2159,6 +2197,7 @@
 	inohp->itt	= icmdp->cmd_itt;
 	inohp->ttt	= icmdp->cmd_ttt;
 	mutex_enter(&isp->sess_cmdsn_mutex);
+	icmdp->cmd_sn	= isp->sess_cmdsn;
 	inohp->cmdsn	= htonl(isp->sess_cmdsn);
 	mutex_exit(&isp->sess_cmdsn_mutex);
 	inohp->expstatsn	= htonl(icp->conn_expstatsn);
@@ -2188,6 +2227,7 @@
 	istmh = kmem_zalloc(sizeof (iscsi_scsi_task_mgt_hdr_t), KM_SLEEP);
 	ASSERT(istmh != NULL);
 	mutex_enter(&isp->sess_cmdsn_mutex);
+	icmdp->cmd_sn	= isp->sess_cmdsn;
 	istmh->cmdsn	= htonl(isp->sess_cmdsn);
 	mutex_exit(&isp->sess_cmdsn_mutex);
 	istmh->expstatsn = htonl(icp->conn_expstatsn);
@@ -2225,6 +2265,7 @@
 	ASSERT(istmh != NULL);
 	istmh->opcode	= ISCSI_OP_SCSI_TASK_MGT_MSG | ISCSI_OP_IMMEDIATE;
 	mutex_enter(&isp->sess_cmdsn_mutex);
+	icmdp->cmd_sn	= isp->sess_cmdsn;
 	istmh->cmdsn	= htonl(isp->sess_cmdsn);
 	mutex_exit(&isp->sess_cmdsn_mutex);
 	istmh->expstatsn	= htonl(icp->conn_expstatsn);
@@ -2276,6 +2317,7 @@
 	ilh->itt		= icmdp->cmd_itt;
 	ilh->cid		= icp->conn_cid;
 	mutex_enter(&isp->sess_cmdsn_mutex);
+	icmdp->cmd_sn	= isp->sess_cmdsn;
 	ilh->cmdsn	= htonl(isp->sess_cmdsn);
 	mutex_exit(&isp->sess_cmdsn_mutex);
 	ilh->expstatsn	= htonl(icp->conn_expstatsn);
@@ -2314,7 +2356,7 @@
 	hton24(ith->dlength, icmdp->cmd_un.text.data_len);
 	ith->ttt		= icmdp->cmd_un.text.ttt;
 	iscsi_tx_init_hdr(isp, icp, (iscsi_text_hdr_t *)ith,
-	    ISCSI_OP_TEXT_CMD, icmdp->cmd_itt);
+	    ISCSI_OP_TEXT_CMD, icmdp);
 	bcopy(icmdp->cmd_un.text.lun, ith->rsvd4, sizeof (ith->rsvd4));
 
 	iscsi_tx_pdu(icp, ISCSI_OP_TEXT_CMD, ith, sizeof (iscsi_text_hdr_t),
@@ -2351,11 +2393,11 @@
 	ASSERT(icmdp->cmd_un.scsi.abort_icmdp == NULL);
 
 	new_icmdp = iscsi_cmd_alloc(icp, KM_SLEEP);
-	new_icmdp->cmd_type		= ISCSI_CMD_TYPE_ABORT;
-	new_icmdp->cmd_lun		= icmdp->cmd_lun;
-	new_icmdp->cmd_un.abort.icmdp	= icmdp;
-	new_icmdp->cmd_conn		= icmdp->cmd_conn;
-	icmdp->cmd_un.scsi.abort_icmdp	= new_icmdp;
+	new_icmdp->cmd_type		    = ISCSI_CMD_TYPE_ABORT;
+	new_icmdp->cmd_lun		    = icmdp->cmd_lun;
+	new_icmdp->cmd_un.abort.icmdp	    = icmdp;
+	new_icmdp->cmd_conn		    = icmdp->cmd_conn;
+	icmdp->cmd_un.scsi.abort_icmdp	    = new_icmdp;
 
 	/* pending queue mutex is already held by timeout_checks */
 	iscsi_cmd_state_machine(new_icmdp, ISCSI_CMD_EVENT_E1, isp);
@@ -2425,7 +2467,7 @@
 }
 
 /*
- * iscsi_handle_reset -
+ * iscsi_handle_reset - send reset request to the target
  *
  */
 iscsi_status_t
@@ -2437,6 +2479,29 @@
 
 	ASSERT(isp != NULL);
 
+	if (level == RESET_LUN) {
+		rw_enter(&isp->sess_lun_list_rwlock, RW_WRITER);
+		ASSERT(ilp != NULL);
+		if (ilp->lun_state & ISCSI_LUN_STATE_BUSY) {
+			rw_exit(&isp->sess_lun_list_rwlock);
+			return (ISCSI_STATUS_SUCCESS);
+		}
+		ilp->lun_state |= ISCSI_LUN_STATE_BUSY;
+		rw_exit(&isp->sess_lun_list_rwlock);
+	} else {
+		mutex_enter(&isp->sess_reset_mutex);
+		if (isp->sess_reset_in_progress == B_TRUE) {
+			/*
+			 * If the reset is in progress, it is unnecessary
+			 * to send reset to the target redunantly.
+			 */
+			mutex_exit(&isp->sess_reset_mutex);
+			return (ISCSI_STATUS_SUCCESS);
+		}
+		isp->sess_reset_in_progress = B_TRUE;
+		mutex_exit(&isp->sess_reset_mutex);
+	}
+
 	bzero(&icmd, sizeof (iscsi_cmd_t));
 	icmd.cmd_sig		= ISCSI_SIG_CMD;
 	icmd.cmd_state		= ISCSI_CMD_STATE_FREE;
@@ -2445,6 +2510,8 @@
 	icmd.cmd_un.reset.level	= level;
 	icmd.cmd_result		= ISCSI_STATUS_SUCCESS;
 	icmd.cmd_completed	= B_FALSE;
+	icmd.cmd_un.reset.response = SCSI_TCP_TM_RESP_COMPLETE;
+
 	mutex_init(&icmd.cmd_mutex, NULL, MUTEX_DRIVER, NULL);
 	cv_init(&icmd.cmd_completion, NULL, CV_DRIVER, NULL);
 	/*
@@ -2456,6 +2523,17 @@
 	if (!ISCSI_SESS_STATE_FULL_FEATURE(isp->sess_state)) {
 		/* We aren't connected to the target fake success */
 		mutex_exit(&isp->sess_state_mutex);
+
+		if (level == RESET_LUN) {
+			rw_enter(&isp->sess_lun_list_rwlock, RW_WRITER);
+			ilp->lun_state &= ~ISCSI_LUN_STATE_BUSY;
+			rw_exit(&isp->sess_lun_list_rwlock);
+		} else {
+			mutex_enter(&isp->sess_reset_mutex);
+			isp->sess_reset_in_progress = B_FALSE;
+			mutex_exit(&isp->sess_reset_mutex);
+		}
+
 		return (ISCSI_STATUS_SUCCESS);
 	}
 
@@ -2483,13 +2561,74 @@
 		icp = isp->sess_conn_list;
 		while (icp != NULL) {
 			iscsi_cmd_t *t_icmdp = NULL;
+			iscsi_cmd_t *next_icmdp = NULL;
 
 			mutex_enter(&icp->conn_queue_active.mutex);
 			t_icmdp = icp->conn_queue_active.head;
 			while (t_icmdp != NULL) {
-				iscsi_cmd_state_machine(t_icmdp,
-				    ISCSI_CMD_EVENT_E7, isp);
-				t_icmdp = icp->conn_queue_active.head;
+				next_icmdp = t_icmdp->cmd_next;
+				mutex_enter(&t_icmdp->cmd_mutex);
+				if (!(t_icmdp->cmd_misc_flags &
+				    ISCSI_CMD_MISCFLAG_SENT)) {
+					/*
+					 * Although this command is in the
+					 * active queue, it has not been sent.
+					 * Skip it.
+					 */
+					mutex_exit(&t_icmdp->cmd_mutex);
+					t_icmdp = next_icmdp;
+					continue;
+				}
+				if (level == RESET_LUN) {
+					if (icmd.cmd_lun == NULL ||
+					    t_icmdp->cmd_lun == NULL ||
+					    (icmd.cmd_lun->lun_num !=
+					    t_icmdp->cmd_lun->lun_num)) {
+						mutex_exit(&t_icmdp->cmd_mutex);
+						t_icmdp = next_icmdp;
+						continue;
+					}
+				}
+
+				if (icmd.cmd_sn == t_icmdp->cmd_sn) {
+					/*
+					 * This command may be replied with
+					 * UA sense key later. So currently
+					 * it is not a suitable time to flush
+					 * it. Mark its flag with FLUSH. There
+					 * is no harm to keep it for a while.
+					 */
+					t_icmdp->cmd_misc_flags |=
+					    ISCSI_CMD_MISCFLAG_FLUSH;
+					if (t_icmdp->cmd_type ==
+					    ISCSI_CMD_TYPE_SCSI) {
+						t_icmdp->cmd_un.scsi.pkt_stat |=
+						    STAT_BUS_RESET;
+					}
+					mutex_exit(&t_icmdp->cmd_mutex);
+				} else if ((icmd.cmd_sn > t_icmdp->cmd_sn) ||
+				    ((t_icmdp->cmd_sn - icmd.cmd_sn) >
+				    ISCSI_CMD_SN_WRAP)) {
+					/*
+					 * This reset request must act on all
+					 * the commnds from the same session
+					 * having a CmdSN lower than the task
+					 * mangement CmdSN. So flush these
+					 * commands here.
+					 */
+					if (t_icmdp->cmd_type ==
+					    ISCSI_CMD_TYPE_SCSI) {
+						t_icmdp->cmd_un.scsi.pkt_stat |=
+						    STAT_BUS_RESET;
+					}
+					mutex_exit(&t_icmdp->cmd_mutex);
+					iscsi_cmd_state_machine(t_icmdp,
+					    ISCSI_CMD_EVENT_E7, isp);
+				} else {
+					mutex_exit(&t_icmdp->cmd_mutex);
+				}
+
+				t_icmdp = next_icmdp;
 			}
 
 			mutex_exit(&icp->conn_queue_active.mutex);
@@ -2502,6 +2641,16 @@
 	cv_destroy(&icmd.cmd_completion);
 	mutex_destroy(&icmd.cmd_mutex);
 
+	if (level == RESET_LUN) {
+		rw_enter(&isp->sess_lun_list_rwlock, RW_WRITER);
+		ilp->lun_state &= ~ISCSI_LUN_STATE_BUSY;
+		rw_exit(&isp->sess_lun_list_rwlock);
+	} else {
+		mutex_enter(&isp->sess_reset_mutex);
+		isp->sess_reset_in_progress = B_FALSE;
+		mutex_exit(&isp->sess_reset_mutex);
+	}
+
 	return (rval);
 }
 
@@ -3227,16 +3376,31 @@
 			if (icmdp->cmd_lbolt_timeout == 0)
 				continue;
 
-			/* Skip if command is not active */
-			if (icmdp->cmd_state != ISCSI_CMD_STATE_ACTIVE)
+			/*
+			 * Skip if command is not active or not needed
+			 * to flush.
+			 */
+			if (icmdp->cmd_state != ISCSI_CMD_STATE_ACTIVE &&
+			    !(icmdp->cmd_misc_flags & ISCSI_CMD_MISCFLAG_FLUSH))
 				continue;
 
 			/* Skip if timeout still in the future */
 			if (now <= icmdp->cmd_lbolt_timeout)
 				continue;
 
-			/* timeout */
-			iscsi_cmd_state_machine(icmdp, ISCSI_CMD_EVENT_E6, isp);
+			if (icmdp->cmd_misc_flags & ISCSI_CMD_MISCFLAG_FLUSH) {
+				/*
+				 * This command is left during target reset,
+				 * we can flush it now.
+				 */
+				iscsi_cmd_state_machine(icmdp,
+				    ISCSI_CMD_EVENT_E7, isp);
+			} else if (icmdp->cmd_state == ISCSI_CMD_STATE_ACTIVE) {
+				/* timeout */
+				iscsi_cmd_state_machine(icmdp,
+				    ISCSI_CMD_EVENT_E6, isp);
+			}
+
 		}
 		mutex_exit(&icp->conn_queue_active.mutex);
 		mutex_exit(&isp->sess_queue_pending.mutex);
@@ -3299,3 +3463,91 @@
  * | End of wd routines						|
  * +--------------------------------------------------------------------+
  */
+
+/*
+ * iscsi_flush_cmd_after_reset - flush commands after reset
+ *
+ * Here we will flush all the commands in the same connection whose cmdsn is
+ * less than the one received with the Unit Attention.
+ */
+static void
+iscsi_flush_cmd_after_reset(uint32_t cmd_sn, uint16_t lun_num,
+    iscsi_conn_t *icp)
+{
+	iscsi_cmd_t	*t_icmdp    = NULL;
+	iscsi_cmd_t	*next_icmdp = NULL;
+
+	ASSERT(icp != NULL);
+
+	t_icmdp = icp->conn_queue_active.head;
+	while (t_icmdp != NULL) {
+		next_icmdp = t_icmdp->cmd_next;
+		mutex_enter(&t_icmdp->cmd_mutex);
+		/*
+		 * We will flush the commands whose cmdsn is less than the one
+		 * got Unit Attention.
+		 * Here we will check for wrap by subtracting and compare to
+		 * 1/2 of a 32 bit number, if greater then we wrapped.
+		 */
+		if ((t_icmdp->cmd_misc_flags & ISCSI_CMD_MISCFLAG_SENT) &&
+		    ((cmd_sn > t_icmdp->cmd_sn) ||
+		    ((t_icmdp->cmd_sn - cmd_sn) >
+		    ISCSI_CMD_SN_WRAP))) {
+			if (t_icmdp->cmd_lun != NULL &&
+			    t_icmdp->cmd_lun->lun_num == lun_num) {
+				t_icmdp->cmd_misc_flags |=
+				    ISCSI_CMD_MISCFLAG_FLUSH;
+				if (t_icmdp->cmd_type == ISCSI_CMD_TYPE_SCSI) {
+					t_icmdp->cmd_un.scsi.pkt_stat |=
+					    STAT_BUS_RESET;
+				}
+			}
+		}
+		mutex_exit(&t_icmdp->cmd_mutex);
+		t_icmdp = next_icmdp;
+	}
+}
+
+/*
+ * iscsi_decode_sense - decode the sense data in the cmd response
+ *
+ * Here we only care about Unit Attention with 0x29.
+ */
+static boolean_t
+iscsi_decode_sense(uint8_t *sense_data)
+{
+	uint8_t	sense_key   = 0;
+	uint8_t	asc	    = 0;
+	boolean_t affect    = B_FALSE;
+
+	ASSERT(sense_data != NULL);
+
+	sense_key = scsi_sense_key(sense_data);
+	switch (sense_key) {
+		case KEY_UNIT_ATTENTION:
+			asc = scsi_sense_asc(sense_data);
+			switch (asc) {
+				case ISCSI_SCSI_RESET_SENSE_CODE:
+					/*
+					 * POWER ON, RESET, OR BUS_DEVICE RESET
+					 * OCCURRED
+					 */
+					affect = B_TRUE;
+					break;
+				default:
+					/*
+					 * Currently we don't care
+					 * about other sense key.
+					 */
+					break;
+			}
+			break;
+		default:
+			/*
+			 * Currently we don't care
+			 * about other sense key.
+			 */
+			break;
+	}
+	return (affect);
+}
--- a/usr/src/uts/common/io/scsi/adapters/iscsi/iscsi_sess.c	Fri Jun 05 10:27:16 2009 -0400
+++ b/usr/src/uts/common/io/scsi/adapters/iscsi/iscsi_sess.c	Fri Jun 05 10:28:40 2009 -0400
@@ -85,6 +85,7 @@
 static iscsi_status_t iscsi_sess_reportluns(iscsi_sess_t *isp);
 static void iscsi_sess_inquiry(iscsi_sess_t *isp, uint16_t lun_num,
     uint8_t lun_addr_type);
+static void iscsi_sess_update_busy_luns(iscsi_sess_t *isp, boolean_t clear);
 
 /*
  * +--------------------------------------------------------------------+
@@ -199,6 +200,7 @@
 	isp->sess_sig			= ISCSI_SIG_SESS;
 	isp->sess_state			= ISCSI_SESS_STATE_FREE;
 	mutex_init(&isp->sess_state_mutex, NULL, MUTEX_DRIVER, NULL);
+	mutex_init(&isp->sess_reset_mutex, NULL, MUTEX_DRIVER, NULL);
 	isp->sess_hba			= ihp;
 	isp->sess_enum_in_progress	= B_FALSE;
 
@@ -215,6 +217,7 @@
 	isp->sess_last_err		= NoError;
 	isp->sess_tsid			= 0;
 	isp->sess_type			= type;
+	isp->sess_reset_in_progress	= B_FALSE;
 	idm_sm_audit_init(&isp->sess_state_audit);
 
 	/* copy default driver login parameters */
@@ -291,6 +294,7 @@
 	iscsi_destroy_queue(&isp->sess_queue_completion);
 	iscsi_destroy_queue(&isp->sess_queue_pending);
 	mutex_destroy(&isp->sess_state_mutex);
+	mutex_destroy(&isp->sess_reset_mutex);
 	kmem_free(isp, sizeof (iscsi_sess_t));
 
 	return (NULL);
@@ -571,6 +575,7 @@
 	rw_destroy(&isp->sess_conn_list_rwlock);
 	mutex_destroy(&isp->sess_cmdsn_mutex);
 	mutex_destroy(&isp->sess_state_mutex);
+	mutex_destroy(&isp->sess_reset_mutex);
 	kmem_free(isp, sizeof (iscsi_sess_t));
 
 	return (rval);
@@ -1217,6 +1222,12 @@
 			iscsi_thread_destroy(isp->sess_ic_thread);
 		}
 
+		mutex_enter(&isp->sess_reset_mutex);
+		isp->sess_reset_in_progress = B_FALSE;
+		mutex_exit(&isp->sess_reset_mutex);
+		/* update busy luns if needed */
+		iscsi_sess_update_busy_luns(isp, B_TRUE);
+
 		mutex_enter(&isp->sess_state_mutex);
 		break;
 
@@ -1399,6 +1410,12 @@
 			iscsi_thread_destroy(isp->sess_ic_thread);
 		}
 
+		mutex_enter(&isp->sess_reset_mutex);
+		isp->sess_reset_in_progress = B_FALSE;
+		mutex_exit(&isp->sess_reset_mutex);
+		/* update busy luns if needed */
+		iscsi_sess_update_busy_luns(isp, B_TRUE);
+
 		mutex_enter(&isp->sess_state_mutex);
 		break;
 
@@ -2173,6 +2190,15 @@
 	mutex_enter(&isp->sess_queue_pending.mutex);
 	icmdp = isp->sess_queue_pending.head;
 	while (icmdp != NULL) {
+
+		if (isp->sess_state == ISCSI_SESS_STATE_FAILED) {
+			mutex_enter(&icmdp->cmd_mutex);
+			if (icmdp->cmd_type == ISCSI_CMD_TYPE_SCSI) {
+				icmdp->cmd_un.scsi.pkt_stat |= STAT_ABORTED;
+			}
+			mutex_exit(&icmdp->cmd_mutex);
+		}
+
 		iscsi_cmd_state_machine(icmdp,
 		    ISCSI_CMD_EVENT_E7, isp);
 		icmdp = isp->sess_queue_pending.head;
@@ -2242,3 +2268,26 @@
 	}
 	return (rval);
 }
+
+static void
+iscsi_sess_update_busy_luns(iscsi_sess_t *isp, boolean_t clear)
+{
+	iscsi_lun_t	*ilp;
+	iscsi_hba_t	*ihp;
+
+	ASSERT(isp != NULL);
+	ihp = isp->sess_hba;
+	ASSERT(ihp != NULL);
+
+	rw_enter(&isp->sess_lun_list_rwlock, RW_WRITER);
+	ilp = isp->sess_lun_list;
+	while (ilp != NULL) {
+		if (clear == B_TRUE) {
+			ilp->lun_state &= ~ISCSI_LUN_STATE_BUSY;
+		} else {
+			ilp->lun_state |= ISCSI_LUN_STATE_BUSY;
+		}
+		ilp = ilp->lun_next;
+	}
+	rw_exit(&isp->sess_lun_list_rwlock);
+}
--- a/usr/src/uts/common/io/usb/hcd/ehci/ehci_isoch_util.c	Fri Jun 05 10:27:16 2009 -0400
+++ b/usr/src/uts/common/io/usb/hcd/ehci/ehci_isoch_util.c	Fri Jun 05 10:28:40 2009 -0400
@@ -19,7 +19,7 @@
  * CDDL HEADER END
  */
 /*
- * Copyright 2008 Sun Microsystems, Inc.  All rights reserved.
+ * Copyright 2009 Sun Microsystems, Inc.  All rights reserved.
  * Use is subject to license terms.
  */
 
@@ -668,7 +668,6 @@
 	    "ehci_deallocate_itd: old_itd = 0x%p", (void *)old_itd);
 
 	ASSERT(mutex_owned(&ehcip->ehci_int_mutex));
-	ASSERT(Get_ITD(old_itd->itd_trans_wrapper) == itw->itw_id);
 
 	/* If it has been marked RECLAIM it has already been removed */
 	if (Get_ITD(old_itd->itd_state) != EHCI_ITD_RECLAIM) {
--- a/usr/src/uts/common/io/usb/hwa/hwahc/hwahc.c	Fri Jun 05 10:27:16 2009 -0400
+++ b/usr/src/uts/common/io/usb/hwa/hwahc/hwahc.c	Fri Jun 05 10:28:40 2009 -0400
@@ -765,7 +765,9 @@
 
 	if (hwahcp->hwahc_flags & HWAHC_WA_STARTED) {
 		/* can be combined with wusb_wa_data_fini() */
+		mutex_exit(&hwahcp->hwahc_mutex);
 		hwahc_wa_stop(hwahcp);
+		mutex_enter(&hwahcp->hwahc_mutex);
 	}
 
 	if (hwahcp->hwahc_flags & HWAHC_HC_INITED) {
@@ -2409,13 +2411,17 @@
 		if (hc_data->hc_children_state[port] & WUSB_CHILD_ZAP) {
 			wusb_dev_info_t		*dev_info;
 			wusb_secrt_data_t	*csecrt_data;
+			usba_device_t		*child_ud;
 
 			USB_DPRINTF_L3(PRINT_MASK_EVENTS,
 			    hwahcp->hwahc_log_handle,
 			    "hwahc_bus_unconfig: physically zap port %d", port);
 
+			child_ud = hc_data->hc_usba_devices[port];
+			mutex_exit(&hc_data->hc_mutex);
 			/* zap the dip and usba_device structure as well */
-			usba_free_usba_device(hc_data->hc_usba_devices[port]);
+			usba_free_usba_device(child_ud);
+			mutex_enter(&hc_data->hc_mutex);
 			hc_data->hc_usba_devices[port] = NULL;
 
 			/* dip freed in usba_destroy_child_devi */
@@ -3689,8 +3695,10 @@
 		return (USB_SUCCESS);
 	}
 
+	mutex_exit(&hwahcp->hwahc_mutex);
 	/* suspend host, refer to WUSB 1.0 spec 8.5.3.14 */
 	rval = wusb_hc_stop_ch(&hwahcp->hwahc_hc_data, 10000); /* 10ms */
+	mutex_enter(&hwahcp->hwahc_mutex);
 	if (rval != USB_SUCCESS) {
 		USB_DPRINTF_L2(PRINT_MASK_PM, hwahcp->hwahc_log_handle,
 		    "hwahc_hc_channel_suspend: wusb channel stop fails");
@@ -3940,8 +3948,8 @@
 		return (rval);
 	}
 
+	mutex_exit(&hwahcp->hwahc_mutex);
 	/* reset wire adapter */
-	mutex_exit(&hwahcp->hwahc_mutex);
 	rval = wusb_wa_reset(&hwahcp->hwahc_wa_data,
 	    hwahcp->hwahc_default_pipe);
 	mutex_enter(&hwahcp->hwahc_mutex);
@@ -3967,12 +3975,14 @@
 		}
 	}
 
+	mutex_exit(&hwahcp->hwahc_mutex);
 	/* set cluster id for the wusb channel */
 	rval = wusb_hc_set_cluster_id(&hwahcp->hwahc_hc_data, cluster_id);
 	if (rval != USB_SUCCESS) {
 		USB_DPRINTF_L2(PRINT_MASK_ATTA, hwahcp->hwahc_log_handle,
 		    "hwahc_hc_initial_start: set cluster id %d fails",
 		    cluster_id);
+		mutex_enter(&hwahcp->hwahc_mutex);
 
 		goto err;
 	}
@@ -3985,6 +3995,7 @@
 		USB_DPRINTF_L2(PRINT_MASK_ATTA, hwahcp->hwahc_log_handle,
 		    "hwahc_hc_initial_start: set stream idx %d fails",
 		    stream_idx);
+		mutex_enter(&hwahcp->hwahc_mutex);
 
 		goto err;
 	}
@@ -3996,16 +4007,17 @@
 	if (rval != USB_SUCCESS) {
 		USB_DPRINTF_L2(PRINT_MASK_ATTA, hwahcp->hwahc_log_handle,
 		    "hwahc_hc_initial_start: set num dnts fails");
+		mutex_enter(&hwahcp->hwahc_mutex);
 
 		goto err;
 	}
 
 	/* set host info IE */
 	rval = wusb_hc_add_host_info(&hwahcp->hwahc_hc_data, stream_idx);
-
 	if (rval != USB_SUCCESS) {
 		USB_DPRINTF_L2(PRINT_MASK_ATTA, hwahcp->hwahc_log_handle,
 		    "hwahc_hc_initial_start: add hostinfo ie fails");
+		mutex_enter(&hwahcp->hwahc_mutex);
 
 		goto err;
 	}
@@ -4014,7 +4026,7 @@
 	(void) memset(mas, 0xff, WUSB_SET_WUSB_MAS_LEN);
 	mas[0] = 0xf0;	/* the first 4 slots are for beacons */
 	rval = wusb_hc_set_wusb_mas(&hwahcp->hwahc_hc_data, mas);
-
+	mutex_enter(&hwahcp->hwahc_mutex);
 	if (rval != USB_SUCCESS) {
 		USB_DPRINTF_L2(PRINT_MASK_ATTA, hwahcp->hwahc_log_handle,
 		    "hwahc_hc_initial_start: set wusb mas fails");
@@ -4030,29 +4042,28 @@
 	random_get_pseudo_bytes(dft_gtkid, 3);
 
 	/* set default GTK, need a way to dynamically compute it */
+	mutex_exit(&hwahcp->hwahc_mutex);
 	rval = wusb_hc_set_gtk(&hwahcp->hwahc_hc_data, dft_gtk, dft_gtkid);
 	if (rval != USB_SUCCESS) {
 		USB_DPRINTF_L2(PRINT_MASK_ATTA, hwahcp->hwahc_log_handle,
 		    "hwahc_hc_initial_start: set gtk fails");
+		mutex_enter(&hwahcp->hwahc_mutex);
 
 		goto err;
 	}
 
 	/* enable wire adapter */
-	mutex_exit(&hwahcp->hwahc_mutex);
 	rval = wusb_wa_enable(&hwahcp->hwahc_wa_data,
 	    hwahcp->hwahc_default_pipe);
-	mutex_enter(&hwahcp->hwahc_mutex);
-
 	if (rval != USB_SUCCESS) {
 		USB_DPRINTF_L2(PRINT_MASK_ATTA, hwahcp->hwahc_log_handle,
 		    "hwahc_hc_initial_start: enable wa fails");
+		mutex_enter(&hwahcp->hwahc_mutex);
 
 		goto err;
 	}
 
 	/* Start Notification endpoint */
-	mutex_exit(&hwahcp->hwahc_mutex);
 	rval = wusb_wa_start_nep(&hwahcp->hwahc_wa_data, USB_FLAGS_SLEEP);
 
 	if (rval != USB_SUCCESS) {
@@ -4104,7 +4115,9 @@
 		wusb_hc_free_cluster_id(cluster_id);
 	}
 
+	mutex_exit(&hwahcp->hwahc_mutex);
 	(void) uwb_stop_beacon(hwahcp->hwahc_dip);
+	mutex_enter(&hwahcp->hwahc_mutex);
 
 	return (rval);
 }
@@ -4159,7 +4172,6 @@
 		mutex_exit(&hwahcp->hwahc_mutex);
 		(void) wusb_wa_disable(&hwahcp->hwahc_wa_data,
 		    hwahcp->hwahc_default_pipe);
-		mutex_enter(&hwahcp->hwahc_mutex);
 
 		/* stop beaconing. Not necessary to unreserve mas */
 		(void) uwb_stop_beacon(hwahcp->hwahc_dip);
@@ -4167,12 +4179,11 @@
 		wusb_hc_rem_host_info(&hwahcp->hwahc_hc_data);
 
 		/* Manually remove all connected children */
-		mutex_exit(&hwahcp->hwahc_mutex);
 		hwahc_run_callbacks(hwahcp, USBA_EVENT_TAG_HOT_REMOVAL);
-		mutex_enter(&hwahcp->hwahc_mutex);
 
 		/* delete all the children */
 		(void) hwahc_cleanup_child(hwahcp->hwahc_dip);
+		mutex_enter(&hwahcp->hwahc_mutex);
 	}
 
 	/*
@@ -4233,11 +4244,13 @@
 	/* set stream idx */
 	stream_idx = 1;
 
+	mutex_exit(&hwahcp->hwahc_mutex);
 	rval = wusb_hc_set_stream_idx(&hwahcp->hwahc_hc_data, stream_idx);
 	if (rval != USB_SUCCESS) {
 		USB_DPRINTF_L2(PRINT_MASK_ATTA, hwahcp->hwahc_log_handle,
 		    "hwahc_hc_channel_start: set stream idx %d fails",
 		    stream_idx);
+		mutex_enter(&hwahcp->hwahc_mutex);
 
 		return (rval);
 	}
@@ -4250,6 +4263,7 @@
 	if (rval != USB_SUCCESS) {
 		USB_DPRINTF_L2(PRINT_MASK_ATTA, hwahcp->hwahc_log_handle,
 		    "hwahc_hc_channel_start: set wusb mas fails");
+		mutex_enter(&hwahcp->hwahc_mutex);
 
 		return (rval);
 	}
@@ -4261,10 +4275,12 @@
 	if (rval != USB_SUCCESS) {
 		USB_DPRINTF_L2(PRINT_MASK_ATTA, hwahcp->hwahc_log_handle,
 		    "hwahc_hc_channel_start: add hostinfo ie fails");
+		mutex_enter(&hwahcp->hwahc_mutex);
 
 		return (rval);
 	}
 
+	mutex_enter(&hwahcp->hwahc_mutex);
 	hwahcp->hwahc_hw_state = HWAHC_HW_STARTED;
 	hwahcp->hwahc_hc_soft_state = HWAHC_CTRL_OPERATIONAL_STATE;
 
@@ -4317,11 +4333,12 @@
 	/* send host disconect IE so that the children know to disconnect */
 	mutex_exit(&hwahcp->hwahc_mutex);
 	rval = wusb_hc_send_host_disconnect(&hwahcp->hwahc_hc_data);
-	mutex_enter(&hwahcp->hwahc_mutex);
 	if (rval != USB_SUCCESS) {
 		USB_DPRINTF_L2(PRINT_MASK_ATTA, hwahcp->hwahc_log_handle,
 		    "hwahc_hc_channel_stop: send host disconnect ie fails");
 
+		mutex_enter(&hwahcp->hwahc_mutex);
+
 		return (rval);
 	}
 
@@ -4335,6 +4352,7 @@
 	if (rval != USB_SUCCESS) {
 		USB_DPRINTF_L2(PRINT_MASK_ATTA, hwahcp->hwahc_log_handle,
 		    "hwahc_hc_channel_stop: set stream idx 0 fails");
+		mutex_enter(&hwahcp->hwahc_mutex);
 
 		return (rval);
 	}
@@ -4346,9 +4364,12 @@
 	if (rval != USB_SUCCESS) {
 		USB_DPRINTF_L2(PRINT_MASK_ATTA, hwahcp->hwahc_log_handle,
 		    "hwahc_hc_channel_stop: set null wusb mas fails");
+		mutex_enter(&hwahcp->hwahc_mutex);
 
 		return (rval);
 	}
+
+	mutex_enter(&hwahcp->hwahc_mutex);
 	(void) memcpy(hwahcp->hwahc_hc_data.hc_mas, mas, WUSB_SET_WUSB_MAS_LEN);
 
 	hwahcp->hwahc_hw_state = HWAHC_HW_CH_STOPPED;
@@ -4897,6 +4918,7 @@
 
 	USB_DPRINTF_L4(PRINT_MASK_ATTA, hwahcp->hwahc_log_handle,
 	    "hwahc_stop_result_thread: reset hwa bulk-in pipe");
+	mutex_exit(&hwahcp->hwahc_mutex);
 	usb_pipe_reset(wa_data->wa_dip, wa_data->wa_bulkin_ph,
 	    USB_FLAGS_SLEEP, NULL, NULL);
 
@@ -4908,6 +4930,7 @@
 	    "hwahc_stop_result_thread: close hwa bulk-in pipe");
 	usb_pipe_close(wa_data->wa_dip, wa_data->wa_bulkin_ph,
 	    USB_FLAGS_SLEEP, NULL, NULL);
+	mutex_enter(&hwahcp->hwahc_mutex);
 
 	mutex_enter(&wa_data->wa_mutex);
 	wa_data->wa_bulkin_ph = NULL;
@@ -5178,8 +5201,10 @@
 		mutex_enter(&hc_data->hc_mutex);
 		if (dn_notif->bSourceDeviceAddr ==
 		    hc_data->hc_alive_ie.bDeviceAddress[0]) {
+			mutex_exit(&hc_data->hc_mutex);
 			wusb_hc_rem_ie(hc_data,
 			    (wusb_ie_header_t *)&hc_data->hc_alive_ie);
+			mutex_enter(&hc_data->hc_mutex);
 		}
 		mutex_exit(&hc_data->hc_mutex);
 
@@ -5441,12 +5466,15 @@
 {
 	wusb_dev_info_t *dev = (wusb_dev_info_t *)arg;
 	usb_port_t port;
+	uint16_t   dev_addr;
 	wusb_hc_data_t *hc_data = dev->wdev_hc;
 	uint8_t	retry = 3;
+	int rval;
 
 	mutex_enter(&hc_data->hc_mutex);
 
 	dev->wdev_trust_timer = 0;
+	dev_addr = dev->wdev_addr;
 
 	if (dev->wdev_active == 1) {
 	/* device is active during the past period. Restart the timer */
@@ -5454,8 +5482,12 @@
 	} else {
 		/* send a KeepAlive IE to query the device */
 		for (retry = 0; retry < 3; retry++) {
-			if (wusb_hc_send_keepalive_ie(hc_data,
-			    dev->wdev_addr) == USB_SUCCESS) {
+			mutex_exit(&hc_data->hc_mutex);
+			rval = wusb_hc_send_keepalive_ie(hc_data,
+			    dev_addr);
+			mutex_enter(&hc_data->hc_mutex);
+
+			if (rval == USB_SUCCESS) {
 				break;
 			}
 			/* retry 3 times if fail to send KeepAlive IE */
--- a/usr/src/uts/common/io/usb/hwa/hwahc/hwahc_util.c	Fri Jun 05 10:27:16 2009 -0400
+++ b/usr/src/uts/common/io/usb/hwa/hwahc/hwahc_util.c	Fri Jun 05 10:28:40 2009 -0400
@@ -288,8 +288,12 @@
 
 		/* if active, abort the requests */
 		if (hdl->rp_state == WA_RPIPE_STATE_ACTIVE) {
+			mutex_exit(&hdl->rp_mutex);
+			mutex_exit(&hwahcp->hwahc_mutex);
 			rval = wusb_wa_rpipe_abort(hwahcp->hwahc_dip,
 			    hwahcp->hwahc_default_pipe, hdl);
+			mutex_enter(&hwahcp->hwahc_mutex);
+			mutex_enter(&hdl->rp_mutex);
 		}
 		mutex_exit(&hdl->rp_mutex);
 
@@ -303,7 +307,10 @@
 	switch (pp->pp_state) {
 	case HWAHC_PIPE_STATE_CLOSE:
 		completion_reason = USB_CR_PIPE_CLOSING;
+
+		mutex_exit(&hwahcp->hwahc_mutex);
 		(void) wusb_wa_rpipe_reset(hwahcp->hwahc_dip, ph, hdl, 0);
+		mutex_enter(&hwahcp->hwahc_mutex);
 
 		break;
 	case HWAHC_PIPE_STATE_RESET:
@@ -487,18 +494,14 @@
 
 	mutex_exit(&hwahcp->hwahc_mutex);
 	/* target the rpipe to the endpoint */
-	mutex_enter(&wa->wa_mutex);
 	rval = wusb_wa_set_rpipe_target(hwahcp->hwahc_dip, wa,
 	    hwahcp->hwahc_default_pipe, ph, pp->pp_rp);
-	mutex_exit(&wa->wa_mutex);
 	mutex_enter(&hwahcp->hwahc_mutex);
 
 	if (rval != USB_SUCCESS) {
 		USB_DPRINTF_L2(PRINT_MASK_HCDI, hwahcp->hwahc_log_handle,
 		    "hwahc_hcdi_pipe_open: set target for rpipe failed");
-		mutex_enter(&wa->wa_mutex);
 		wusb_wa_release_rpipe(wa, pp->pp_rp);
-		mutex_exit(&wa->wa_mutex);
 		kmem_free(pp, sizeof (hwahc_pipe_private_t));
 		mutex_exit(&hwahcp->hwahc_mutex);
 
@@ -536,7 +539,6 @@
 	hwahc_state_t		*hwahcp;
 	hwahc_pipe_private_t	*pp;
 	usb_ep_descr_t		*epdt = &ph->p_ep;
-	wusb_wa_data_t		*wa;
 
 	hwahcp = hwahc_obtain_state(ph->p_usba_device->usb_root_hub_dip);
 
@@ -559,10 +561,7 @@
 	wusb_wa_clear_dev_ep(ph); /* clear the remote dev's endpoint */
 	mutex_enter(&hwahcp->hwahc_mutex);
 
-	wa = &hwahcp->hwahc_wa_data;
-	mutex_enter(&wa->wa_mutex);
 	wusb_wa_release_rpipe(&hwahcp->hwahc_wa_data, pp->pp_rp);
-	mutex_exit(&wa->wa_mutex);
 
 	mutex_enter(&ph->p_mutex);
 	cv_destroy(&pp->pp_xfer_cmpl_cv);
--- a/usr/src/uts/common/io/usb/usba/wa.c	Fri Jun 05 10:27:16 2009 -0400
+++ b/usr/src/uts/common/io/usb/usba/wa.c	Fri Jun 05 10:28:40 2009 -0400
@@ -106,9 +106,11 @@
 		mutex_init(&hdl->rp_mutex, NULL, MUTEX_DRIVER, NULL);
 		cv_init(&hdl->rp_cv, NULL, CV_DRIVER, NULL);
 
+		_NOTE(NOW_INVISIBLE_TO_OTHER_THREADS(*hdl));
 		hdl->rp_state = WA_RPIPE_STATE_FREE;
 		hdl->rp_refcnt = 0;
 		hdl->rp_timeout_list = NULL;
+		_NOTE(NOW_VISIBLE_TO_OTHER_THREADS(*hdl));
 	}
 }
 
@@ -149,6 +151,8 @@
 		return (USB_INVALID_ARGS);
 	}
 
+	_NOTE(NOW_INVISIBLE_TO_OTHER_THREADS(*wa_data));
+
 	/* get inf descr and ept descrs from altif data */
 	altif_data = &dev_data->dev_curr_cfg->
 	    cfg_if[dev_data->dev_curr_if].if_alt[0];
@@ -227,6 +231,8 @@
 
 	mutex_init(&wa_data->wa_mutex, NULL, MUTEX_DRIVER, NULL);
 
+	_NOTE(NOW_VISIBLE_TO_OTHER_THREADS(*wa_data));
+
 	return (USB_SUCCESS);
 }
 
@@ -234,11 +240,13 @@
 void
 wusb_wa_data_fini(wusb_wa_data_t *wa_data)
 {
+	mutex_enter(&wa_data->wa_mutex);
 	if (wa_data->wa_rpipe_hdl) {
 		wusb_wa_rpipes_fini(wa_data);
 		kmem_free(wa_data->wa_rpipe_hdl, wa_data->wa_num_rpipes *
 		    sizeof (wusb_wa_rpipe_hdl_t));
 	}
+	mutex_exit(&wa_data->wa_mutex);
 	mutex_destroy(&wa_data->wa_mutex);
 }
 
@@ -354,6 +362,9 @@
 		return (USB_INVALID_ARGS);
 	}
 
+	/* called at initialization, no other threads yet */
+	_NOTE(NOW_INVISIBLE_TO_OTHER_THREADS(*wa_data));
+
 	for (i = 0; i < wa_data->wa_num_rpipes; i++) {
 		rval = wusb_wa_get_rpipe_descr(dip, ph, i,
 		    &wa_data->wa_rpipe_hdl[i].rp_descr, mask, handle);
@@ -366,6 +377,7 @@
 			return (rval);
 		}
 	}
+	_NOTE(NOW_VISIBLE_TO_OTHER_THREADS(*wa_data));
 
 	return (USB_SUCCESS);
 }
@@ -836,15 +848,16 @@
 int
 wusb_wa_release_rpipe(wusb_wa_data_t *wa, wusb_wa_rpipe_hdl_t *hdl)
 {
-	ASSERT(mutex_owned(&wa->wa_mutex));
 	if (hdl == NULL) {
 
 		return (USB_FAILURE);
 	}
 
+	mutex_enter(&wa->wa_mutex);
 	mutex_enter(&hdl->rp_mutex);
 	if (hdl->rp_refcnt == 0) {
 		mutex_exit(&hdl->rp_mutex);
+		mutex_exit(&wa->wa_mutex);
 
 		return (USB_FAILURE);
 	}
@@ -860,6 +873,7 @@
 	}
 
 	mutex_exit(&hdl->rp_mutex);
+	mutex_exit(&wa->wa_mutex);
 
 	return (USB_SUCCESS);
 }
@@ -1037,7 +1051,6 @@
 	uint16_t		maxsize;
 	uint16_t		seg_len;
 
-	ASSERT(mutex_owned(&wa->wa_mutex));
 
 	USB_DPRINTF_L4(DPRINT_MASK_WHCDI, whcdi_log_handle,
 	    "wusb_wa_set_rpipe_target: ph_data = 0x%p rp_hdl = 0x%p",
@@ -1078,6 +1091,7 @@
 		return (USB_FAILURE);
 	}
 
+	mutex_enter(&wa->wa_mutex);
 	usba_device = usba_get_usba_device(ph_data->p_dip);
 
 	mutex_enter(&hdl->rp_mutex);
@@ -1106,6 +1120,7 @@
 		/* WA don't have so many blocks to fulfill this reqirement */
 		if (wa->wa_avail_blocks < blockcnt) {
 			mutex_exit(&hdl->rp_mutex);
+			mutex_exit(&wa->wa_mutex);
 
 			return (USB_FAILURE);
 		}
@@ -1156,6 +1171,8 @@
 	hdl->rp_descr.wNumTransactionErrors = 0;
 	mutex_exit(&hdl->rp_mutex);
 
+	mutex_exit(&wa->wa_mutex);
+
 	/* set rpipe descr */
 	rval = wusb_wa_set_rpipe_descr(dip, ph, &hdl->rp_descr);
 	if (rval != USB_SUCCESS) {
@@ -1199,17 +1216,19 @@
 	usb_cb_flags_t	cb_flags;
 	int		rval;
 
-	ASSERT(mutex_owned(&hdl->rp_mutex));
+	mutex_enter(&hdl->rp_mutex);
 
 	USB_DPRINTF_L4(DPRINT_MASK_WHCDI, whcdi_log_handle,
 	    "wusb_wa_rpipe_abort: rp_hdl = 0x%p", (void *)hdl);
 
 	/* only abort when there is active transfer */
 	if (hdl->rp_state != WA_RPIPE_STATE_ACTIVE) {
+		mutex_exit(&hdl->rp_mutex);
 
 		return (USB_SUCCESS);
 	}
 
+	mutex_exit(&hdl->rp_mutex);
 	rval = usb_pipe_sync_ctrl_xfer(dip, ph,
 	    WA_CLASS_RPIPE_REQ_OUT_TYPE,
 	    WA_REQ_ABORT_RPIPE,
@@ -2098,6 +2117,7 @@
 		return;
 	}
 
+
 	for (i = 0; i < wr->wr_nsegs; i++) {
 		seg = &wr->wr_seg_array[i];
 
@@ -2204,8 +2224,10 @@
 	p[7] = (uint8_t)(id >> 24);
 	req->bulk_data->b_wptr += WA_ABORT_REQ_LEN;
 
+	mutex_exit(&wr->wr_rp->rp_mutex);
 	rval = usb_pipe_bulk_xfer(wa_data->wa_bulkout_ph, req,
 	    USB_FLAGS_SLEEP);
+	mutex_enter(&wr->wr_rp->rp_mutex);
 	if (rval != USB_SUCCESS) {
 		USB_DPRINTF_L2(DPRINT_MASK_WHCDI, whcdi_log_handle,
 		    "wusb_wa_abort_req: send abort req failed, rval = %d",
@@ -2360,9 +2382,13 @@
 		wr->wr_state = WR_TIMEOUT;
 
 		wa_data = wr->wr_wa_data;
+
+		mutex_exit(&hdl->rp_mutex);
 		rval = wusb_wa_get_rpipe_status(wa_data->wa_dip,
 		    wa_data->wa_default_pipe, hdl->rp_descr.wRPipeIndex,
 		    &rp_status);
+		mutex_enter(&hdl->rp_mutex);
+
 		if (rval != USB_SUCCESS) {
 			/* reset WA perhaps? */
 			hdl->rp_state = WA_RPIPE_STATE_ERROR;
@@ -2389,8 +2415,10 @@
 			USB_DPRINTF_L3(DPRINT_MASK_WHCDI, whcdi_log_handle,
 			    "wusb_wa_xfer_timeout_handler: rp not idle");
 
+			mutex_exit(&hdl->rp_mutex);
 			rval = wusb_wa_rpipe_abort(wa_data->wa_dip,
 			    wa_data->wa_default_pipe, hdl);
+			mutex_enter(&hdl->rp_mutex);
 
 			USB_DPRINTF_L3(DPRINT_MASK_WHCDI,
 			    whcdi_log_handle,
@@ -2549,8 +2577,10 @@
 		req = wr->wr_seg_array[i].seg_trans_reqp;
 		ASSERT(req != NULL);
 
+		mutex_exit(&hdl->rp_mutex);
 		/* send ith transfer request */
 		rval = usb_pipe_bulk_xfer(wa_data->wa_bulkout_ph, req, 0);
+		mutex_enter(&hdl->rp_mutex);
 		if (rval != USB_SUCCESS) {
 			USB_DPRINTF_L2(DPRINT_MASK_WHCDI, whcdi_log_handle,
 			    "wusb_wa_wr_xfer: send transfer request %d failed,"
@@ -2595,8 +2625,10 @@
 		}
 
 		wr->wr_seg_array[i].seg_data_req_state = 1; /* submitted */
+		mutex_exit(&hdl->rp_mutex);
 		/* send ith data asynchronously */
 		rval = usb_pipe_bulk_xfer(wa_data->wa_bulkout_ph, req, 0);
+		mutex_enter(&hdl->rp_mutex);
 		if (rval != USB_SUCCESS) {
 			USB_DPRINTF_L2(DPRINT_MASK_WHCDI, whcdi_log_handle,
 			    "wusb_wa_wr_xfer: send transfer data %d failed",
@@ -3222,9 +3254,7 @@
 		USB_DPRINTF_L3(DPRINT_MASK_WHCDI, whcdi_log_handle,
 		    "wusb_wa_handle_error: segment err, abort other segs");
 
-		mutex_exit(&wr->wr_rp->rp_mutex);
 		wusb_wa_abort_req(wa_data, wr, wr->wr_id);
-		mutex_enter(&wr->wr_rp->rp_mutex);
 	}
 
 	wusb_wa_stop_xfer_timer(wr);
--- a/usr/src/uts/common/io/usb/usba/whcdi.c	Fri Jun 05 10:27:16 2009 -0400
+++ b/usr/src/uts/common/io/usb/usba/whcdi.c	Fri Jun 05 10:28:40 2009 -0400
@@ -54,6 +54,7 @@
 /* use 0-30 bit as wusb cluster_id bitmaps */
 static uint32_t cluster_id_mask = 0;
 
+_NOTE(MUTEX_PROTECTS_DATA(whcdi_mutex, cluster_id_mask))
 
 usb_log_handle_t	whcdi_log_handle;
 uint_t			whcdi_errlevel = USB_LOG_L4;
@@ -105,10 +106,9 @@
 		/* set the bitmask */
 		cluster_id_mask |= (1 << i);
 		id = WUSB_MIN_CLUSTER_ID + i;
-		mutex_exit(&whcdi_mutex);
-
 		USB_DPRINTF_L3(DPRINT_MASK_WHCDI, whcdi_log_handle,
 		    "new cluster id %d, mask %d", id, cluster_id_mask);
+		mutex_exit(&whcdi_mutex);
 
 		return (id);
 	}
@@ -244,7 +244,9 @@
 		USB_DPRINTF_L2(DPRINT_MASK_WHCDI, whcdi_log_handle,
 		    "Set_Cluster_ID fails: rval=%d ", rval);
 	} else {
+		mutex_enter(&hc_data->hc_mutex);
 		hc_data->hc_cluster_id = cluster_id;
+		mutex_exit(&hc_data->hc_mutex);
 	}
 
 	return (rval);
@@ -321,13 +323,17 @@
 	dev_info_t	*dip = hc_data->hc_dip;
 	int		rval;
 
+	ASSERT(mutex_owned(&hc_data->hc_mutex));
+
 	if ((iehdl >= hc_data->hc_num_mmcies) ||
 	    (hc_data->hc_mmcie_list[iehdl] == NULL)) {
 
 		return (USB_FAILURE);
 	}
 
+	mutex_exit(&hc_data->hc_mutex);
 	rval = hc_data->rem_mmc_ie(dip, iehdl);
+	mutex_enter(&hc_data->hc_mutex);
 	if (rval != USB_SUCCESS) {
 		USB_DPRINTF_L2(DPRINT_MASK_WHCDI, whcdi_log_handle,
 		    "Remove_MMC_IE fails: rval=%d ", rval);
@@ -402,6 +408,7 @@
 	int	i;
 	int16_t	iehdl = -1;
 
+	mutex_enter(&hc_data->hc_mutex);
 	for (i = 0; i < hc_data->hc_num_mmcies; i++) {
 		if (hc_data->hc_mmcie_list[i] == ieh) {
 			iehdl = (int16_t)i;
@@ -413,12 +420,15 @@
 	if (iehdl == -1) {
 		USB_DPRINTF_L2(DPRINT_MASK_WHCDI, whcdi_log_handle,
 		    "wusb_hc_rem_ie: IE(%p) iehdl not found", (void *)ieh);
+		mutex_exit(&hc_data->hc_mutex);
 
 		return;
 	}
 
 	(void) wusb_hc_remove_mmc_ie(hc_data, (uint8_t)iehdl);
+
 	wusb_hc_free_iehdl(hc_data, (uint8_t)iehdl);
+	mutex_exit(&hc_data->hc_mutex);
 }
 
 /* Add Host Info IE */
@@ -431,6 +441,8 @@
 
 	hinfo = kmem_zalloc(sizeof (wusb_ie_host_info_t), KM_SLEEP);
 
+	mutex_enter(&hc_data->hc_mutex);
+
 	hinfo->bIEIdentifier = WUSB_IE_HOSTINFO;
 	hinfo->bLength = sizeof (wusb_ie_host_info_t);
 	if (hc_data->hc_newcon_enabled) {
@@ -446,6 +458,7 @@
 	if (rval != USB_SUCCESS) {
 		USB_DPRINTF_L2(DPRINT_MASK_WHCDI, whcdi_log_handle,
 		    "wusb_hc_add_host_info: get ie handle fails");
+		mutex_exit(&hc_data->hc_mutex);
 
 		return (rval);
 	}
@@ -453,16 +466,20 @@
 	USB_DPRINTF_L3(DPRINT_MASK_WHCDI, whcdi_log_handle,
 	    "wusb_hc_add_host_info: iehdl=%d", iehdl);
 
+	mutex_exit(&hc_data->hc_mutex);
 	rval = wusb_hc_add_mmc_ie(hc_data, 10, 1, iehdl,
 	    sizeof (wusb_ie_host_info_t), (uint8_t *)hinfo);
 	if (rval != USB_SUCCESS) {
 		USB_DPRINTF_L2(DPRINT_MASK_WHCDI, whcdi_log_handle,
 		    "wusb_hc_add_host_info: add host info mmc ie fails");
+		mutex_enter(&hc_data->hc_mutex);
 		wusb_hc_free_iehdl(hc_data, iehdl);
+		mutex_exit(&hc_data->hc_mutex);
 
 		return (rval);
 	}
 
+
 	return (USB_SUCCESS);
 }
 
@@ -667,8 +684,7 @@
 	uint8_t			iehdl;
 	int			rval;
 
-	ASSERT(mutex_owned(&hc_data->hc_mutex));
-
+	mutex_enter(&hc_data->hc_mutex);
 	/*
 	 * the scheme ensures each time only one device addr
 	 * is set each time
@@ -684,6 +700,7 @@
 	if (rval != USB_SUCCESS) {
 		USB_DPRINTF_L2(DPRINT_MASK_WHCDI, whcdi_log_handle,
 		    "wusb_hc_send_keepalive_ie: get ie handle fails");
+		mutex_exit(&hc_data->hc_mutex);
 
 		return (rval);
 	}
@@ -701,7 +718,6 @@
 	if (rval != USB_SUCCESS) {
 		USB_DPRINTF_L2(DPRINT_MASK_WHCDI, whcdi_log_handle,
 		    "wusb_hc_send_keepalive_ie: add keepalive ie fails");
-		mutex_enter(&hc_data->hc_mutex);
 
 		/* no need to free the ack iehdl since it is reused */
 		return (rval);
@@ -719,6 +735,7 @@
 	mutex_enter(&hc_data->hc_mutex);
 	(void) wusb_hc_remove_mmc_ie(hc_data, iehdl);
 	wusb_hc_free_iehdl(hc_data, iehdl);
+	mutex_exit(&hc_data->hc_mutex);
 
 	return (USB_SUCCESS);
 }
@@ -1212,6 +1229,7 @@
 {
 	int16_t		value;
 	int		i, rval;
+	usb_pipe_handle_t dev_ph;
 
 	value = wusb_get_ccm_encryption_value(&dev_info->wdev_secrt_data);
 	if (value == -1) {
@@ -1224,7 +1242,11 @@
 	for (i = 0; i < 1; i++) {
 		USB_DPRINTF_L4(DPRINT_MASK_WHCDI, whcdi_log_handle,
 		    "wusb_test_write %d start:", i);
-		rval = wusb_dev_set_encrypt(dev_info->wdev_ph, (uint8_t)value);
+		mutex_enter(&dev_info->wdev_hc->hc_mutex);
+		dev_ph = dev_info->wdev_ph;
+		mutex_exit(&dev_info->wdev_hc->hc_mutex);
+
+		rval = wusb_dev_set_encrypt(dev_ph, (uint8_t)value);
 		if (rval != USB_SUCCESS) {
 			USB_DPRINTF_L2(DPRINT_MASK_WHCDI, whcdi_log_handle,
 			    "wusb_test_write: %dth set encryption failed", i);
@@ -1237,10 +1259,12 @@
 
 /* enable CCM encryption on the device */
 int
-wusb_enable_dev_encrypt(wusb_dev_info_t *dev_info)
+wusb_enable_dev_encrypt(wusb_hc_data_t *hc_data, wusb_dev_info_t *dev_info)
 {
 	int16_t		value;
 	int		rval;
+	usb_pipe_handle_t ph;
+
 	USB_DPRINTF_L3(DPRINT_MASK_WHCDI, whcdi_log_handle,
 	    "wusb_enable_dev_encrypt:enter");
 
@@ -1252,7 +1276,11 @@
 		return (USB_FAILURE);
 	}
 
-	rval = wusb_dev_set_encrypt(dev_info->wdev_ph, (uint8_t)value);
+	mutex_enter(&hc_data->hc_mutex);
+	ph = dev_info->wdev_ph;
+	mutex_exit(&hc_data->hc_mutex);
+
+	rval = wusb_dev_set_encrypt(ph, (uint8_t)value);
 	if (rval != USB_SUCCESS) {
 		USB_DPRINTF_L2(DPRINT_MASK_WHCDI, whcdi_log_handle,
 		    "wusb_enable_dev_encrypt: set encryption failed");
@@ -1275,6 +1303,7 @@
 {
 	wusb_dev_info_t		*dev_info;
 	usb_pipe_handle_t	child_ph;
+	dev_info_t		*child_dip;
 
 	ASSERT(mutex_owned(&hc_data->hc_mutex));
 
@@ -1288,6 +1317,7 @@
 		return (USB_INVALID_ARGS);
 	}
 	child_ph = dev_info->wdev_ph;
+	child_dip = hc_data->hc_children_dips[port];
 
 	mutex_exit(&hc_data->hc_mutex);
 	/* get device security descrs */
@@ -1304,7 +1334,7 @@
 	 * enable CCM encryption on the device, this needs to be done
 	 * before 4-way handshake. [WUSB 1.0/7.3.2.5]
 	 */
-	if (wusb_enable_dev_encrypt(dev_info) != USB_SUCCESS) {
+	if (wusb_enable_dev_encrypt(hc_data, dev_info) != USB_SUCCESS) {
 		USB_DPRINTF_L2(DPRINT_MASK_WHCDI, whcdi_log_handle,
 		    "wusb_hc_auth_dev: set encryption failed");
 
@@ -1312,11 +1342,12 @@
 		return (USB_FAILURE);
 	}
 
-	mutex_enter(&hc_data->hc_mutex);
 
 	/* this seems to relieve the non-response issue somehow */
-	usb_pipe_close(hc_data->hc_children_dips[port], dev_info->wdev_ph,
+	usb_pipe_close(child_dip, child_ph,
 	    USB_FLAGS_SLEEP | USBA_FLAGS_PRIVILEGED, NULL, NULL);
+
+	mutex_enter(&hc_data->hc_mutex);
 	dev_info->wdev_ph = NULL;
 
 	/* unauthenticated state */
@@ -1346,7 +1377,7 @@
 	    (void *)dev_info->wdev_cc);
 
 	mutex_exit(&hc_data->hc_mutex);
-	if (usb_pipe_open(hc_data->hc_children_dips[port], NULL, NULL,
+	if (usb_pipe_open(child_dip, NULL, NULL,
 	    USB_FLAGS_SLEEP | USBA_FLAGS_PRIVILEGED, &child_ph) !=
 	    USB_SUCCESS) {
 		USB_DPRINTF_L2(DPRINT_MASK_WHCDI, whcdi_log_handle,
@@ -1369,8 +1400,7 @@
 		    port);
 
 		/* perhaps resetting the device is better */
-		usb_pipe_reset(hc_data->hc_children_dips[port],
-		    child_ph,
+		usb_pipe_reset(child_dip, child_ph,
 		    USB_FLAGS_SLEEP | USBA_FLAGS_PRIVILEGED,
 		    NULL, NULL);
 		(void) wusb_dev_set_encrypt(child_ph, 0);
@@ -1658,8 +1688,11 @@
 
 error:
 	if (dev_info->wdev_ph != NULL) {
-		usb_pipe_close(child_dip, dev_info->wdev_ph,
+		mutex_exit(&hc_data->hc_mutex);
+		usb_pipe_close(child_dip, child_ph,
 		    USB_FLAGS_SLEEP | USBA_FLAGS_PRIVILEGED, NULL, NULL);
+		mutex_enter(&hc_data->hc_mutex);
+
 		dev_info->wdev_ph = NULL;
 	}
 
@@ -1674,9 +1707,10 @@
 			    "failure to remove child node");
 		}
 
-		/* no need to unset address for WUSB */
-		child_ud->usb_addr = 0;
+		mutex_exit(&hc_data->hc_mutex);
 		usba_free_usba_device(child_ud);
+		mutex_enter(&hc_data->hc_mutex);
+
 		hc_data->hc_children_dips[port] = NULL;
 		hc_data->hc_usba_devices[port] = NULL;
 	}
@@ -2096,8 +2130,10 @@
 	disconn_ie->bIEIdentifier = WUSB_IE_HOST_DISCONNECT;
 	disconn_ie->bLength = sizeof (wusb_ie_host_disconnect_t);
 
+	mutex_enter(&hc_data->hc_mutex);
 	rval = wusb_hc_get_iehdl(hc_data, (wusb_ie_header_t *)disconn_ie,
 	    &iehdl);
+	mutex_exit(&hc_data->hc_mutex);
 	if (rval != USB_SUCCESS) {
 		USB_DPRINTF_L2(DPRINT_MASK_WHCDI, whcdi_log_handle,
 		    "wusb_hc_send_host_disconnect: get ie handle fails");
@@ -2112,15 +2148,21 @@
 		USB_DPRINTF_L2(DPRINT_MASK_WHCDI, whcdi_log_handle,
 		    "wusb_hc_send_host_disconnect: add host "
 		    "disconnect ie fails");
+		mutex_enter(&hc_data->hc_mutex);
 		wusb_hc_free_iehdl(hc_data, iehdl);
+		mutex_exit(&hc_data->hc_mutex);
 		kmem_free(disconn_ie, sizeof (wusb_ie_host_disconnect_t));
 
 		return (rval);
 	}
 
 	delay(drv_usectohz(100000));	/* WUSB 1.0/7.5.5 */
+
+	mutex_enter(&hc_data->hc_mutex);
 	(void) wusb_hc_remove_mmc_ie(hc_data, iehdl);
 	wusb_hc_free_iehdl(hc_data, iehdl);
+	mutex_exit(&hc_data->hc_mutex);
+
 	kmem_free(disconn_ie, sizeof (wusb_ie_host_disconnect_t));
 
 	return (USB_SUCCESS);
@@ -2435,6 +2477,8 @@
 	int		rval;
 	uint8_t		*p;
 
+	ASSERT(mutex_owned(&hc_data->hc_mutex));
+
 	if ((key_data == NULL) || (dev_info == NULL)) {
 
 		return (USB_INVALID_ARGS);
@@ -2449,6 +2493,8 @@
 	p = &key_descr->KeyData[0];
 	(void) memcpy(p, key_data, 16);
 
+	mutex_exit(&hc_data->hc_mutex);
+
 	if ((rval = hc_data->set_ptk(dip, key_descr, klen, port)) !=
 	    USB_SUCCESS) {
 		USB_DPRINTF_L2(DPRINT_MASK_WHCDI, whcdi_log_handle,
@@ -2456,6 +2502,7 @@
 	}
 
 	kmem_free(key_descr, klen);
+	mutex_enter(&hc_data->hc_mutex);
 
 	return (rval);
 }
@@ -2649,21 +2696,27 @@
 	uchar_t			adata2[] = "out-of-bandMIC";
 	uchar_t			bdata[32], keyout[32], mic[8];
 	int			rval;
+	usb_pipe_handle_t	w_ph;
 
 	USB_DPRINTF_L4(DPRINT_MASK_WHCDI, whcdi_log_handle,
 	    "wusb_4way_handshake: port = %d", port);
 
+	mutex_enter(&hc_data->hc_mutex);
 	dev_info = hc_data->hc_dev_infos[port];
 	if (dev_info == NULL) {
+		mutex_exit(&hc_data->hc_mutex);
 
 		return (USB_FAILURE);
 	}
 	cc = dev_info->wdev_cc;
 	if (dev_info->wdev_ph == NULL || cc == NULL) {
+		mutex_exit(&hc_data->hc_mutex);
 
 		return (USB_FAILURE);
 	}
 
+	w_ph = dev_info->wdev_ph;
+
 	hs = (wusb_hndshk_data_t *)kmem_zalloc(
 	    3 * sizeof (wusb_hndshk_data_t), KM_SLEEP);
 
@@ -2683,6 +2736,7 @@
 	    != USB_SUCCESS) {
 		USB_DPRINTF_L2(DPRINT_MASK_WHCDI, whcdi_log_handle,
 		    "Nonce generation failed: %d", rval);
+		mutex_exit(&hc_data->hc_mutex);
 
 		goto done;
 	}
@@ -2691,7 +2745,8 @@
 	USB_DPRINTF_L4(DPRINT_MASK_WHCDI, whcdi_log_handle,
 	    "wusb_4way_handshake: shake 1.............");
 
-	rval = wusb_handshake(dev_info->wdev_ph, &(hs[0]), 1);
+	mutex_exit(&hc_data->hc_mutex);
+	rval = wusb_handshake(w_ph, &(hs[0]), 1);
 	if (rval != USB_SUCCESS) {
 		USB_DPRINTF_L2(DPRINT_MASK_WHCDI, whcdi_log_handle,
 		    "handshake 1 failed, rval = %d", rval);
@@ -2702,7 +2757,7 @@
 	/* handshake 2 */
 	USB_DPRINTF_L4(DPRINT_MASK_WHCDI, whcdi_log_handle,
 	    "wusb_4way_handshake: shake 2.............");
-	rval = wusb_handshake(dev_info->wdev_ph, &(hs[1]), 2);
+	rval = wusb_handshake(w_ph, &(hs[1]), 2);
 	if (rval != USB_SUCCESS) {
 		USB_DPRINTF_L2(DPRINT_MASK_WHCDI, whcdi_log_handle,
 		    "handshake 2 failed, rval = %d", rval);
@@ -2721,10 +2776,14 @@
 	/* derived session keys, refer to WUSB 1.0/6.5.1 */
 	n.sfn = 0;
 	n.tkid = tkid[0] | (tkid[1]<<8) | (tkid[2] << 16);
+
+	mutex_enter(&hc_data->hc_mutex);
 	n.daddr = dev_info->wdev_addr;
+
 	n.saddr = hc_data->hc_addr;
 	bcopy(hs[0].Nonce, bdata, 16);
 	bcopy(hs[1].Nonce, bdata + 16, 16);
+	mutex_exit(&hc_data->hc_mutex);
 
 	rval = PRF_256(cc->CK, 16, &n, adata1, 14, bdata, 32, keyout);
 	if (rval != 0) {
@@ -2769,7 +2828,7 @@
 
 	USB_DPRINTF_L4(DPRINT_MASK_WHCDI, whcdi_log_handle,
 	    "wusb_4way_handshake: shake 3.............");
-	rval = wusb_handshake(dev_info->wdev_ph, &(hs[2]), 3);
+	rval = wusb_handshake(w_ph, &(hs[2]), 3);
 	if (rval != USB_SUCCESS) {
 		USB_DPRINTF_L2(DPRINT_MASK_WHCDI, whcdi_log_handle,
 		    "handshake 3 failed, rval = %d", rval);
@@ -2777,12 +2836,14 @@
 		goto done;
 	}
 
+	mutex_enter(&hc_data->hc_mutex);
 	/* set PTK for host */
 	(void) memcpy(dev_info->wdev_ptk, keyout + 16, 16);
 
 	USB_DPRINTF_L4(DPRINT_MASK_WHCDI, whcdi_log_handle,
 	    "wusb_4way_handshake: set ptk .............");
 	rval = wusb_hc_set_ptk(hc_data, dev_info->wdev_ptk, port);
+	mutex_exit(&hc_data->hc_mutex);
 	if (rval != USB_SUCCESS) {
 		USB_DPRINTF_L2(DPRINT_MASK_WHCDI, whcdi_log_handle,
 		    "set ptk for host failed, rval = %d", rval);
@@ -2809,7 +2870,7 @@
 	 * set GTK for device
 	 * GTK is initialized when hc_data is inited
 	 */
-	rval = wusb_dev_set_key(dev_info->wdev_ph, 2 << 4,
+	rval = wusb_dev_set_key(w_ph, 2 << 4,
 	    &hc_data->hc_gtk, hc_data->hc_gtk.bLength);
 done:
 	kmem_free(hs, 3 * sizeof (wusb_hndshk_data_t));
--- a/usr/src/uts/common/io/warlock/hid_with_usba.wlcmd	Fri Jun 05 10:27:16 2009 -0400
+++ b/usr/src/uts/common/io/warlock/hid_with_usba.wlcmd	Fri Jun 05 10:28:40 2009 -0400
@@ -46,6 +46,7 @@
 root	usb_parse_CV_cfg_descr
 root	usb_parse_CV_ep_descr
 root	usb_parse_CV_if_descr
+root	usb_parse_comp_ep_descr
 root	usb_pipe_get_private
 root	usb_get_current_frame_number
 root	usb_get_max_isoc_pkts
@@ -191,6 +192,7 @@
 add ehci_trans_wrapper::tw_handle_qtd targets ehci_handle_bulk_qtd
 add ehci_trans_wrapper::tw_handle_qtd targets ehci_handle_intr_qtd
 
+add hubd::h_cleanup_child targets warlock_dummy
 add usba_hcdi_ops::usba_hcdi_console_input_init targets \
                                                 ohci_hcdi_polled_input_init
 add usba_hcdi_ops::usba_hcdi_console_input_fini targets \
--- a/usr/src/uts/common/io/warlock/scsa2usb.wlcmd	Fri Jun 05 10:27:16 2009 -0400
+++ b/usr/src/uts/common/io/warlock/scsa2usb.wlcmd	Fri Jun 05 10:28:40 2009 -0400
@@ -19,11 +19,10 @@
 # CDDL HEADER END
 #
 #
-# Copyright 2006 Sun Microsystems, Inc.  All rights reserved.
+# Copyright 2009 Sun Microsystems, Inc.  All rights reserved.
 # Use is subject to license terms.
 #
 
-#ident	"%Z%%M%	%I%	%E% SMI"
 
 one scsa2usb_state
 
@@ -33,7 +32,6 @@
 root	scsa2usb_scsi_destroy_pkt
 root	scsa2usb_reconnect_event_cb
 root	scsa2usb_disconnect_event_cb
-root	scsa2usb_null_free
 root	scsa2usb_panic_callb
 root	scsa2usb_work_thread
 
--- a/usr/src/uts/common/io/warlock/scsa2usb_with_usba.wlcmd	Fri Jun 05 10:27:16 2009 -0400
+++ b/usr/src/uts/common/io/warlock/scsa2usb_with_usba.wlcmd	Fri Jun 05 10:28:40 2009 -0400
@@ -19,10 +19,9 @@
 # CDDL HEADER END
 #
 #
-# Copyright 2008 Sun Microsystems, Inc.  All rights reserved.
+# Copyright 2009 Sun Microsystems, Inc.  All rights reserved.
 # Use is subject to license terms.
 #
-#ident	"%Z%%M%	%I%	%E% SMI"
 
 one	ohci_state
 one	ehci_state
@@ -51,6 +50,7 @@
 root	usb_parse_CV_cfg_descr
 root	usb_parse_CV_ep_descr
 root	usb_parse_CV_if_descr
+root	usb_parse_comp_ep_descr
 root	usb_pipe_get_private
 root	usb_get_current_frame_number
 root	usb_get_max_isoc_pkts
@@ -125,6 +125,9 @@
 
 root	ohci_intr
 root	ehci_intr
+root    ohci_quiesce
+root    uhci_quiesce
+root    ehci_quiesce
 
 root    usba_dbuf_tail
 root	usb_log
@@ -142,6 +145,7 @@
 add usba_pipe_async_req::sync_func targets usba_pipe_sync_close
 add usba_pipe_async_req::sync_func targets usba_pipe_sync_reset
 
+add hubd::h_cleanup_child targets warlock_dummy
 add usb_isoc_req::isoc_cb		targets warlock_dummy
 add usb_isoc_req::isoc_exc_cb		targets warlock_dummy
 add usba_pipe_async_req::callback	targets warlock_dummy
@@ -169,7 +173,6 @@
 
 root	scsa2usb_reconnect_event_cb
 root	scsa2usb_disconnect_event_cb
-root	scsa2usb_null_free
 root	scsa2usb_work_thread
 root	scsa2usb_panic_callb
 
--- a/usr/src/uts/common/io/warlock/ugen_with_usba.wlcmd	Fri Jun 05 10:27:16 2009 -0400
+++ b/usr/src/uts/common/io/warlock/ugen_with_usba.wlcmd	Fri Jun 05 10:28:40 2009 -0400
@@ -18,10 +18,9 @@
 #
 # CDDL HEADER END
 #
-# Copyright 2007 Sun Microsystems, Inc.  All rights reserved.
+# Copyright 2009 Sun Microsystems, Inc.  All rights reserved.
 # Use is subject to license terms.
 #
-#ident	"%Z%%M%	%I%	%E% SMI"
 
 one ohci_state
 one ehci_state
@@ -45,6 +44,7 @@
 root	usb_parse_CV_cfg_descr
 root	usb_parse_CV_ep_descr
 root	usb_parse_CV_if_descr
+root	usb_parse_comp_ep_descr
 root	usb_pipe_get_private
 root	usb_get_max_isoc_pkts
 root	usb_pipe_set_private
@@ -109,6 +109,9 @@
 root    hubd_reconnect_event_cb
 root	hubd_root_hub_cleanup_thread
 root	hubd_bus_power
+root	ehci_quiesce
+root	uhci_quiesce
+root	ohci_quiesce
 
 ### specify the ugen root functions
 root    ugen_skel_disconnect_ev_cb
@@ -126,6 +129,7 @@
 add usb_ctrl_req::ctrl_exc_cb targets warlock_dummy
 add usb_intr_req::intr_cb targets warlock_dummy
 add usb_intr_req::intr_exc_cb targets warlock_dummy
+add hubd::h_cleanup_child targets warlock_dummy
 
 add usba_pm_req::cb targets warlock_dummy
 add ohci_trans_wrapper::tw_handle_td targets ohci_handle_bulk_td
--- a/usr/src/uts/common/io/warlock/usb_ac_with_usba.wlcmd	Fri Jun 05 10:27:16 2009 -0400
+++ b/usr/src/uts/common/io/warlock/usb_ac_with_usba.wlcmd	Fri Jun 05 10:28:40 2009 -0400
@@ -47,6 +47,7 @@
 root	usb_parse_CV_cfg_descr
 root	usb_parse_CV_ep_descr
 root	usb_parse_CV_if_descr
+root	usb_parse_comp_ep_descr
 root	usb_pipe_get_private
 root	usb_get_current_frame_number
 root	usb_get_max_isoc_pkts
@@ -94,6 +95,10 @@
 root	hcdi_cb_thread
 root    hcdi_shared_cb_thread
 
+root    ohci_quiesce
+root    uhci_quiesce
+root    ehci_quiesce
+
 root 	usb_ugen_attach
 root    usb_ugen_close
 root    usb_ugen_detach
@@ -164,6 +169,7 @@
 root usb_ac_stop_record
 root usb_ac_teardown
 
+add hubd::h_cleanup_child targets warlock_dummy
 add usba_pipe_async_req::sync_func targets usba_pipe_sync_close
 add usba_pipe_async_req::sync_func targets usba_pipe_sync_reset
 
--- a/usr/src/uts/common/io/warlock/usb_ia_with_usba.wlcmd	Fri Jun 05 10:27:16 2009 -0400
+++ b/usr/src/uts/common/io/warlock/usb_ia_with_usba.wlcmd	Fri Jun 05 10:28:40 2009 -0400
@@ -1,5 +1,5 @@
 #
-# Copyright 2007 Sun Microsystems, Inc.  All rights reserved.
+# Copyright 2009 Sun Microsystems, Inc.  All rights reserved.
 # Use is subject to license terms.
 #
 # CDDL HEADER START
@@ -21,7 +21,6 @@
 #
 # CDDL HEADER END
 #
-#ident	"%Z%%M%	%I%	%E% SMI"
 
 
 one ohci_state
@@ -51,6 +50,7 @@
 root	usb_parse_CV_cfg_descr
 root	usb_parse_CV_ep_descr
 root	usb_parse_CV_if_descr
+root    usb_parse_comp_ep_descr
 root	usb_pipe_reset
 root	usb_pipe_get_private
 root	usb_get_current_frame_number
@@ -142,6 +142,9 @@
 root	hcdi_cb_thread
 root    hcdi_shared_cb_thread
 
+root	ohci_quiesce
+root	uhci_quiesce
+root	ehci_quiesce
 
 root	usba_pipe_do_async_func_thread
 root	usba_get_hc_dma_attr
@@ -152,6 +155,7 @@
 root	usba_mk_mctl
 root	usb_fail_checkpoint
 
+add hubd::h_cleanup_child targets warlock_dummy
 add usb_bulk_req::bulk_cb	targets warlock_dummy
 add usb_bulk_req::bulk_exc_cb	targets warlock_dummy
 add usb_ctrl_req::ctrl_cb	targets warlock_dummy
--- a/usr/src/uts/common/io/warlock/usb_mid_with_usba.wlcmd	Fri Jun 05 10:27:16 2009 -0400
+++ b/usr/src/uts/common/io/warlock/usb_mid_with_usba.wlcmd	Fri Jun 05 10:28:40 2009 -0400
@@ -1,5 +1,5 @@
 #
-# Copyright 2007 Sun Microsystems, Inc.  All rights reserved.
+# Copyright 2009 Sun Microsystems, Inc.  All rights reserved.
 # Use is subject to license terms.
 #
 # CDDL HEADER START
@@ -21,7 +21,6 @@
 #
 # CDDL HEADER END
 #
-#ident	"%Z%%M%	%I%	%E% SMI"
 
 
 one ohci_state
@@ -51,6 +50,7 @@
 root	usb_parse_CV_cfg_descr
 root	usb_parse_CV_ep_descr
 root	usb_parse_CV_if_descr
+root	usb_parse_comp_ep_descr
 root	usb_pipe_reset
 root	usb_pipe_get_private
 root	usb_get_current_frame_number
@@ -60,6 +60,9 @@
 root	usba_free_hcdi_ops
 root	ohci_intr
 root	ehci_intr
+root    ohci_quiesce
+root    uhci_quiesce
+root    ehci_quiesce
 
 root    usba_dbuf_tail
 root	usb_log
@@ -140,6 +143,7 @@
 root	usba_mk_mctl
 root	usb_fail_checkpoint
 
+add hubd::h_cleanup_child	targets warlock_dummy
 add usb_bulk_req::bulk_cb	targets warlock_dummy
 add usb_bulk_req::bulk_exc_cb	targets warlock_dummy
 add usb_ctrl_req::ctrl_cb	targets warlock_dummy
--- a/usr/src/uts/common/io/warlock/usbftdi_with_usba.wlcmd	Fri Jun 05 10:27:16 2009 -0400
+++ b/usr/src/uts/common/io/warlock/usbftdi_with_usba.wlcmd	Fri Jun 05 10:28:40 2009 -0400
@@ -47,6 +47,7 @@
 root usb_parse_CV_cfg_descr
 root usb_parse_CV_ep_descr
 root usb_parse_CV_if_descr
+root usb_parse_comp_ep_descr
 root usb_pipe_get_private
 root usb_get_current_frame_number
 root usb_get_max_isoc_pkts
@@ -143,6 +144,9 @@
 
 root ohci_intr
 root ehci_intr
+root ohci_quiesce
+root uhci_quiesce
+root ehci_quiesce
 
 ### specify the uftdi root functions
 
@@ -194,6 +198,7 @@
 add uftdi_state::uf_cb.cb_tx targets usbser_tx_cb
 add uftdi_state::uf_cb.cb_rx targets usbser_rx_cb
 
+add hubd::h_cleanup_child targets warlock_dummy
 add usb_ctrl_req::ctrl_cb	targets warlock_dummy
 add usb_ctrl_req::ctrl_exc_cb	targets warlock_dummy
 add usb_bulk_req::bulk_cb	targets uftdi_bulkin_cb
--- a/usr/src/uts/common/io/warlock/usbprn_with_usba.wlcmd	Fri Jun 05 10:27:16 2009 -0400
+++ b/usr/src/uts/common/io/warlock/usbprn_with_usba.wlcmd	Fri Jun 05 10:28:40 2009 -0400
@@ -1,5 +1,5 @@
 #
-# Copyright 2007 Sun Microsystems, Inc.  All rights reserved.
+# Copyright 2009 Sun Microsystems, Inc.  All rights reserved.
 # Use is subject to license terms.
 #
 # CDDL HEADER START
@@ -21,7 +21,6 @@
 #
 # CDDL HEADER END
 #
-#ident	"%Z%%M%	%I%	%E% SMI"
 
 one ohci_state
 one ehci_state
@@ -45,6 +44,7 @@
 root	usb_parse_CV_cfg_descr
 root	usb_parse_CV_ep_descr
 root	usb_parse_CV_if_descr
+root    usb_parse_comp_ep_descr
 root	usb_pipe_get_private
 root	usb_get_current_frame_number
 root	usb_get_max_isoc_pkts
@@ -94,6 +94,9 @@
 root	hcdi_cb_thread
 root    hcdi_shared_cb_thread
 
+root    ohci_quiesce
+root    uhci_quiesce
+root    ehci_quiesce
 
 root	hubd_restore_state_cb
 root    hubd_disconnect_event_cb
@@ -139,6 +142,7 @@
 root 	usbprn_disconnect_event_cb
 root 	usbprn_power
 
+add hubd::h_cleanup_child targets warlock_dummy
 add usb_ctrl_req::ctrl_cb	targets warlock_dummy
 add usb_ctrl_req::ctrl_exc_cb	targets warlock_dummy
 add usb_bulk_req::bulk_cb	targets usbprn_bulk_xfer_cb
--- a/usr/src/uts/common/io/warlock/usbser_edge_with_usba.wlcmd	Fri Jun 05 10:27:16 2009 -0400
+++ b/usr/src/uts/common/io/warlock/usbser_edge_with_usba.wlcmd	Fri Jun 05 10:28:40 2009 -0400
@@ -1,8 +1,7 @@
 #
-# Copyright 2007 Sun Microsystems, Inc.  All rights reserved.
+# Copyright 2009 Sun Microsystems, Inc.  All rights reserved.
 # Use is subject to license terms.
 #
-#ident	"%Z%%M%	%I%	%E% SMI"
 
 one ohci_state
 one ehci_state
@@ -32,6 +31,7 @@
 root	usb_parse_CV_cfg_descr
 root	usb_parse_CV_ep_descr
 root	usb_parse_CV_if_descr
+root    usb_parse_comp_ep_descr
 root	usb_pipe_get_private
 root	usb_get_current_frame_number
 root	usb_get_max_isoc_pkts
@@ -129,6 +129,9 @@
 
 root	ohci_intr
 root	ehci_intr
+root    ohci_quiesce
+root    uhci_quiesce
+root    ehci_quiesce
 
 ### specify the edgeport root functions
 root usbser_soft_state_size
@@ -188,6 +191,7 @@
 add        edge_port::ep_cb.cb_rx targets usbser_rx_cb
 add        edge_port::ep_cb.cb_status targets usbser_status_cb
 
+add hubd::h_cleanup_child targets warlock_dummy
 add usb_ctrl_req::ctrl_cb	targets warlock_dummy
 add usb_ctrl_req::ctrl_exc_cb	targets warlock_dummy
 add usb_bulk_req::bulk_cb	targets edgesp_bulkin_cb
--- a/usr/src/uts/common/io/warlock/usbser_keyspan_with_usba.wlcmd	Fri Jun 05 10:27:16 2009 -0400
+++ b/usr/src/uts/common/io/warlock/usbser_keyspan_with_usba.wlcmd	Fri Jun 05 10:28:40 2009 -0400
@@ -20,11 +20,10 @@
 #
 #
 #
-# Copyright 2007 Sun Microsystems, Inc.  All rights reserved.
+# Copyright 2009 Sun Microsystems, Inc.  All rights reserved.
 # Use is subject to license terms.
 #
 
-#ident	"%Z%%M%	%I%	%E% SMI"
 
 one ohci_state
 one ehci_state
@@ -53,6 +52,7 @@
 root	usb_parse_CV_cfg_descr
 root	usb_parse_CV_ep_descr
 root	usb_parse_CV_if_descr
+root	usb_parse_comp_ep_descr
 root	usb_pipe_get_private
 root	usb_get_current_frame_number
 root	usb_get_max_isoc_pkts
@@ -150,6 +150,9 @@
 
 root	ohci_intr
 root	ehci_intr
+root    ohci_quiesce
+root    uhci_quiesce
+root    ehci_quiesce
 
 ### specify the keyspan root functions
 root usbser_soft_state_size
@@ -208,6 +211,7 @@
 add        keyspan_port::kp_cb.cb_rx targets usbser_rx_cb
 add        keyspan_port::kp_cb.cb_status targets usbser_status_cb
 
+add hubd::h_cleanup_child	targets warlock_dummy
 add usb_ctrl_req::ctrl_cb	targets warlock_dummy
 add usb_ctrl_req::ctrl_exc_cb	targets warlock_dummy
 add usb_bulk_req::bulk_cb	targets keyspan_bulkin_cb
--- a/usr/src/uts/common/io/warlock/usbskel_with_usba.wlcmd	Fri Jun 05 10:27:16 2009 -0400
+++ b/usr/src/uts/common/io/warlock/usbskel_with_usba.wlcmd	Fri Jun 05 10:28:40 2009 -0400
@@ -1,5 +1,5 @@
 #
-# Copyright 2007 Sun Microsystems, Inc.  All rights reserved.
+# Copyright 2009 Sun Microsystems, Inc.  All rights reserved.
 # Use is subject to license terms.
 #
 # CDDL HEADER START
@@ -21,7 +21,6 @@
 #
 # CDDL HEADER END
 #
-#ident	"%Z%%M%	%I%	%E% SMI"
 
 one ohci_state
 one ehci_state
@@ -46,6 +45,7 @@
 root	usb_parse_CV_cfg_descr
 root	usb_parse_CV_ep_descr
 root	usb_parse_CV_if_descr
+root    usb_parse_comp_ep_descr
 root	usb_pipe_reset
 root	usb_pipe_get_private
 root	usb_get_max_isoc_pkts
@@ -117,7 +117,9 @@
 root	hcdi_autoclearing
 root	hcdi_cb_thread
 root    hcdi_shared_cb_thread
-
+root    ohci_quiesce
+root    uhci_quiesce
+root    ehci_quiesce
 
 root	hubd_bus_power
 root	hubd_hotplug_thread
@@ -139,6 +141,7 @@
 root usbskel_disconnect_callback
 root usbskel_reconnect_callback
 
+add hubd::h_cleanup_child targets warlock_dummy
 add usba_pipe_async_req::sync_func targets usba_pipe_sync_close
 add usba_pipe_async_req::sync_func targets usba_pipe_sync_reset
 
--- a/usr/src/uts/common/io/warlock/usbsprl_with_usba.wlcmd	Fri Jun 05 10:27:16 2009 -0400
+++ b/usr/src/uts/common/io/warlock/usbsprl_with_usba.wlcmd	Fri Jun 05 10:28:40 2009 -0400
@@ -17,10 +17,9 @@
 #
 # CDDL HEADER END
 #
-# Copyright 2007 Sun Microsystems, Inc.  All rights reserved.
+# Copyright 2009 Sun Microsystems, Inc.  All rights reserved.
 # Use is subject to license terms.
 #
-#ident	"%Z%%M%	%I%	%E% SMI"
 
 one ohci_state
 one ehci_state
@@ -47,6 +46,7 @@
 root usb_parse_CV_cfg_descr
 root usb_parse_CV_ep_descr
 root usb_parse_CV_if_descr
+root usb_parse_comp_ep_descr
 root usb_pipe_get_private
 root usb_get_current_frame_number
 root usb_get_max_isoc_pkts
@@ -143,6 +143,9 @@
 
 root ohci_intr
 root ehci_intr
+root    ohci_quiesce
+root    uhci_quiesce
+root    ehci_quiesce
 
 ### specify the pl2303 root functions
 
@@ -194,6 +197,7 @@
 add pl2303_state::pl_cb.cb_tx targets usbser_tx_cb
 add pl2303_state::pl_cb.cb_rx targets usbser_rx_cb
 
+add hubd::h_cleanup_child targets warlock_dummy
 add usb_ctrl_req::ctrl_cb	targets warlock_dummy
 add usb_ctrl_req::ctrl_exc_cb	targets warlock_dummy
 add usb_bulk_req::bulk_cb	targets pl2303_bulkin_cb
--- a/usr/src/uts/common/io/warlock/usbvc_with_usba.wlcmd	Fri Jun 05 10:27:16 2009 -0400
+++ b/usr/src/uts/common/io/warlock/usbvc_with_usba.wlcmd	Fri Jun 05 10:28:40 2009 -0400
@@ -19,10 +19,9 @@
 # CDDL HEADER END
 #
 #
-# Copyright 2007 Sun Microsystems, Inc.  All rights reserved.
+# Copyright 2009 Sun Microsystems, Inc.  All rights reserved.
 # Use is subject to license terms.
 #
-#ident	"%Z%%M%	%I%	%E% SMI"
 
 one ohci_state
 one ehci_state
@@ -46,6 +45,7 @@
 root	usb_parse_CV_cfg_descr
 root	usb_parse_CV_ep_descr
 root	usb_parse_CV_if_descr
+root    usb_parse_comp_ep_descr
 root	usb_pipe_get_private
 root	usb_get_current_frame_number
 root	usb_get_max_isoc_pkts
@@ -139,6 +139,9 @@
 
 root	ohci_intr
 root	ehci_intr
+root    ohci_quiesce
+root    uhci_quiesce
+root    ehci_quiesce
 
 ### specify the usbvc root functions
 root usbvc_open
@@ -151,6 +154,7 @@
 root usbvc_disconnect_event_cb
 root usbvc_reconnect_event_cb
 
+add hubd::h_cleanup_child	targets warlock_dummy
 add usb_ctrl_req::ctrl_cb	targets warlock_dummy
 add usb_ctrl_req::ctrl_exc_cb	targets warlock_dummy
 add usb_isoc_req::isoc_cb	targets usbvc_isoc_cb
--- a/usr/src/uts/common/os/priv.c	Fri Jun 05 10:27:16 2009 -0400
+++ b/usr/src/uts/common/os/priv.c	Fri Jun 05 10:28:40 2009 -0400
@@ -614,18 +614,19 @@
 }
 
 /*
- * Set the privilege aware bit, adding L to E/P if
- * necessasry.
+ * Set the privilege aware bit, adding L to E/P if necessary.
+ * Each time we set it, we also clear PRIV_AWARE_RESET.
  */
 void
 priv_set_PA(cred_t *cr)
 {
 	ASSERT(cr->cr_ref <= 2);
 
-	if (CR_FLAGS(cr) & PRIV_AWARE)
+	if ((CR_FLAGS(cr) & (PRIV_AWARE|PRIV_AWARE_RESET)) == PRIV_AWARE)
 		return;
 
 	CR_FLAGS(cr) |= PRIV_AWARE;
+	CR_FLAGS(cr) &= ~PRIV_AWARE_RESET;
 
 	if (cr->cr_uid == 0)
 		priv_union(&CR_LPRIV(cr), &CR_EPRIV(cr));
@@ -661,8 +662,10 @@
 	ASSERT(cr->cr_ref <= 2);
 
 	if (!(CR_FLAGS(cr) & PRIV_AWARE) ||
-	    !priv_can_clear_PA(cr))
+	    !priv_can_clear_PA(cr)) {
+		CR_FLAGS(cr) &= ~PRIV_AWARE_RESET;
 		return;
+	}
 
 	if (CR_FLAGS(cr) & PRIV_AWARE_INHERIT)
 		return;
@@ -679,5 +682,50 @@
 		priv_intersect(&CR_IPRIV(cr), &CR_PPRIV(cr));
 	}
 
-	CR_FLAGS(cr) &= ~PRIV_AWARE;
+	CR_FLAGS(cr) &= ~(PRIV_AWARE|PRIV_AWARE_RESET);
 }
+
+/*
+ * Reset privilege aware bit if so requested by setting the PRIV_AWARE_RESET
+ * flag.
+ */
+void
+priv_reset_PA(cred_t *cr, boolean_t finalize)
+{
+	ASSERT(cr->cr_ref <= 2);
+
+	if ((CR_FLAGS(cr) & (PRIV_AWARE|PRIV_AWARE_RESET)) !=
+	    (PRIV_AWARE|PRIV_AWARE_RESET)) {
+		CR_FLAGS(cr) &= ~PRIV_AWARE_RESET;
+		return;
+	}
+
+	/*
+	 * When PRIV_AWARE_RESET is enabled, any change of uids causes
+	 * a change to the P and E sets.  Bracketing with
+	 * seteuid(0) ... seteuid(uid)/setreuid(-1, 0) .. setreuid(-1, uid)
+	 * will cause the privilege sets "do the right thing.".
+	 * When the change of the uid is "final", e.g., by using setuid(uid),
+	 * or setreuid(uid, uid) or when the last set*uid() call causes all
+	 * uids to be the same, we set P and E to I & L, like when you exec.
+	 * We make an exception when all the uids are 0; this is required
+	 * when we login as root as in that particular case we cannot
+	 * make a distinction between seteuid(0) and seteuid(uid).
+	 * We rely on seteuid/setreuid/setuid to tell us with the
+	 * "finalize" argument that we no longer expect new uid changes,
+	 * cf. setreuid(uid, uid) and setuid(uid).
+	 */
+	if (cr->cr_suid == cr->cr_ruid && cr->cr_suid == cr->cr_uid) {
+		if (finalize || cr->cr_uid != 0) {
+			CR_EPRIV(cr) = CR_IPRIV(cr);
+			priv_intersect(&CR_LPRIV(cr), &CR_EPRIV(cr));
+			CR_PPRIV(cr) = CR_EPRIV(cr);
+			CR_FLAGS(cr) &= ~(PRIV_AWARE|PRIV_AWARE_RESET);
+		} else {
+			CR_EPRIV(cr) = CR_PPRIV(cr);
+		}
+	} else if (cr->cr_uid != 0 && (cr->cr_ruid == 0 || cr->cr_suid == 0)) {
+		CR_EPRIV(cr) = CR_IPRIV(cr);
+		priv_intersect(&CR_LPRIV(cr), &CR_EPRIV(cr));
+	}
+}
--- a/usr/src/uts/common/sys/fibre-channel/fca/qlc/exioct.h	Fri Jun 05 10:27:16 2009 -0400
+++ b/usr/src/uts/common/sys/fibre-channel/fca/qlc/exioct.h	Fri Jun 05 10:28:40 2009 -0400
@@ -393,14 +393,14 @@
 	UINT16	Bus;				/* 2 */
 	UINT16	Lun;				/* 2 */
 	UINT8	WWPN[EXT_DEF_WWN_NAME_SIZE];	/* 8 */
-	UINT8	Id  [EXT_DEF_PORTID_SIZE];	/* 4; 3 bytes valid Port Id. */
+	UINT8	Id[EXT_DEF_PORTID_SIZE];	/* 4; 3 bytes valid Port Id. */
 	UINT8	PortSupportedFC4Types;		/* 1 */
 	UINT8	PortActiveFC4Types;		/* 1 */
 	UINT8	FabricName[EXT_DEF_WWN_NAME_SIZE];	/* 8 */
 	UINT16	LinkState2;			/* 2; sfp status */
 	UINT16	LinkState3;			/* 2; reserved field */
 	UINT8	Reserved[6];			/* 6 */
-} EXT_HBA_PORT, *PEXT_HBA_PORT;			/* 62 */
+} EXT_HBA_PORT, *PEXT_HBA_PORT;			/* 64 */
 
 /* FC-4 Instrumentation */
 typedef struct _EXT_HBA_FC4Statistics {
@@ -477,6 +477,7 @@
 #define	IIDMA_RATE_2GB		0x1
 #define	IIDMA_RATE_4GB		0x3
 #define	IIDMA_RATE_8GB		0x4
+#define	IIDMA_RATE_10GB		0x13
 #define	IIDMA_RATE_UNKNOWN	0xffff
 
 /* IIDMA Mode values */
@@ -649,6 +650,9 @@
 	UINT32	Reserved[29];					/* 116 */
 } EXT_CNA_PORT, *PEXT_CNA_PORT;					/* 128 */
 
+/* Fabric Parameters */
+#define	EXT_DEF_MAC_ADDR_MODE_FPMA	0x8000
+
 /* Request Buffer for RNID */
 typedef struct _EXT_RNID_REQ {
 	EXT_FC_ADDR	Addr;				/* 14 */
--- a/usr/src/uts/common/sys/fibre-channel/fca/qlc/ql_api.h	Fri Jun 05 10:27:16 2009 -0400
+++ b/usr/src/uts/common/sys/fibre-channel/fca/qlc/ql_api.h	Fri Jun 05 10:28:40 2009 -0400
@@ -1607,6 +1607,12 @@
 	uint16_t		idc_mb[8];
 	uint8_t			restart_mpi_timer;
 	uint8_t			flash_acc_timer;
+
+	/* VLAN ID and MAC address */
+	uint8_t			fcoe_vnport_mac[6];
+	uint16_t		fabric_params;
+	uint16_t		fcoe_vlan_id;
+	uint16_t		fcoe_fcf_idx;
 } ql_adapter_state_t;
 
 /*
--- a/usr/src/uts/common/sys/fibre-channel/fca/qlc/ql_open.h	Fri Jun 05 10:27:16 2009 -0400
+++ b/usr/src/uts/common/sys/fibre-channel/fca/qlc/ql_open.h	Fri Jun 05 10:28:40 2009 -0400
@@ -42,7 +42,7 @@
 #endif
 
 #ifndef QL_VERSION
-#define	QL_VERSION	"20090424-2.35"
+#define	QL_VERSION	"20090519-2.36"
 #endif
 
 #ifndef	QL_NAME
--- a/usr/src/uts/common/sys/ib/mgt/ibmf/ibmf_saa_impl.h	Fri Jun 05 10:27:16 2009 -0400
+++ b/usr/src/uts/common/sys/ib/mgt/ibmf/ibmf_saa_impl.h	Fri Jun 05 10:28:40 2009 -0400
@@ -19,15 +19,13 @@
  * CDDL HEADER END
  */
 /*
- * Copyright 2006 Sun Microsystems, Inc.  All rights reserved.
+ * Copyright 2009 Sun Microsystems, Inc.  All rights reserved.
  * Use is subject to license terms.
  */
 
 #ifndef _SYS_IB_MGT_IBMF_IBMF_SAA_IMPL_H
 #define	_SYS_IB_MGT_IBMF_IBMF_SAA_IMPL_H
 
-#pragma ident	"%Z%%M%	%I%	%E% SMI"
-
 /*
  * saa_impl.h
  */
@@ -43,7 +41,8 @@
 #define	SAA_MAD_BASE_VERSION		1
 #define	SAA_MAD_CLASS_VERSION		2
 #define	IBMF_SAA_RETRANS_RETRIES 	2
-#define	IBMF_SAA_MAX_SUBNET_TIMEOUT 	17
+#define	IBMF_SAA_MAX_SUBNET_TIMEOUT 	20
+#define	IBMF_SAA_MAX_RESP_TIME		20
 #define	IBMF_SAA_MAX_BUSY_RETRY_COUNT	10
 #define	IBMF_SAA_MAX_WAIT_TIME_IN_SECS	60
 #define	IBMF_SAA_TRANS_WAIT_TIME_IN_SECS 240
--- a/usr/src/uts/common/sys/priv.h	Fri Jun 05 10:27:16 2009 -0400
+++ b/usr/src/uts/common/sys/priv.h	Fri Jun 05 10:28:40 2009 -0400
@@ -19,15 +19,13 @@
  * CDDL HEADER END
  */
 /*
- * Copyright 2008 Sun Microsystems, Inc.  All rights reserved.
+ * Copyright 2009 Sun Microsystems, Inc.  All rights reserved.
  * Use is subject to license terms.
  */
 
 #ifndef	_SYS_PRIV_H
 #define	_SYS_PRIV_H
 
-#pragma ident	"%Z%%M%	%I%	%E% SMI"	/* from TSOL 8 */
-
 #include <sys/types.h>
 #include <sys/cred.h>
 #include <sys/priv_names.h>
@@ -137,11 +135,12 @@
 #define	__PROC_PROTECT			0x0008		/* Private */
 #define	NET_MAC_AWARE			0x0010		/* Is MAC aware */
 #define	NET_MAC_AWARE_INHERIT		0x0020		/* Inherit MAC aware */
+#define	PRIV_AWARE_RESET		0x0040		/* Reset on setuid() */
 #define	PRIV_XPOLICY			0x0080		/* Extended policy */
 
 /* user-settable flags: */
 #define	PRIV_USER	(PRIV_DEBUG | NET_MAC_AWARE | NET_MAC_AWARE_INHERIT |\
-			    PRIV_XPOLICY)
+			    PRIV_XPOLICY | PRIV_AWARE_RESET)
 
 /*
  * Header of the privilege info data structure; multiple structures can
@@ -234,6 +233,7 @@
 
 extern void priv_set_PA(cred_t *);
 extern void priv_adjust_PA(cred_t *);
+extern void priv_reset_PA(cred_t *, boolean_t);
 extern boolean_t priv_can_clear_PA(const cred_t *);
 
 extern int setpflags(uint_t, uint_t, cred_t *);
--- a/usr/src/uts/common/sys/usb/hubd/hubdvar.h	Fri Jun 05 10:27:16 2009 -0400
+++ b/usr/src/uts/common/sys/usb/hubd/hubdvar.h	Fri Jun 05 10:28:40 2009 -0400
@@ -260,6 +260,8 @@
 		hubd::h_hubpm
 		hubd::h_dip
 		hubd::h_ignore_pwr_budget
+		hubd::h_hub_descr
+		hubd::h_cleanup_child
 ))
 
 _NOTE(SCHEME_PROTECTS_DATA("stable data", usb_ep_descr))
--- a/usr/src/uts/common/sys/usb/usba/usba_types.h	Fri Jun 05 10:27:16 2009 -0400
+++ b/usr/src/uts/common/sys/usb/usba/usba_types.h	Fri Jun 05 10:28:40 2009 -0400
@@ -234,6 +234,7 @@
 	usb_uwb_cap_descr_t	*uwb_descr;	/* UWB capability descr */
 } usba_wireless_data_t;
 
+
 /*
  * This	structure uniquely identifies a USB device
  * with all interfaces,	or just one interface of a USB device.
@@ -355,6 +356,7 @@
 
 _NOTE(MUTEX_PROTECTS_DATA(usba_device::usb_mutex, usba_device))
 _NOTE(MUTEX_PROTECTS_DATA(usba_device::usb_mutex, usba_evdata))
+_NOTE(MUTEX_PROTECTS_DATA(usba_device::usb_mutex, usba_wireless_data))
 
 _NOTE(SCHEME_PROTECTS_DATA("chg at attach only",
 				usba_evdata::ev_rm_cb_id))
@@ -365,6 +367,13 @@
 _NOTE(SCHEME_PROTECTS_DATA("chg at attach only",
 				usba_evdata::ev_resume_cb_id))
 
+_NOTE(SCHEME_PROTECTS_DATA("chg at attach only",
+				usba_wireless_data::wusb_bos))
+_NOTE(SCHEME_PROTECTS_DATA("chg at attach only",
+				usba_wireless_data::wusb_bos_length))
+_NOTE(SCHEME_PROTECTS_DATA("chg at attach only",
+				usba_wireless_data::uwb_descr))
+
 /* this should be really stable data */
 _NOTE(DATA_READABLE_WITHOUT_LOCK(usba_device::usb_serialno_str))
 _NOTE(DATA_READABLE_WITHOUT_LOCK(usba_device::usb_root_hub_dip))
@@ -398,9 +407,14 @@
 _NOTE(DATA_READABLE_WITHOUT_LOCK(usba_device::usb_dip))
 _NOTE(DATA_READABLE_WITHOUT_LOCK(usba_device::usb_is_wireless))
 _NOTE(DATA_READABLE_WITHOUT_LOCK(usba_device::usb_wireless_data))
+_NOTE(DATA_READABLE_WITHOUT_LOCK(usba_device::usb_is_wa))
 _NOTE(SCHEME_PROTECTS_DATA("set at device creation",
 					usba_device::usb_shared_taskq))
 
+_NOTE(SCHEME_PROTECTS_DATA("local use only",
+				usb_key_descr::bDescriptorType))
+_NOTE(SCHEME_PROTECTS_DATA("local use only",
+				usb_key_descr::bLength))
 /*
  * serialization in drivers
  */
--- a/usr/src/uts/common/sys/usb/usba/whcdi.h	Fri Jun 05 10:27:16 2009 -0400
+++ b/usr/src/uts/common/sys/usb/usba/whcdi.h	Fri Jun 05 10:28:40 2009 -0400
@@ -68,6 +68,7 @@
 _NOTE(DATA_READABLE_WITHOUT_LOCK(wusb_dev_info::wdev_addr))
 _NOTE(DATA_READABLE_WITHOUT_LOCK(wusb_dev_info::wdev_uwb_descr))
 _NOTE(DATA_READABLE_WITHOUT_LOCK(wusb_dev_info::wdev_hc))
+_NOTE(DATA_READABLE_WITHOUT_LOCK(wusb_dev_info::wdev_secrt_data))
 
 /*
  * According to WUSB 1.0 spec, WUSB hosts can support up to 127 devices.
@@ -150,8 +151,31 @@
 _NOTE(MUTEX_PROTECTS_DATA(wusb_hc_data_t::hc_mutex, wusb_hc_data_t))
 
 _NOTE(DATA_READABLE_WITHOUT_LOCK(wusb_hc_data_t::hc_num_ports))
+_NOTE(DATA_READABLE_WITHOUT_LOCK(wusb_hc_data_t::hc_num_mmcies))
 _NOTE(DATA_READABLE_WITHOUT_LOCK(wusb_hc_data_t::hc_dip))
+_NOTE(DATA_READABLE_WITHOUT_LOCK(wusb_hc_data_t::hc_gtk))
+_NOTE(DATA_READABLE_WITHOUT_LOCK(wusb_hc_data_t::add_mmc_ie))
+_NOTE(DATA_READABLE_WITHOUT_LOCK(wusb_hc_data_t::rem_mmc_ie))
+_NOTE(DATA_READABLE_WITHOUT_LOCK(wusb_hc_data_t::set_cluster_id))
+_NOTE(DATA_READABLE_WITHOUT_LOCK(wusb_hc_data_t::set_encrypt))
+_NOTE(DATA_READABLE_WITHOUT_LOCK(wusb_hc_data_t::set_gtk))
+_NOTE(DATA_READABLE_WITHOUT_LOCK(wusb_hc_data_t::set_ptk))
+_NOTE(DATA_READABLE_WITHOUT_LOCK(wusb_hc_data_t::set_num_dnts))
+_NOTE(DATA_READABLE_WITHOUT_LOCK(wusb_hc_data_t::set_stream_idx))
+_NOTE(DATA_READABLE_WITHOUT_LOCK(wusb_hc_data_t::set_wusb_mas))
+_NOTE(DATA_READABLE_WITHOUT_LOCK(wusb_hc_data_t::stop_ch))
+_NOTE(DATA_READABLE_WITHOUT_LOCK(wusb_hc_data_t::create_child))
+_NOTE(DATA_READABLE_WITHOUT_LOCK(wusb_hc_data_t::destroy_child))
+_NOTE(DATA_READABLE_WITHOUT_LOCK(wusb_hc_data_t::disconnect_dev))
+_NOTE(DATA_READABLE_WITHOUT_LOCK(wusb_hc_data_t::reconnect_dev))
+_NOTE(DATA_READABLE_WITHOUT_LOCK(wusb_hc_data_t::get_time))
 
+_NOTE(SCHEME_PROTECTS_DATA("local use only",
+				wusb_ie_host_disconnect::bLength))
+_NOTE(SCHEME_PROTECTS_DATA("local use only",
+				wusb_ie_host_disconnect::bIEIdentifier))
+_NOTE(SCHEME_PROTECTS_DATA("local use only",
+				wusb_ccm_nonce::sfn))
 /*
  * WUSB 1.0 4.3.8.5 says the range of cluster id is in 0x80-0xfe,
  * we limit the maximum WUSB host controller numbers to 31 now,
@@ -258,7 +282,7 @@
 
 /* security functions */
 int	wusb_dev_set_encrypt(usb_pipe_handle_t ph, uint8_t value);
-int	wusb_enable_dev_encrypt(wusb_dev_info_t *dev_info);
+int	wusb_enable_dev_encrypt(wusb_hc_data_t *hc, wusb_dev_info_t *dev_info);
 int	wusb_dev_set_key(usb_pipe_handle_t ph, uint8_t key_index,
 	usb_key_descr_t *key, size_t klen);
 int	wusb_hc_set_encrypt(wusb_hc_data_t *hc_data, usb_port_t port,
--- a/usr/src/uts/common/syscall/ppriv.c	Fri Jun 05 10:27:16 2009 -0400
+++ b/usr/src/uts/common/syscall/ppriv.c	Fri Jun 05 10:28:40 2009 -0400
@@ -19,12 +19,10 @@
  * CDDL HEADER END
  */
 /*
- * Copyright 2008 Sun Microsystems, Inc.  All rights reserved.
+ * Copyright 2009 Sun Microsystems, Inc.  All rights reserved.
  * Use is subject to license terms.
  */
 
-#pragma ident	"%Z%%M%	%I%	%E% SMI"
-
 #include <sys/param.h>
 #include <sys/types.h>
 #include <sys/sysmacros.h>
@@ -238,7 +236,8 @@
 
 	if (val > 1 || (flag != PRIV_DEBUG && flag != PRIV_AWARE &&
 	    flag != NET_MAC_AWARE && flag != NET_MAC_AWARE_INHERIT &&
-	    flag != __PROC_PROTECT && flag != PRIV_XPOLICY)) {
+	    flag != __PROC_PROTECT && flag != PRIV_XPOLICY &&
+	    flag != PRIV_AWARE_RESET)) {
 		return (EINVAL);
 	}
 
@@ -262,10 +261,13 @@
 
 	newflags = CR_FLAGS(pcr);
 
-	if (val != 0)
+	if (val != 0) {
+		if (flag == PRIV_AWARE)
+			newflags &= ~PRIV_AWARE_RESET;
 		newflags |= flag;
-	else
+	} else {
 		newflags &= ~flag;
+	}
 
 	/* No change */
 	if (CR_FLAGS(pcr) == newflags) {
@@ -342,7 +344,7 @@
 {
 	if (flag != PRIV_DEBUG && flag != PRIV_AWARE &&
 	    flag != NET_MAC_AWARE && flag != NET_MAC_AWARE_INHERIT &&
-	    flag != PRIV_XPOLICY)
+	    flag != PRIV_XPOLICY && flag != PRIV_AWARE_RESET)
 		return ((uint_t)-1);
 
 	return ((CR_FLAGS(cr) & flag) != 0);
--- a/usr/src/uts/common/syscall/uid.c	Fri Jun 05 10:27:16 2009 -0400
+++ b/usr/src/uts/common/syscall/uid.c	Fri Jun 05 10:28:40 2009 -0400
@@ -19,7 +19,7 @@
  * CDDL HEADER END
  */
 /*
- * Copyright 2008 Sun Microsystems, Inc.  All rights reserved.
+ * Copyright 2009 Sun Microsystems, Inc.  All rights reserved.
  * Use is subject to license terms.
  */
 
@@ -27,8 +27,6 @@
  * 	Copyright (c) 1984, 1986, 1987, 1988, 1989 AT&T
  */
 
-#pragma ident	"%Z%%M%	%I%	%E% SMI"
-
 #include <sys/param.h>
 #include <sys/types.h>
 #include <sys/sysmacros.h>
@@ -136,6 +134,9 @@
 		newcr->cr_suid = uid;
 		newcr->cr_uid = uid;
 		crsetsid(newcr, ksp, KSID_USER);
+
+		priv_reset_PA(newcr, B_TRUE);
+
 		ASSERT(uid != oldruid ? uidchge : 1);
 		mutex_exit(&p->p_crlock);
 	} else {
@@ -230,6 +231,7 @@
 		p->p_cred = newcr;
 		newcr->cr_uid = uid;
 		crsetsid(newcr, ksp, KSID_USER);
+		priv_reset_PA(newcr, B_FALSE);
 		mutex_exit(&p->p_crlock);
 		if (do_nocd) {
 			mutex_enter(&p->p_lock);
@@ -364,6 +366,7 @@
 		    cr->cr_suid != newcr->cr_suid))
 			do_nocd = 1;
 
+		priv_reset_PA(newcr, ruid != -1 && euid != -1 && ruid == euid);
 		crfree(cr);
 	}
 	mutex_exit(&p->p_crlock);
--- a/usr/src/uts/intel/hid/Makefile	Fri Jun 05 10:27:16 2009 -0400
+++ b/usr/src/uts/intel/hid/Makefile	Fri Jun 05 10:28:40 2009 -0400
@@ -20,7 +20,7 @@
 #
 #
 # uts/intel/hid/Makefile
-# Copyright 2008 Sun Microsystems, Inc.  All rights reserved.
+# Copyright 2009 Sun Microsystems, Inc.  All rights reserved.
 # Use is subject to license terms.
 #
 #
@@ -112,7 +112,7 @@
 #
 #	lock_lint rules
 #
-USBA_FILES = $(USBA_OBJS:%.o=../usba/%.ll)
+USBA_FILES = $(USBA_WITHOUT_WUSB_OBJS:%.o=../usba/%.ll)
 OHCI_FILES = $(OHCI_OBJS:%.o=../ohci/%.ll)
 UHCI_FILES = $(UHCI_OBJS:%.o=../uhci/%.ll)
 EHCI_FILES = $(EHCI_OBJS:%.o=../ehci/%.ll)
@@ -148,6 +148,7 @@
 ehci_files:
 	@cd ../ehci;pwd; $(MAKE) warlock
 
+
 warlock_ddi.files:
 	cd ../warlock; pwd; $(MAKE) warlock
 
--- a/usr/src/uts/intel/io/intel_nb5000/intel_nb5000.c	Fri Jun 05 10:27:16 2009 -0400
+++ b/usr/src/uts/intel/io/intel_nb5000/intel_nb5000.c	Fri Jun 05 10:28:40 2009 -0400
@@ -20,7 +20,7 @@
  */
 
 /*
- * Copyright 2008 Sun Microsystems, Inc.  All rights reserved.
+ * Copyright 2009 Sun Microsystems, Inc.  All rights reserved.
  * Use is subject to license terms.
  */
 
@@ -362,6 +362,9 @@
 		nerr++;
 	}
 
+	if (rt)
+		err_nf_int &= ~ERR_NF_INT_B18;
+
 	sz = sizeof (nf_int_error_code) / sizeof (struct mch_error_code);
 
 	for (i = 0; i < sz; i++) {
@@ -379,10 +382,11 @@
 	return (rt);
 }
 
-static void
+static int
 log_int_err(nb_regs_t *rp, int *interpose)
 {
 	int t = 0;
+	int rt = 0;
 
 	rp->flag = NB_REG_LOG_INT;
 	rp->nb.int_regs.ferr_fat_int = FERR_FAT_INT_RD(interpose);
@@ -412,6 +416,14 @@
 		NRECSF_WR();
 		RECSF_WR();
 	}
+	if (rp->nb.int_regs.ferr_fat_int == 0 &&
+	    rp->nb.int_regs.nerr_fat_int == 0 &&
+	    (rp->nb.int_regs.ferr_nf_int == ERR_NF_INT_B18 ||
+	    (rp->nb.int_regs.ferr_nf_int == 0 &&
+	    rp->nb.int_regs.nerr_nf_int == ERR_NF_INT_B18))) {
+		rt = 1;
+	}
+	return (rt);
 }
 
 static void
@@ -1016,6 +1028,7 @@
 	nb_regs_t *rp = &log->nb_regs;
 	uint32_t nerr = *nerrp;
 	int interpose = 0;
+	int spurious = 0;
 
 	log->acl_timestamp = gethrtime_waitfree();
 	if ((ferr & (GE_PCIEX_FATAL | GE_PCIEX_NF)) != 0) {
@@ -1034,7 +1047,7 @@
 		log_dma_err(rp, &interpose);
 		*nerrp = nerr & ~(GE_DMA_FATAL | GE_DMA_NF);
 	} else if ((ferr & (GE_INT_FATAL | GE_INT_NF)) != 0) {
-		log_int_err(rp, &interpose);
+		spurious = log_int_err(rp, &interpose);
 		*nerrp = nerr & ~(GE_INT_FATAL | GE_INT_NF);
 	} else if (nb_chipset == INTEL_NB_5400 &&
 	    (ferr & (GE_FERR_THERMAL_FATAL | GE_FERR_THERMAL_NF)) != 0) {
@@ -1045,8 +1058,10 @@
 		log->type = "inject";
 	else
 		log->type = "error";
-	errorq_dispatch(nb_queue, log, sizeof (nb_logout_t),
-	    willpanic ? ERRORQ_SYNC : ERRORQ_ASYNC);
+	if (!spurious) {
+		errorq_dispatch(nb_queue, log, sizeof (nb_logout_t),
+		    willpanic ? ERRORQ_SYNC : ERRORQ_ASYNC);
+	}
 }
 
 static void
@@ -1055,6 +1070,7 @@
 	uint32_t err;
 	nb_regs_t *rp = &log->nb_regs;
 	int interpose = 0;
+	int spurious = 0;
 
 	err = *errp;
 	log->acl_timestamp = gethrtime_waitfree();
@@ -1074,15 +1090,17 @@
 		log_dma_err(rp, &interpose);
 		*errp = err & ~(GE_DMA_FATAL | GE_DMA_NF);
 	} else if ((err & (GE_INT_FATAL | GE_INT_NF)) != 0) {
-		log_int_err(rp, &interpose);
+		spurious = log_int_err(rp, &interpose);
 		*errp = err & ~(GE_INT_FATAL | GE_INT_NF);
 	}
 	if (interpose)
 		log->type = "inject";
 	else
 		log->type = "error";
-	errorq_dispatch(nb_queue, log, sizeof (nb_logout_t),
-	    willpanic ? ERRORQ_SYNC : ERRORQ_ASYNC);
+	if (!spurious) {
+		errorq_dispatch(nb_queue, log, sizeof (nb_logout_t),
+		    willpanic ? ERRORQ_SYNC : ERRORQ_ASYNC);
+	}
 }
 
 /*ARGSUSED*/
--- a/usr/src/uts/intel/io/intel_nb5000/intel_nbdrv.c	Fri Jun 05 10:27:16 2009 -0400
+++ b/usr/src/uts/intel/io/intel_nb5000/intel_nbdrv.c	Fri Jun 05 10:28:40 2009 -0400
@@ -20,7 +20,7 @@
  */
 
 /*
- * Copyright 2008 Sun Microsystems, Inc.  All rights reserved.
+ * Copyright 2009 Sun Microsystems, Inc.  All rights reserved.
  * Use is subject to license terms.
  */
 
@@ -408,11 +408,11 @@
 		return (DDI_FAILURE);
 	if (inb_dip == NULL) {
 		inb_dip = dip;
-		(void) ddi_prop_update_string(DDI_DEV_T_NONE, dip, "model",
-		    inb_mc_name());
 		nb_no_smbios = ddi_prop_get_int(DDI_DEV_T_ANY, dip,
 		    DDI_PROP_DONTPASS, "no-smbios", 0);
 		nb_pci_cfg_setup(dip);
+		(void) ddi_prop_update_string(DDI_DEV_T_NONE, dip, "model",
+		    inb_mc_name());
 		if (nb_dev_init()) {
 			nb_pci_cfg_free();
 			inb_dip = NULL;
--- a/usr/src/uts/intel/io/intel_nb5000/nb5000.h	Fri Jun 05 10:27:16 2009 -0400
+++ b/usr/src/uts/intel/io/intel_nb5000/nb5000.h	Fri Jun 05 10:28:40 2009 -0400
@@ -20,7 +20,7 @@
  */
 
 /*
- * Copyright 2008 Sun Microsystems, Inc.  All rights reserved.
+ * Copyright 2009 Sun Microsystems, Inc.  All rights reserved.
  * Use is subject to license terms.
  */
 
@@ -287,6 +287,8 @@
 	EMASK_FBD_M11|EMASK_FBD_M10|EMASK_FBD_M9|EMASK_FBD_M8|EMASK_FBD_M7| \
 	EMASK_FBD_M6|EMASK_FBD_M5|EMASK_FBD_M4)
 
+#define	ERR_INT_ALL	(nb_chipset == INTEL_NB_5400 ? 0xffffffff : 0xff)
+
 #define	ERR_FAT_INT_B14	0x0400	/* B14Msk SF Scrub DBE */
 #define	ERR_FAT_INT_B12	0x0100	/* B12Msk Parity Protected register */
 #define	ERR_FAT_INT_B25	0x0080	/* B25Msk illegal HISMM/TSEG access */
@@ -346,6 +348,7 @@
 #define	EMASK_INT_7300	(EMASK_INT_B3|EMASK_INT_B1)
 /* MCH 7300 errata 17,20 & 21 */
 #define	EMASK_INT_7300_STEP_0	(EMASK_INT_B7|EMASK_INT_B3|EMASK_INT_B1)
+#define	EMASK_INT_5400 0
 
 #define	EMASK_INT_FATAL (EMASK_INT_B7|EMASK_INT_B4|EMASK_INT_B3|EMASK_INT_B2| \
 	EMASK_INT_B1)
@@ -730,12 +733,12 @@
 	nb_pci_getb(0, 16, 2, 0xc2, ip) : \
 	(uint16_t)nb_pci_getb(0, 16, 2, 0xc1, ip))
 #define	NERR_FAT_INT_RD(ip)	((nb_chipset == INTEL_NB_5400) ? \
-	((uint16_t)nb_pci_getb(0, 16, 2, 0xc4, ip) << 8) | \
-	nb_pci_getb(0, 16, 2, 0xc5, ip) : \
+	((uint16_t)nb_pci_getb(0, 16, 2, 0xc5, ip) << 8) | \
+	nb_pci_getb(0, 16, 2, 0xc4, ip) : \
 	(uint16_t)nb_pci_getb(0, 16, 2, 0xc2, ip))
 #define	NERR_NF_INT_RD(ip)	((nb_chipset == INTEL_NB_5400) ? \
-	((uint16_t)nb_pci_getb(0, 16, 2, 0xc6, ip) << 8) | \
-	nb_pci_getb(0, 16, 2, 0xc7, ip) : \
+	((uint16_t)nb_pci_getb(0, 16, 2, 0xc7, ip) << 8) | \
+	nb_pci_getb(0, 16, 2, 0xc6, ip) : \
 	(uint16_t)nb_pci_getb(0, 16, 2, 0xc3, ip))
 #define	EMASK_INT_RD()		((nb_chipset == INTEL_NB_5400) ? \
 	nb_pci_getl(0, 16, 2, 0xd0, 0) : nb_pci_getb(0, 16, 2, 0xcc, 0))
--- a/usr/src/uts/intel/io/intel_nb5000/nb5000_init.c	Fri Jun 05 10:27:16 2009 -0400
+++ b/usr/src/uts/intel/io/intel_nb5000/nb5000_init.c	Fri Jun 05 10:28:40 2009 -0400
@@ -20,7 +20,7 @@
  */
 
 /*
- * Copyright 2008 Sun Microsystems, Inc.  All rights reserved.
+ * Copyright 2009 Sun Microsystems, Inc.  All rights reserved.
  * Use is subject to license terms.
  */
 
@@ -70,10 +70,10 @@
 
 static int nb_dimm_slots;
 
-static uint8_t nb_err0_int;
-static uint8_t nb_err1_int;
-static uint8_t nb_err2_int;
-static uint8_t nb_mcerr_int;
+static uint32_t nb_err0_int;
+static uint32_t nb_err1_int;
+static uint32_t nb_err2_int;
+static uint32_t nb_mcerr_int;
 static uint32_t nb_emask_int;
 
 static uint32_t nb_err0_fbd;
@@ -100,7 +100,7 @@
 static uint32_t docmd_pex[NB_PCI_DEV];
 static uint32_t uncerrsev[NB_PCI_DEV];
 
-static uint8_t l_mcerr_int;
+static uint32_t l_mcerr_int;
 static uint32_t l_mcerr_fbd;
 static uint16_t l_mcerr_fsb;
 static uint16_t l_mcerr_thr;
@@ -118,7 +118,7 @@
 uint_t nb5000_mask_poll_fsb = EMASK_FSB_NF;
 uint_t nb5000_mask_bios_fsb = EMASK_FSB_FATAL;
 
-uint_t nb5400_emask_int = 0;
+uint_t nb5400_emask_int = EMASK_INT_5400;
 
 uint_t nb7300_emask_int = EMASK_INT_7300;
 uint_t nb7300_emask_int_step0 = EMASK_INT_7300_STEP_0;
@@ -134,12 +134,7 @@
 uint_t nb5000_mask_uncor_pex = 0;
 int nb5000_reset_cor_pex = 0;
 uint_t nb5000_mask_cor_pex = 0xffffffff;
-int nb_set_docmd = 1;
 uint32_t nb5000_rp_pex = 0x1;
-uint32_t nb5000_docmd_pex_mask = DOCMD_PEX_MASK;
-uint32_t nb5400_docmd_pex_mask = DOCMD_5400_PEX_MASK;
-uint32_t nb5000_docmd_pex = DOCMD_PEX;
-uint32_t nb5400_docmd_pex = DOCMD_5400_PEX;
 
 int nb_mask_mc_set;
 
@@ -523,13 +518,22 @@
 	return (rt);
 }
 
+struct smb_dimm_rec {
+	int dimms;
+	int slots;
+	int populated;
+	nb_dimm_t **dimmpp;
+};
+
 static int
 dimm_label(smbios_hdl_t *shp, const smbios_struct_t *sp, void *arg)
 {
-	nb_dimm_t ***dimmpp = arg;
+	struct smb_dimm_rec *rp = (struct smb_dimm_rec *)arg;
+	nb_dimm_t ***dimmpp;
 	nb_dimm_t *dimmp;
 	smbios_memdevice_t md;
 
+	dimmpp = &rp->dimmpp;
 	if (sp->smbstr_type == SMB_TYPE_MEMDEVICE) {
 		if (*dimmpp >= &nb_dimms[nb_dimm_slots])
 			return (-1);
@@ -537,6 +541,12 @@
 		if (smbios_info_memdevice(shp, sp->smbstr_id, &md) == 0 &&
 		    md.smbmd_dloc != NULL) {
 			if (md.smbmd_size) {
+				if (dimmp == NULL &&
+				    (rp->slots == nb_dimm_slots ||
+				    rp->dimms < rp->populated)) {
+					(*dimmpp)++;
+					return (0);
+				}
 				/*
 				 * if there is no physical dimm for this smbios
 				 * record it is because this system has less
@@ -545,9 +555,10 @@
 				 * smbios record belongs too
 				 */
 				while (dimmp == NULL) {
+					(*dimmpp)++;
 					if (*dimmpp >= &nb_dimms[nb_dimm_slots])
 						return (-1);
-					dimmp = *(++(*dimmpp));
+					dimmp = **dimmpp;
 				}
 				(void) snprintf(dimmp->label,
 				    sizeof (dimmp->label), "%s", md.smbmd_dloc);
@@ -558,14 +569,40 @@
 	return (0);
 }
 
+static int
+check_memdevice(smbios_hdl_t *shp, const smbios_struct_t *sp, void *arg)
+{
+	struct smb_dimm_rec *rp = (struct smb_dimm_rec *)arg;
+	smbios_memdevice_t md;
+
+	if (sp->smbstr_type == SMB_TYPE_MEMDEVICE) {
+		if (smbios_info_memdevice(shp, sp->smbstr_id, &md) == 0) {
+			rp->slots++;
+			if (md.smbmd_size) {
+				rp->populated++;
+			}
+		}
+	}
+	return (0);
+}
+
 void
 nb_smbios()
 {
-	nb_dimm_t **dimmpp;
+	struct smb_dimm_rec r;
+	int i;
 
 	if (ksmbios != NULL && nb_no_smbios == 0) {
-		dimmpp = nb_dimms;
-		(void) smbios_iter(ksmbios, dimm_label, &dimmpp);
+		r.dimms = 0;
+		r.slots = 0;
+		r.populated = 0;
+		r.dimmpp = nb_dimms;
+		for (i = 0; i < nb_dimm_slots; i++) {
+			if (nb_dimms[i] != NULL)
+				r.dimms++;
+		}
+		(void) smbios_iter(ksmbios, check_memdevice, &r);
+		(void) smbios_iter(ksmbios, dimm_label, &r);
 	}
 }
 
@@ -650,7 +687,6 @@
 nb_pex_init()
 {
 	int i = 0; /* ESI port */
-	uint32_t mask;
 	uint16_t regw;
 
 	emask_uncor_pex[i] = EMASK_UNCOR_PEX_RD(i);
@@ -663,18 +699,9 @@
 		EMASK_UNCOR_PEX_WR(i, nb5000_mask_uncor_pex);
 	if (nb5000_reset_cor_pex)
 		EMASK_COR_PEX_WR(i, nb5000_mask_cor_pex);
-	if (nb_set_docmd) {
-		if (nb_chipset == INTEL_NB_5400) {
-			/* disable masking of ERR pins used by DOCMD */
-			PEX_ERR_PIN_MASK_WR(i, 0x10);
-
-			mask = (docmd_pex[i] & nb5400_docmd_pex_mask) |
-			    (nb5400_docmd_pex & ~nb5400_docmd_pex_mask);
-		} else {
-			mask = (docmd_pex[i] & nb5000_docmd_pex_mask) |
-			    (nb5000_docmd_pex & ~nb5000_docmd_pex_mask);
-		}
-		PEX_ERR_DOCMD_WR(i, mask);
+	if (nb_chipset == INTEL_NB_5400) {
+		/* disable masking of ERR pins used by DOCMD */
+		PEX_ERR_PIN_MASK_WR(i, 0x10);
 	}
 
 	/* RP error message (CE/NFE/FE) detect mask */
@@ -712,10 +739,10 @@
 void
 nb_int_init()
 {
-	uint8_t err0_int;
-	uint8_t err1_int;
-	uint8_t err2_int;
-	uint8_t mcerr_int;
+	uint32_t err0_int;
+	uint32_t err1_int;
+	uint32_t err2_int;
+	uint32_t mcerr_int;
 	uint32_t emask_int;
 	uint16_t stepping;
 
@@ -731,11 +758,11 @@
 	nb_mcerr_int = mcerr_int;
 	nb_emask_int = emask_int;
 
-	ERR0_INT_WR(0xff);
-	ERR1_INT_WR(0xff);
-	ERR2_INT_WR(0xff);
-	MCERR_INT_WR(0xff);
-	EMASK_INT_WR(0xff);
+	ERR0_INT_WR(ERR_INT_ALL);
+	ERR1_INT_WR(ERR_INT_ALL);
+	ERR2_INT_WR(ERR_INT_ALL);
+	MCERR_INT_WR(ERR_INT_ALL);
+	EMASK_INT_WR(ERR_INT_ALL);
 
 	mcerr_int &= ~nb5000_mask_bios_int;
 	mcerr_int |= nb5000_mask_bios_int & (~err0_int | ~err1_int | ~err2_int);
@@ -770,11 +797,11 @@
 void
 nb_int_fini()
 {
-	ERR0_INT_WR(0xff);
-	ERR1_INT_WR(0xff);
-	ERR2_INT_WR(0xff);
-	MCERR_INT_WR(0xff);
-	EMASK_INT_WR(0xff);
+	ERR0_INT_WR(ERR_INT_ALL);
+	ERR1_INT_WR(ERR_INT_ALL);
+	ERR2_INT_WR(ERR_INT_ALL);
+	MCERR_INT_WR(ERR_INT_ALL);
+	EMASK_INT_WR(ERR_INT_ALL);
 
 	ERR0_INT_WR(nb_err0_int);
 	ERR1_INT_WR(nb_err1_int);
--- a/usr/src/uts/intel/io/intel_nb5000/nb_pci_cfg.c	Fri Jun 05 10:27:16 2009 -0400
+++ b/usr/src/uts/intel/io/intel_nb5000/nb_pci_cfg.c	Fri Jun 05 10:28:40 2009 -0400
@@ -20,7 +20,7 @@
  */
 
 /*
- * Copyright 2008 Sun Microsystems, Inc.  All rights reserved.
+ * Copyright 2009 Sun Microsystems, Inc.  All rights reserved.
  * Use is subject to license terms.
  */
 
@@ -88,6 +88,7 @@
 			    "intel_nb5000: pci_config_setup failed");
 		reg.pci_phys_hi += 1 << PCI_REG_DEV_SHIFT;
 	}
+	ddi_prop_remove_all(dip);
 }
 
 void
--- a/usr/src/uts/intel/io/intel_nhm/intel_nhmdrv.c	Fri Jun 05 10:27:16 2009 -0400
+++ b/usr/src/uts/intel/io/intel_nhm/intel_nhmdrv.c	Fri Jun 05 10:28:40 2009 -0400
@@ -186,9 +186,9 @@
 		return (DDI_FAILURE);
 	if (inhm_dip == NULL) {
 		inhm_dip = dip;
+		nhm_pci_cfg_setup(dip);
 		(void) ddi_prop_update_string(DDI_DEV_T_NONE, dip, "model",
 		    inhm_mc_name());
-		nhm_pci_cfg_setup(dip);
 		if (nhm_dev_init()) {
 			nhm_pci_cfg_free();
 			inhm_dip = NULL;
--- a/usr/src/uts/intel/io/intel_nhm/nhm_pci_cfg.c	Fri Jun 05 10:27:16 2009 -0400
+++ b/usr/src/uts/intel/io/intel_nhm/nhm_pci_cfg.c	Fri Jun 05 10:28:40 2009 -0400
@@ -70,6 +70,7 @@
 			}
 		}
 	}
+	ddi_prop_remove_all(dip);
 }
 
 void
--- a/usr/src/uts/intel/os/device_policy	Fri Jun 05 10:27:16 2009 -0400
+++ b/usr/src/uts/intel/os/device_policy	Fri Jun 05 10:28:40 2009 -0400
@@ -1,5 +1,5 @@
 #
-# Copyright 2008 Sun Microsystems, Inc.  All rights reserved.
+# Copyright 2009 Sun Microsystems, Inc.  All rights reserved.
 # Use is subject to license terms.
 #
 # CDDL HEADER START
@@ -49,22 +49,6 @@
 ipsecah		read_priv_set=sys_ip_config	write_priv_set=sys_ip_config
 ipsecesp	read_priv_set=sys_ip_config	write_priv_set=sys_ip_config
 spdsock		read_priv_set=sys_ip_config	write_priv_set=sys_ip_config
-#
-# Raw network interface access permissions
-#
-dnet		read_priv_set=net_rawaccess	write_priv_set=net_rawaccess
-elxl		read_priv_set=net_rawaccess	write_priv_set=net_rawaccess
-ibd		read_priv_set=net_rawaccess	write_priv_set=net_rawaccess
-iprb		read_priv_set=net_rawaccess	write_priv_set=net_rawaccess
-pcelx		read_priv_set=net_rawaccess	write_priv_set=net_rawaccess
-spwr		read_priv_set=net_rawaccess	write_priv_set=net_rawaccess
-aggr		read_priv_set=net_rawaccess	write_priv_set=net_rawaccess
-vnic		read_priv_set=net_rawaccess	write_priv_set=net_rawaccess
-softmac		read_priv_set=net_rawaccess	write_priv_set=net_rawaccess
-#
-# Virtual network interface access permission
-#
-vni		read_priv_set=net_rawaccess	write_priv_set=net_rawaccess
 
 #
 # IP observability device access permission
--- a/usr/src/uts/intel/os/minor_perm	Fri Jun 05 10:27:16 2009 -0400
+++ b/usr/src/uts/intel/os/minor_perm	Fri Jun 05 10:28:40 2009 -0400
@@ -107,7 +107,7 @@
 bl:* 0666 root sys
 sctp:* 0666 root sys
 sctp6:* 0666 root sys
-vni:* 0666 root sys
+dlpistub:* 0666 root sys
 cpuid:self 0644 root sys
 bmc:bmc 0666 root sys
 dld:* 0666 root sys
--- a/usr/src/uts/intel/scsa2usb/Makefile	Fri Jun 05 10:27:16 2009 -0400
+++ b/usr/src/uts/intel/scsa2usb/Makefile	Fri Jun 05 10:28:40 2009 -0400
@@ -20,7 +20,7 @@
 #
 #
 # uts/intel/scsa2usb/Makefile
-# Copyright 2008 Sun Microsystems, Inc.  All rights reserved.
+# Copyright 2009 Sun Microsystems, Inc.  All rights reserved.
 # Use is subject to license terms.
 #
 #
@@ -114,7 +114,7 @@
 #
 #	lock_lint rules
 #
-USBA_FILES = $(USBA_OBJS:%.o=../usba/%.ll)
+USBA_FILES = $(USBA_WITHOUT_WUSB_OBJS:%.o=../usba/%.ll)
 UHCI_FILES = $(UHCI_OBJS:%.o=../uhci/%.ll)
 OHCI_FILES = $(OHCI_OBJS:%.o=../ohci/%.ll)
 EHCI_FILES = $(EHCI_OBJS:%.o=../ehci/%.ll)
--- a/usr/src/uts/intel/ugen/Makefile	Fri Jun 05 10:27:16 2009 -0400
+++ b/usr/src/uts/intel/ugen/Makefile	Fri Jun 05 10:28:40 2009 -0400
@@ -19,7 +19,7 @@
 # CDDL HEADER END
 #
 #
-# Copyright 2008 Sun Microsystems, Inc.  All rights reserved.
+# Copyright 2009 Sun Microsystems, Inc.  All rights reserved.
 # Use is subject to license terms.
 #
 
@@ -100,7 +100,7 @@
 #
 #	lock_lint rules
 #
-USBA_FILES = $(USBA_OBJS:%.o=../usba/%.ll)
+USBA_FILES = $(USBA_WITHOUT_WUSB_OBJS:%.o=../usba/%.ll)
 UHCI_FILES = $(UHCI_OBJS:%.o=../uhci/%.ll)
 OHCI_FILES = $(OHCI_OBJS:%.o=../ohci/%.ll)
 EHCI_FILES = $(EHCI_OBJS:%.o=../ehci/%.ll)
--- a/usr/src/uts/intel/usb_ac/Makefile	Fri Jun 05 10:27:16 2009 -0400
+++ b/usr/src/uts/intel/usb_ac/Makefile	Fri Jun 05 10:28:40 2009 -0400
@@ -108,7 +108,7 @@
 #
 #	lock_lint rules
 #
-USBA_FILES	= $(USBA_OBJS:%.o=../usba/%.ll)
+USBA_FILES	= $(USBA_WITHOUT_WUSB_OBJS:%.o=../usba/%.ll)
 UHCI_FILES	= $(UHCI_OBJS:%.o=../uhci/%.ll)
 OHCI_FILES	= $(OHCI_OBJS:%.o=../ohci/%.ll)
 EHCI_FILES	= $(EHCI_OBJS:%.o=../ehci/%.ll)
--- a/usr/src/uts/intel/usb_ia/Makefile	Fri Jun 05 10:27:16 2009 -0400
+++ b/usr/src/uts/intel/usb_ia/Makefile	Fri Jun 05 10:28:40 2009 -0400
@@ -20,10 +20,9 @@
 #
 #
 # uts/intel/usb_ia/Makefile
-# Copyright 2007 Sun Microsystems, Inc.  All rights reserved.
+# Copyright 2009 Sun Microsystems, Inc.  All rights reserved.
 # Use is subject to license terms.
 #
-#ident	"%Z%%M%	%I%	%E% SMI"
 #
 #	This makefile drives the production of the usb_ia driver kernel module.
 #
@@ -106,7 +105,7 @@
 #
 #	lock_lint rules
 #
-USBA_FILES = $(USBA_OBJS:%.o=../usba/%.ll)
+USBA_FILES = $(USBA_WITHOUT_WUSB_OBJS:%.o=../usba/%.ll)
 UHCI_FILES = $(UHCI_OBJS:%.o=../uhci/%.ll)
 OHCI_FILES = $(OHCI_OBJS:%.o=../ohci/%.ll)
 EHCI_FILES = $(EHCI_OBJS:%.o=../ehci/%.ll)
--- a/usr/src/uts/intel/usb_mid/Makefile	Fri Jun 05 10:27:16 2009 -0400
+++ b/usr/src/uts/intel/usb_mid/Makefile	Fri Jun 05 10:28:40 2009 -0400
@@ -20,10 +20,9 @@
 #
 
 # uts/intel/usb_mid/Makefile
-# Copyright 2007 Sun Microsystems, Inc.  All rights reserved.
+# Copyright 2009 Sun Microsystems, Inc.  All rights reserved.
 # Use is subject to license terms.
 #
-#ident	"%Z%%M%	%I%	%E% SMI"
 #
 #	This makefile drives the production of the usb_mid driver kernel module.
 #
@@ -106,7 +105,7 @@
 #
 #	lock_lint rules
 #
-USBA_FILES = $(USBA_OBJS:%.o=../usba/%.ll)
+USBA_FILES = $(USBA_WITHOUT_WUSB_OBJS:%.o=../usba/%.ll)
 UHCI_FILES = $(UHCI_OBJS:%.o=../uhci/%.ll)
 OHCI_FILES = $(OHCI_OBJS:%.o=../ohci/%.ll)
 EHCI_FILES = $(EHCI_OBJS:%.o=../ehci/%.ll)
--- a/usr/src/uts/intel/usbftdi/Makefile	Fri Jun 05 10:27:16 2009 -0400
+++ b/usr/src/uts/intel/usbftdi/Makefile	Fri Jun 05 10:28:40 2009 -0400
@@ -95,7 +95,7 @@
 #
 WARLOCK_CMD	= $(WLCMD_DIR)/$(MODULE).wlcmd
 USBSER_FILES = $(USBSER_OBJS:%.o=../usbser/%.ll)
-USBA_FILES = $(USBA_OBJS:%.o=../usba/%.ll)
+USBA_FILES = $(USBA_WITHOUT_WUSB_OBJS:%.o=../usba/%.ll)
 UHCI_FILES = $(UHCI_OBJS:%.o=../uhci/%.ll)
 OHCI_FILES = $(OHCI_OBJS:%.o=../ohci/%.ll)
 EHCI_FILES = $(EHCI_OBJS:%.o=../ehci/%.ll)
--- a/usr/src/uts/intel/usbprn/Makefile	Fri Jun 05 10:27:16 2009 -0400
+++ b/usr/src/uts/intel/usbprn/Makefile	Fri Jun 05 10:28:40 2009 -0400
@@ -20,7 +20,7 @@
 #
 #
 # uts/intel/usbprn/Makefile
-# Copyright 2008 Sun Microsystems, Inc.  All rights reserved.
+# Copyright 2009 Sun Microsystems, Inc.  All rights reserved.
 # Use is subject to license terms.
 #
 #
@@ -105,7 +105,8 @@
 #
 #	lock_lint rules
 #
-USBA_FILES = $(USBA_OBJS:%.o=../usba/%.ll)
+#USBA_FILES = $(USBA_OBJS:%.o=../usba/%.ll)
+USBA_FILES = $(USBA_WITHOUT_WUSB_OBJS:%.o=../usba/%.ll)
 UHCI_FILES = $(UHCI_OBJS:%.o=../uhci/%.ll)
 OHCI_FILES = $(OHCI_OBJS:%.o=../ohci/%.ll)
 EHCI_FILES = $(EHCI_OBJS:%.o=../ehci/%.ll)
--- a/usr/src/uts/intel/usbskel/Makefile	Fri Jun 05 10:27:16 2009 -0400
+++ b/usr/src/uts/intel/usbskel/Makefile	Fri Jun 05 10:28:40 2009 -0400
@@ -20,7 +20,7 @@
 #
 #
 # uts/intel/usbskel/Makefile
-# Copyright 2008 Sun Microsystems, Inc.  All rights reserved.
+# Copyright 2009 Sun Microsystems, Inc.  All rights reserved.
 # Use is subject to license terms.
 #
 #
@@ -106,7 +106,7 @@
 #
 #	lock_lint rules
 #
-USBA_FILES = $(USBA_OBJS:%.o=../usba/%.ll)
+USBA_FILES = $(USBA_WITHOUT_WUSB_OBJS:%.o=../usba/%.ll)
 UHCI_FILES = $(UHCI_OBJS:%.o=../uhci/%.ll)
 OHCI_FILES = $(OHCI_OBJS:%.o=../ohci/%.ll)
 EHCI_FILES = $(EHCI_OBJS:%.o=../ehci/%.ll)
--- a/usr/src/uts/intel/usbsksp/Makefile	Fri Jun 05 10:27:16 2009 -0400
+++ b/usr/src/uts/intel/usbsksp/Makefile	Fri Jun 05 10:28:40 2009 -0400
@@ -20,7 +20,7 @@
 #
 #
 #
-# Copyright 2008 Sun Microsystems, Inc.  All rights reserved.
+# Copyright 2009 Sun Microsystems, Inc.  All rights reserved.
 # Use is subject to license terms.
 #
 # uts/intel/usbsksp/Makefile
@@ -97,7 +97,7 @@
 WARLOCK_CMD	= $(WLCMD_DIR)/usbser_keyspan.wlcmd
 
 USBSER_FILES = $(USBSER_OBJS:%.o=../usbser/%.ll)
-USBA_FILES = $(USBA_OBJS:%.o=../usba/%.ll)
+USBA_FILES = $(USBA_WITHOUT_WUSB_OBJS:%.o=../usba/%.ll)
 UHCI_FILES = $(UHCI_OBJS:%.o=../uhci/%.ll)
 OHCI_FILES = $(OHCI_OBJS:%.o=../ohci/%.ll)
 EHCI_FILES = $(EHCI_OBJS:%.o=../ehci/%.ll)
--- a/usr/src/uts/intel/usbsprl/Makefile	Fri Jun 05 10:27:16 2009 -0400
+++ b/usr/src/uts/intel/usbsprl/Makefile	Fri Jun 05 10:28:40 2009 -0400
@@ -20,7 +20,7 @@
 #
 #
 # uts/intel/usbsprl/Makefile
-# Copyright 2008 Sun Microsystems, Inc.  All rights reserved.
+# Copyright 2009 Sun Microsystems, Inc.  All rights reserved.
 # Use is subject to license terms.
 #
 #
@@ -94,7 +94,7 @@
 #
 WARLOCK_CMD	= $(WLCMD_DIR)/$(MODULE).wlcmd
 USBSER_FILES = $(USBSER_OBJS:%.o=../usbser/%.ll)
-USBA_FILES = $(USBA_OBJS:%.o=../usba/%.ll)
+USBA_FILES = $(USBA_WITHOUT_WUSB_OBJS:%.o=../usba/%.ll)
 UHCI_FILES = $(UHCI_OBJS:%.o=../uhci/%.ll)
 OHCI_FILES = $(OHCI_OBJS:%.o=../ohci/%.ll)
 EHCI_FILES = $(EHCI_OBJS:%.o=../ehci/%.ll)
--- a/usr/src/uts/intel/usbvc/Makefile	Fri Jun 05 10:27:16 2009 -0400
+++ b/usr/src/uts/intel/usbvc/Makefile	Fri Jun 05 10:28:40 2009 -0400
@@ -20,10 +20,9 @@
 #
 
 #
-# Copyright 2007 Sun Microsystems, Inc.  All rights reserved.
+# Copyright 2009 Sun Microsystems, Inc.  All rights reserved.
 # Use is subject to license terms.
 #
-#ident	"%Z%%M%	%I%	%E% SMI"
 
 #
 #	This makefile drives the production of the usbvc driver kernel module.
@@ -113,7 +112,7 @@
 #
 #	lock_lint rules
 #
-USBA_FILES = $(USBA_OBJS:%.o=../usba/%.ll)
+USBA_FILES = $(USBA_WITHOUT_WUSB_OBJS:%.o=../usba/%.ll)
 UHCI_FILES = $(UHCI_OBJS:%.o=../uhci/%.ll)
 OHCI_FILES = $(OHCI_OBJS:%.o=../ohci/%.ll)
 EHCI_FILES = $(EHCI_OBJS:%.o=../ehci/%.ll)
--- a/usr/src/uts/sparc/Makefile.sparc.shared	Fri Jun 05 10:27:16 2009 -0400
+++ b/usr/src/uts/sparc/Makefile.sparc.shared	Fri Jun 05 10:28:40 2009 -0400
@@ -257,6 +257,7 @@
 DRV_KMODS	+= igb
 DRV_KMODS	+= ixgbe
 DRV_KMODS	+= vr
+DRV_KMODS	+= mr_sas
 $(CLOSED_BUILD)CLOSED_DRV_KMODS	+= ixgb
 
 #
--- a/usr/src/uts/sparc/hid/Makefile	Fri Jun 05 10:27:16 2009 -0400
+++ b/usr/src/uts/sparc/hid/Makefile	Fri Jun 05 10:28:40 2009 -0400
@@ -21,7 +21,7 @@
 #
 # uts/sparc/hid/Makefile
 
-# Copyright 2008 Sun Microsystems, Inc.  All rights reserved.
+# Copyright 2009 Sun Microsystems, Inc.  All rights reserved.
 # Use is subject to license terms.
 #
 #
@@ -111,7 +111,7 @@
 #
 #	lock_lint rules
 #
-USBA_FILES = $(USBA_OBJS:%.o=../usba/%.ll)
+USBA_FILES = $(USBA_WITHOUT_WUSB_OBJS:%.o=../usba/%.ll)
 OHCI_FILES = $(OHCI_OBJS:%.o=../ohci/%.ll)
 UHCI_FILES = $(UHCI_OBJS:%.o=../uhci/%.ll)
 EHCI_FILES = $(EHCI_OBJS:%.o=../ehci/%.ll)
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/usr/src/uts/sparc/mr_sas/Makefile	Fri Jun 05 10:28:40 2009 -0400
@@ -0,0 +1,100 @@
+#
+# 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
+# or http://www.opensolaris.org/os/licensing.
+# 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 2009 Sun Microsystems, Inc.  All rights reserved.
+# Use is subject to license terms.
+#
+#	This makefile drives the production of the mr_sas driver kernel module.
+#
+#	Sparc implementation architecture dependent
+#
+
+#
+#	Path to the base of the uts directory tree (usually /usr/src/uts).
+#
+UTSBASE	= ../..
+
+#
+#	Define the module and object file sets.
+#
+MODULE		= mr_sas
+OBJECTS		= $(MR_SAS_OBJS:%=$(OBJS_DIR)/%)
+LINTS		= $(MR_SAS_OBJS:%.o=$(LINTS_DIR)/%.ln)
+ROOTMODULE	= $(ROOT_DRV_DIR)/$(MODULE)
+CONF_SRCDIR	= $(UTSBASE)/common/io/mr_sas
+
+#
+#	Include common rules.
+#
+include $(UTSBASE)/sparc/Makefile.sparc
+
+#
+#	Define targets
+#
+ALL_TARGET	= $(BINARY) $(CONFMOD)
+LINT_TARGET	= $(MODULE).lint
+INSTALL_TARGET	= $(BINARY) $(ROOTMODULE) $(ROOT_CONFFILE)
+
+# 
+# lint pass one enforcement 
+# 
+CFLAGS		+= $(CCVERBOSE)
+
+#
+# Turn on doubleword alignment for 64 bit registers
+#
+CFLAGS		+= -dalign
+
+#
+# 	Kernel Module Dependencies
+#
+LDFLAGS		+= -dy -Nmisc/scsi
+
+#
+# 	Overrides
+#
+
+#
+#	Default build targets.
+#
+.KEEP_STATE:
+
+def:		$(DEF_DEPS)
+
+all:		$(ALL_DEPS)
+
+clean:		$(CLEAN_DEPS)
+
+clobber:	$(CLOBBER_DEPS)
+
+lint:		$(LINT_DEPS)
+
+modlintlib:	$(MODLINTLIB_DEPS)
+
+clean.lint:	$(CLEAN_LINT_DEPS)
+
+install:	$(INSTALL_DEPS)
+
+#
+#	Include common targets.
+#
+include $(UTSBASE)/sparc/Makefile.targ
--- a/usr/src/uts/sparc/os/device_policy	Fri Jun 05 10:27:16 2009 -0400
+++ b/usr/src/uts/sparc/os/device_policy	Fri Jun 05 10:28:40 2009 -0400
@@ -51,21 +51,6 @@
 ipsecah		read_priv_set=sys_ip_config	write_priv_set=sys_ip_config
 ipsecesp	read_priv_set=sys_ip_config	write_priv_set=sys_ip_config
 spdsock		read_priv_set=sys_ip_config	write_priv_set=sys_ip_config
-#
-# Raw network interface access permissions
-#
-ce		read_priv_set=net_rawaccess	write_priv_set=net_rawaccess
-eri		read_priv_set=net_rawaccess	write_priv_set=net_rawaccess
-ge		read_priv_set=net_rawaccess	write_priv_set=net_rawaccess
-ibd		read_priv_set=net_rawaccess	write_priv_set=net_rawaccess
-pcelx		read_priv_set=net_rawaccess	write_priv_set=net_rawaccess
-aggr		read_priv_set=net_rawaccess	write_priv_set=net_rawaccess
-vnic		read_priv_set=net_rawaccess	write_priv_set=net_rawaccess
-softmac		read_priv_set=net_rawaccess	write_priv_set=net_rawaccess
-#
-# Virtual network interface access permission
-#
-vni		read_priv_set=net_rawaccess	write_priv_set=net_rawaccess
 
 #
 # IP observability device access permission
--- a/usr/src/uts/sparc/os/minor_perm	Fri Jun 05 10:27:16 2009 -0400
+++ b/usr/src/uts/sparc/os/minor_perm	Fri Jun 05 10:28:40 2009 -0400
@@ -139,7 +139,7 @@
 bl:* 0666 root sys
 sctp:* 0666 root sys
 sctp6:* 0666 root sys
-vni:* 0666 root sys
+dlpistub:* 0666 root sys
 cpuid:self 0644 root sys
 mdesc:* 0666 root sys
 dld:* 0666 root sys
--- a/usr/src/uts/sparc/scsa2usb/Makefile	Fri Jun 05 10:27:16 2009 -0400
+++ b/usr/src/uts/sparc/scsa2usb/Makefile	Fri Jun 05 10:28:40 2009 -0400
@@ -19,7 +19,7 @@
 # CDDL HEADER END
 #
 #
-# Copyright 2008 Sun Microsystems, Inc.  All rights reserved.
+# Copyright 2009 Sun Microsystems, Inc.  All rights reserved.
 # Use is subject to license terms.
 #
 # uts/sparc/scsa2usb/Makefile
@@ -110,7 +110,7 @@
 #
 #	lock_lint rules
 #
-USBA_FILES = $(USBA_OBJS:%.o=../usba/%.ll)
+USBA_FILES = $(USBA_WITHOUT_WUSB_OBJS:%.o=../usba/%.ll)
 UHCI_FILES = $(UHCI_OBJS:%.o=../uhci/%.ll)
 OHCI_FILES = $(OHCI_OBJS:%.o=../ohci/%.ll)
 EHCI_FILES = $(EHCI_OBJS:%.o=../ehci/%.ll)
--- a/usr/src/uts/sparc/ugen/Makefile	Fri Jun 05 10:27:16 2009 -0400
+++ b/usr/src/uts/sparc/ugen/Makefile	Fri Jun 05 10:28:40 2009 -0400
@@ -21,7 +21,7 @@
 #
 # uts/sparc/ugen/Makefile
 #
-# Copyright 2008 Sun Microsystems, Inc.  All rights reserved.
+# Copyright 2009 Sun Microsystems, Inc.  All rights reserved.
 # Use is subject to license terms.
 #
 #
@@ -104,7 +104,7 @@
 #
 #	lock_lint rules
 #
-USBA_FILES = $(USBA_OBJS:%.o=../usba/%.ll)
+USBA_FILES = $(USBA_WITHOUT_WUSB_OBJS:%.o=../usba/%.ll)
 UHCI_FILES = $(UHCI_OBJS:%.o=../uhci/%.ll)
 OHCI_FILES = $(OHCI_OBJS:%.o=../ohci/%.ll)
 EHCI_FILES = $(EHCI_OBJS:%.o=../ehci/%.ll)
--- a/usr/src/uts/sparc/usb_ia/Makefile	Fri Jun 05 10:27:16 2009 -0400
+++ b/usr/src/uts/sparc/usb_ia/Makefile	Fri Jun 05 10:28:40 2009 -0400
@@ -21,14 +21,13 @@
 #
 # uts/sparc/usb_ia/Makefile
 
-# Copyright 2007 Sun Microsystems, Inc.  All rights reserved.
+# Copyright 2009 Sun Microsystems, Inc.  All rights reserved.
 # Use is subject to license terms.
 #
 #	This makefile drives the production of the usb_ia driver kernel module.
 #	sparc architecture dependent
 #
 
-#ident	"%Z%%M%	%I%	%E% SMI"
 
 #
 #	Path to the base of the uts directory tree (usually /usr/src/uts).
@@ -104,7 +103,7 @@
 #
 #	lock_lint rules
 #
-USBA_FILES = $(USBA_OBJS:%.o=../usba/%.ll)
+USBA_FILES = $(USBA_WITHOUT_WUSB_OBJS:%.o=../usba/%.ll)
 UHCI_FILES = $(UHCI_OBJS:%.o=../uhci/%.ll)
 OHCI_FILES = $(OHCI_OBJS:%.o=../ohci/%.ll)
 EHCI_FILES = $(EHCI_OBJS:%.o=../ehci/%.ll)
--- a/usr/src/uts/sparc/usb_mid/Makefile	Fri Jun 05 10:27:16 2009 -0400
+++ b/usr/src/uts/sparc/usb_mid/Makefile	Fri Jun 05 10:28:40 2009 -0400
@@ -21,14 +21,13 @@
 
 # uts/sparc/usb_mid/Makefile
 
-# Copyright 2007 Sun Microsystems, Inc.  All rights reserved.
+# Copyright 2009 Sun Microsystems, Inc.  All rights reserved.
 # Use is subject to license terms.
 #
 #	This makefile drives the production of the usb_mid driver kernel module.
 #	sparc architecture dependent
 #
 
-#ident	"%Z%%M%	%I%	%E% SMI"
 
 #
 #	Path to the base of the uts directory tree (usually /usr/src/uts).
@@ -104,7 +103,7 @@
 #
 #	lock_lint rules
 #
-USBA_FILES = $(USBA_OBJS:%.o=../usba/%.ll)
+USBA_FILES = $(USBA_WITHOUT_WUSB_OBJS:%.o=../usba/%.ll)
 UHCI_FILES = $(UHCI_OBJS:%.o=../uhci/%.ll)
 OHCI_FILES = $(OHCI_OBJS:%.o=../ohci/%.ll)
 EHCI_FILES = $(EHCI_OBJS:%.o=../ehci/%.ll)
--- a/usr/src/uts/sparc/usbftdi/Makefile	Fri Jun 05 10:27:16 2009 -0400
+++ b/usr/src/uts/sparc/usbftdi/Makefile	Fri Jun 05 10:28:40 2009 -0400
@@ -95,7 +95,7 @@
 #
 WARLOCK_CMD	= $(WLCMD_DIR)/$(MODULE).wlcmd
 USBSER_FILES = $(USBSER_OBJS:%.o=../usbser/%.ll)
-USBA_FILES = $(USBA_OBJS:%.o=../usba/%.ll)
+USBA_FILES = $(USBA_WITHOUT_WUSB_OBJS:%.o=../usba/%.ll)
 UHCI_FILES = $(UHCI_OBJS:%.o=../uhci/%.ll)
 OHCI_FILES = $(OHCI_OBJS:%.o=../ohci/%.ll)
 EHCI_FILES = $(EHCI_OBJS:%.o=../ehci/%.ll)
--- a/usr/src/uts/sparc/usbprn/Makefile	Fri Jun 05 10:27:16 2009 -0400
+++ b/usr/src/uts/sparc/usbprn/Makefile	Fri Jun 05 10:28:40 2009 -0400
@@ -19,7 +19,7 @@
 # CDDL HEADER END
 #
 #
-# Copyright 2008 Sun Microsystems, Inc.  All rights reserved.
+# Copyright 2009 Sun Microsystems, Inc.  All rights reserved.
 # Use is subject to license terms.
 #
 #
@@ -101,7 +101,7 @@
 #
 #	lock_lint rules
 #
-USBA_FILES = $(USBA_OBJS:%.o=../usba/%.ll)
+USBA_FILES = $(USBA_WITHOUT_WUSB_OBJS:%.o=../usba/%.ll)
 UHCI_FILES = $(UHCI_OBJS:%.o=../uhci/%.ll)
 OHCI_FILES = $(OHCI_OBJS:%.o=../ohci/%.ll)
 EHCI_FILES = $(EHCI_OBJS:%.o=../ehci/%.ll)
--- a/usr/src/uts/sparc/usbskel/Makefile	Fri Jun 05 10:27:16 2009 -0400
+++ b/usr/src/uts/sparc/usbskel/Makefile	Fri Jun 05 10:28:40 2009 -0400
@@ -21,7 +21,7 @@
 #
 # uts/sparc/usbskel/Makefile
 
-# Copyright 2008 Sun Microsystems, Inc.  All rights reserved.
+# Copyright 2009 Sun Microsystems, Inc.  All rights reserved.
 # Use is subject to license terms.
 #
 #
@@ -105,7 +105,7 @@
 #
 #	lock_lint rules
 #
-USBA_FILES = $(USBA_OBJS:%.o=../usba/%.ll)
+USBA_FILES = $(USBA_WITHOUT_WUSB_OBJS:%.o=../usba/%.ll)
 UHCI_FILES = $(UHCI_OBJS:%.o=../uhci/%.ll)
 OHCI_FILES = $(OHCI_OBJS:%.o=../ohci/%.ll)
 EHCI_FILES = $(EHCI_OBJS:%.o=../ehci/%.ll)
--- a/usr/src/uts/sparc/usbsksp/Makefile	Fri Jun 05 10:27:16 2009 -0400
+++ b/usr/src/uts/sparc/usbsksp/Makefile	Fri Jun 05 10:28:40 2009 -0400
@@ -20,7 +20,7 @@
 #
 #
 #
-# Copyright 2008 Sun Microsystems, Inc.  All rights reserved.
+# Copyright 2009 Sun Microsystems, Inc.  All rights reserved.
 # Use is subject to license terms.
 #
 # uts/sparc/usbsksp/Makefile
@@ -102,7 +102,7 @@
 #
 WARLOCK_CMD	= $(WLCMD_DIR)/usbser_keyspan.wlcmd
 USBSER_FILES = $(USBSER_OBJS:%.o=../usbser/%.ll)
-USBA_FILES = $(USBA_OBJS:%.o=../usba/%.ll)
+USBA_FILES = $(USBA_WITHOUT_WUSB_OBJS:%.o=../usba/%.ll)
 UHCI_FILES = $(UHCI_OBJS:%.o=../uhci/%.ll)
 OHCI_FILES = $(OHCI_OBJS:%.o=../ohci/%.ll)
 EHCI_FILES = $(EHCI_OBJS:%.o=../ehci/%.ll)
--- a/usr/src/uts/sparc/usbsprl/Makefile	Fri Jun 05 10:27:16 2009 -0400
+++ b/usr/src/uts/sparc/usbsprl/Makefile	Fri Jun 05 10:28:40 2009 -0400
@@ -21,7 +21,7 @@
 #
 # uts/sparc/usbsprl/Makefile
 #
-# Copyright 2008 Sun Microsystems, Inc.  All rights reserved.
+# Copyright 2009 Sun Microsystems, Inc.  All rights reserved.
 # Use is subject to license terms.
 #
 #
@@ -100,7 +100,7 @@
 #
 WARLOCK_CMD	= $(WLCMD_DIR)/$(MODULE).wlcmd
 USBSER_FILES = $(USBSER_OBJS:%.o=../usbser/%.ll)
-USBA_FILES = $(USBA_OBJS:%.o=../usba/%.ll)
+USBA_FILES = $(USBA_WITHOUT_WUSB_OBJS:%.o=../usba/%.ll)
 UHCI_FILES = $(UHCI_OBJS:%.o=../uhci/%.ll)
 OHCI_FILES = $(OHCI_OBJS:%.o=../ohci/%.ll)
 EHCI_FILES = $(EHCI_OBJS:%.o=../ehci/%.ll)
--- a/usr/src/uts/sparc/usbvc/Makefile	Fri Jun 05 10:27:16 2009 -0400
+++ b/usr/src/uts/sparc/usbvc/Makefile	Fri Jun 05 10:28:40 2009 -0400
@@ -20,10 +20,9 @@
 #
 
 #
-# Copyright 2007 Sun Microsystems, Inc.  All rights reserved.
+# Copyright 2009 Sun Microsystems, Inc.  All rights reserved.
 # Use is subject to license terms.
 #
-#ident	"%Z%%M%	%I%	%E% SMI"
 
 #
 # uts/sparc/usbvc/Makefile
@@ -111,7 +110,7 @@
 #
 #	lock_lint rules
 #
-USBA_FILES = $(USBA_OBJS:%.o=../usba/%.ll)
+USBA_FILES = $(USBA_WITHOUT_WUSB_OBJS:%.o=../usba/%.ll)
 UHCI_FILES = $(UHCI_OBJS:%.o=../uhci/%.ll)
 OHCI_FILES = $(OHCI_OBJS:%.o=../ohci/%.ll)
 EHCI_FILES = $(EHCI_OBJS:%.o=../ehci/%.ll)
--- a/usr/src/uts/sun4v/Makefile.files	Fri Jun 05 10:27:16 2009 -0400
+++ b/usr/src/uts/sun4v/Makefile.files	Fri Jun 05 10:28:40 2009 -0400
@@ -188,7 +188,7 @@
 NIAGARACPU_OBJS += niagara_asm.o atomic.o
 NIAGARA2CPU_OBJS = niagara2.o niagara_copy.o common_asm.o niagara_perfctr.o
 NIAGARA2CPU_OBJS += niagara2_asm.o atomic.o
-ROCKCPU_OBJS = rock.o rock_copy.o common_asm.o rock_atomic.o rock_asm.o 
+ROCKCPU_OBJS = rock.o rock_copy.o common_asm.o rock_asm.o atomic.o
 
 #
 #			platform module
--- a/usr/src/uts/sun4v/cpu/rock.c	Fri Jun 05 10:27:16 2009 -0400
+++ b/usr/src/uts/sun4v/cpu/rock.c	Fri Jun 05 10:28:40 2009 -0400
@@ -56,12 +56,13 @@
 #include <sys/rock_hypervisor_api.h>
 #include <sys/hsvc.h>
 #include <vm/hat_sfmmu.h>
+#include <sys/mutex_impl.h>
 
 uint_t root_phys_addr_lo_mask = 0xffffffffU;
 uint8_t	enable_tm = 1;
 
 char cpu_module_name[] = "SUNW,UltraSPARC-AT10";
-static	boolean_t	hsvc_tm_available = B_TRUE;
+boolean_t	hsvc_tm_available = B_TRUE;
 
 static	hsvc_info_t rock_tm_hsvc = {
 	HSVC_REV_1,		/* HSVC rev num */
@@ -87,6 +88,8 @@
 static void set_pgsz_order(uchar_t, uchar_t, uint64_t *, int *, int *,
     sfmmu_t *);
 
+extern	void rock_mutex_delay(void);
+
 /*
  * External /etc/system tunable, for controlling whether shared or private pages
  * come first in the pagesize order register.
@@ -273,6 +276,7 @@
 cpu_init_private(struct cpu *cp)
 {
 	cpu_map_exec_units(cp);
+	mutex_delay = rock_mutex_delay;
 }
 
 /*ARGSUSED*/
@@ -289,6 +293,18 @@
 void
 cpu_feature_init(void)
 {
+	static	int	set_mutex_backoff_tunables = 0;
+	/*
+	 * Set constants for mutex_backoff only once.
+	 * On Rock, setting this to 8 gives the best performance,
+	 * even for multi-chip systems.
+	 */
+	if (! set_mutex_backoff_tunables) {
+		mutex_backoff_base = 1;
+		mutex_cap_factor = 8;
+		set_mutex_backoff_tunables = 1;
+	}
+
 	/*
 	 * Enable or disable for each cpu if hypervisor API is negotiated.
 	 */
--- a/usr/src/uts/sun4v/cpu/rock_asm.s	Fri Jun 05 10:27:16 2009 -0400
+++ b/usr/src/uts/sun4v/cpu/rock_asm.s	Fri Jun 05 10:28:40 2009 -0400
@@ -20,7 +20,7 @@
  */
 
 /*
- * Copyright 2008 Sun Microsystems, Inc.  All rights reserved.
+ * Copyright 2009 Sun Microsystems, Inc.  All rights reserved.
  * Use is subject to license terms.
  */
 
@@ -32,23 +32,8 @@
 #include <sys/fsr.h>		/* FPRS_FEF, FPRS_DU */
 #include <vm/hat_sfmmu.h>	/* TSBTAG_INVALID */
 
-#define	TRANS_RETRY_COUNT	3
-
-/*
- * XXX Delete this comment and these #undef's when the corresponding
- * Makefile.workarounds lines are deleted.
- * XXX
- *
- * Transactional instructions are used here regardless of what's in
- * Makefile.workarounds.
- */
-#undef	chkpt
-#undef	commit
-
-
 #if defined(lint)
-
-#include <sys/mutex.h>
+#include <sys/types.h>
 
 void
 cpu_smt_pause(void)
@@ -129,24 +114,43 @@
 cpu_inv_tsb(caddr_t tsb_base, uint_t tsb_bytes)
 {}
 
+void
+cpu_atomic_delay(void)
+{}
+
+void
+rock_mutex_delay(void)
+{}
 #else	/* lint */
 
-/* XXX TODO XXX
- * When we get real hardware, we need to do performance tuning on this.
- * Does it help?  Does it ever hurt?  How many membar's should we have here?
+/*
+ * Called from various spin loops to prevent this strand from
+ * stealing too many cycles from its sibling, who is presumably
+ * doing useful work.
+ *
+ * With a 2.1 GHz clock, 100 membar #Halt instructions plus
+ * the call/return overhead will take approximately 500 nanoseconds.
+ * That is a suitable time for a PAUSE, as it is roughly equal to
+ * two memory accesses.
  */
-	/*
-	 * Called from various spin loops to prevent this strand from
-	 * stealing too many cycles from its sibling, who is presumably
-	 * doing useful work.
-	 */
 	ENTRY_NP(cpu_smt_pause)
+	mov	10, %o0
+1:	membar	#Halt
+	membar	#Halt
+	membar	#Halt
+	membar	#Halt
+	membar	#Halt
+	membar	#Halt
+	membar	#Halt
+	membar	#Halt
+	membar	#Halt
+	subcc	%o0, 1, %o0
+	bg,pt	%xcc, 1b
 	membar	#Halt
 	retl
-	nop
+	membar	#Halt
 	SET_SIZE(cpu_smt_pause)
 
-
 /*
  * fp_zero() - clear all fp data registers and the fsr
  */
@@ -436,4 +440,47 @@
 	restore
 	SET_SIZE(cpu_inv_tsb)
 
+/*
+ * This is CPU specific delay routine for atomic backoff.
+ * It is used in case of Rock CPU. The rd instruction uses
+ * less resources than casx on these CPUs.
+ */
+	.align	32
+	ENTRY(cpu_atomic_delay)
+	rd	%ccr, %g0
+	rd	%ccr, %g0
+	retl
+	rd	%ccr, %g0
+	SET_SIZE(cpu_atomic_delay)
+
+/*
+ * Delay to last ~100 nano seconds on a 2.1 GHz. Membars
+ * should be linear and not in a loop to avoid impact
+ * on the sibling strand (BR pipeline is shared by
+ * two sibling strands).
+ */
+	.align	64
+	ENTRY(rock_mutex_delay)
+	membar	#Halt
+	membar	#Halt
+	membar	#Halt
+	membar	#Halt
+	membar	#Halt
+	membar	#Halt
+	membar	#Halt
+	membar	#Halt
+	membar	#Halt
+	membar	#Halt
+	membar	#Halt
+	membar	#Halt
+	membar	#Halt
+	membar	#Halt
+	membar	#Halt
+	membar	#Halt
+	membar	#Halt
+	membar	#Halt
+	membar	#Halt
+	retl
+	membar	#Halt
+	SET_SIZE(rock_mutex_delay)
 #endif /* lint */
--- a/usr/src/uts/sun4v/cpu/rock_atomic.s	Fri Jun 05 10:27:16 2009 -0400
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,1367 +0,0 @@
-/*
- * 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
- * or http://www.opensolaris.org/os/licensing.
- * 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.
- */
-
-#include <sys/asm_linkage.h>
-
-#ifndef chkpt 
-#define chkpt(FAILADDR) btrans.FAILADDR:	\
-		.word 0x30500000|((FAILADDR-btrans.FAILADDR)>>2)
-#endif
-
-#ifndef commit
-#define commit	.word 0xbdf00000
-#endif
-
-#if defined(lint)
-
-/* ARGSUSED */
-uint8_t
-cas8(uint8_t *target, uint8_t value1, uint8_t value2)
-{ return (0); }
-
-/* ARGSUSED */
-uint32_t
-cas32(uint32_t *target, uint32_t value1, uint32_t value2)
-{ return (0); }
-
-/* ARGSUSED */
-uint64_t
-cas64(uint64_t *target, uint64_t value1, uint64_t value2)
-{ return (0); }
-
-/* ARGSUSED */
-ulong_t
-caslong(ulong_t *target, ulong_t value1, ulong_t value2)
-{ return (0); }
-
-/* ARGSUSED */
-void *
-casptr(void *ptr1, void *ptr2, void *ptr3)
-{ return (0); }
-
-/* ARGSUSED */
-void
-atomic_and_long(ulong_t *target, ulong_t value)
-{}
-
-/* ARGSUSED */
-void
-atomic_or_long(ulong_t *target, ulong_t value)
-{}
-
-/* ARGSUSED */
-void
-atomic_inc_8(volatile uint8_t *target)
-{}
-
-/* ARGSUSED */
-void
-atomic_inc_uchar(volatile uchar_t *target)
-{}
-
-/* ARGSUSED */
-void
-atomic_inc_16(volatile uint16_t *target)
-{}
-
-/* ARGSUSED */
-void
-atomic_inc_ushort(volatile ushort_t *target)
-{}
-
-/* ARGSUSED */
-void
-atomic_inc_32(volatile uint32_t *target)
-{}
-
-/* ARGSUSED */
-void
-atomic_inc_uint(volatile uint_t *target)
-{}
-
-/* ARGSUSED */
-void
-atomic_inc_ulong(volatile ulong_t *target)
-{}
-
-/* ARGSUSED */
-void
-atomic_inc_64(volatile uint64_t *target)
-{}
-
-/* ARGSUSED */
-void
-atomic_dec_8(volatile uint8_t *target)
-{}
-
-/* ARGSUSED */
-void
-atomic_dec_uchar(volatile uchar_t *target)
-{}
-
-/* ARGSUSED */
-void
-atomic_dec_16(volatile uint16_t *target)
-{}
-
-/* ARGSUSED */
-void
-atomic_dec_ushort(volatile ushort_t *target)
-{}
-
-/* ARGSUSED */
-void
-atomic_dec_32(volatile uint32_t *target)
-{}
-
-/* ARGSUSED */
-void
-atomic_dec_uint(volatile uint_t *target)
-{}
-
-/* ARGSUSED */
-void
-atomic_dec_ulong(volatile ulong_t *target)
-{}
-
-/* ARGSUSED */
-void
-atomic_dec_64(volatile uint64_t *target)
-{}
-
-/* ARGSUSED */
-void
-atomic_add_8(volatile uint8_t *target, int8_t value)
-{}
-
-/* ARGSUSED */
-void
-atomic_add_char(volatile uchar_t *target, signed char value)
-{}
-
-/* ARGSUSED */
-void
-atomic_add_16(volatile uint16_t *target, int16_t delta)
-{}
-
-/* ARGSUSED */
-void
-atomic_add_ushort(volatile ushort_t *target, short value)
-{}
-
-/* ARGSUSED */
-void
-atomic_add_32(volatile uint32_t *target, int32_t delta)
-{}
-
-/* ARGSUSED */
-void
-atomic_add_ptr(volatile void *target, ssize_t value)
-{}
-
-/* ARGSUSED */
-void
-atomic_add_long(volatile ulong_t *target, long delta)
-{}
-
-/* ARGSUSED */
-void
-atomic_add_64(volatile uint64_t *target, int64_t delta)
-{}
-
-/* ARGSUSED */
-void
-atomic_or_8(volatile uint8_t *target, uint8_t bits)
-{}
-
-/* ARGSUSED */
-void
-atomic_or_uchar(volatile uchar_t *target, uchar_t bits)
-{}
-
-/* ARGSUSED */
-void
-atomic_or_16(volatile uint16_t *target, uint16_t bits)
-{}
-
-/* ARGSUSED */
-void
-atomic_or_ushort(volatile ushort_t *target, ushort_t bits)
-{}
-
-/* ARGSUSED */
-void
-atomic_or_32(volatile uint32_t *target, uint32_t bits)
-{}
-
-/* ARGSUSED */
-void
-atomic_or_uint(volatile uint_t *target, uint_t bits)
-{}
-
-/* ARGSUSED */
-void
-atomic_or_ulong(volatile ulong_t *target, ulong_t bits)
-{}
-
-/* ARGSUSED */
-void
-atomic_or_64(volatile uint64_t *target, uint64_t bits)
-{}
-
-/* ARGSUSED */
-void
-atomic_and_8(volatile uint8_t *target, uint8_t bits)
-{}
-
-/* ARGSUSED */
-void
-atomic_and_uchar(volatile uchar_t *target, uchar_t bits)
-{}
-
-/* ARGSUSED */
-void
-atomic_and_16(volatile uint16_t *target, uint16_t bits)
-{}
-
-/* ARGSUSED */
-void
-atomic_and_ushort(volatile ushort_t *target, ushort_t bits)
-{}
-
-/* ARGSUSED */
-void
-atomic_and_32(volatile uint32_t *target, uint32_t bits)
-{}
-
-/* ARGSUSED */
-void
-atomic_and_uint(volatile uint_t *target, uint_t bits)
-{}
-
-/* ARGSUSED */
-void
-atomic_and_ulong(volatile ulong_t *target, ulong_t bits)
-{}
-
-/* ARGSUSED */
-void
-atomic_and_64(volatile uint64_t *target, uint64_t bits)
-{}
-
-/* ARGSUSED */
-uint8_t
-atomic_inc_8_nv(volatile uint8_t *target)
-{ return (0); }
-
-/* ARGSUSED */
-uchar_t
-atomic_inc_uchar_nv(volatile uchar_t *target)
-{ return (0); }
-
-/* ARGSUSED */
-uint16_t
-atomic_inc_16_nv(volatile uint16_t *target)
-{ return (0); }
-
-/* ARGSUSED */
-ushort_t
-atomic_inc_ushort_nv(volatile ushort_t *target)
-{ return (0); }
-
-/* ARGSUSED */
-uint32_t
-atomic_inc_32_nv(volatile uint32_t *target)
-{ return (0); }
-
-/* ARGSUSED */
-uint_t
-atomic_inc_uint_nv(volatile uint_t *target)
-{ return (0); }
-
-/* ARGSUSED */
-ulong_t
-atomic_inc_ulong_nv(volatile ulong_t *target)
-{ return (0); }
-
-/* ARGSUSED */
-uint64_t
-atomic_inc_64_nv(volatile uint64_t *target)
-{ return (0); }
-
-/* ARGSUSED */
-uint8_t
-atomic_dec_8_nv(volatile uint8_t *target)
-{ return (0); }
-
-/* ARGSUSED */
-uchar_t
-atomic_dec_uchar_nv(volatile uchar_t *target)
-{ return (0); }
-
-/* ARGSUSED */
-uint16_t
-atomic_dec_16_nv(volatile uint16_t *target)
-{ return (0); }
-
-/* ARGSUSED */
-ushort_t
-atomic_dec_ushort_nv(volatile ushort_t *target)
-{ return (0); }
-
-/* ARGSUSED */
-uint32_t
-atomic_dec_32_nv(volatile uint32_t *target)
-{ return (0); }
-
-/* ARGSUSED */
-uint_t
-atomic_dec_uint_nv(volatile uint_t *target)
-{ return (0); }
-
-/* ARGSUSED */
-ulong_t
-atomic_dec_ulong_nv(volatile ulong_t *target)
-{ return (0); }
-
-/* ARGSUSED */
-uint64_t
-atomic_dec_64_nv(volatile uint64_t *target)
-{ return (0); }
-
-/* ARGSUSED */
-uint8_t
-atomic_add_8_nv(volatile uint8_t *target, int8_t value)
-{ return (0); }
-
-/* ARGSUSED */
-uchar_t
-atomic_add_char_nv(volatile uchar_t *target, signed char value)
-{ return (0); }
-
-/* ARGSUSED */
-uint16_t
-atomic_add_16_nv(volatile uint16_t *target, int16_t delta)
-{ return (0); }
-
-/* ARGSUSED */
-ushort_t
-atomic_add_short_nv(volatile ushort_t *target, short value)
-{ return (0); }
-
-/* ARGSUSED */
-uint32_t
-atomic_add_32_nv(volatile uint32_t *target, int32_t delta)
-{ return (0); }
-
-/* ARGSUSED */
-uint_t
-atomic_add_int_nv(volatile uint_t *target, int delta)
-{ return (0); }
-
-/* ARGSUSED */
-void *
-atomic_add_ptr_nv(volatile void *target, ssize_t value)
-{ return (0); }
-
-/* ARGSUSED */
-ulong_t
-atomic_add_long_nv(volatile ulong_t *target, long delta)
-{ return (0); }
-
-/* ARGSUSED */
-uint64_t
-atomic_add_64_nv(volatile uint64_t *target, int64_t delta)
-{ return (0); }
-
-/* ARGSUSED */
-uint8_t
-atomic_or_8_nv(volatile uint8_t *target, uint8_t value)
-{ return (0); }
-
-/* ARGSUSED */
-uchar_t
-atomic_or_uchar_nv(volatile uchar_t *target, uchar_t value)
-{ return (0); }
-
-/* ARGSUSED */
-uint16_t
-atomic_or_16_nv(volatile uint16_t *target, uint16_t value)
-{ return (0); }
-
-/* ARGSUSED */
-ushort_t
-atomic_or_ushort_nv(volatile ushort_t *target, ushort_t value)
-{ return (0); }
-
-/* ARGSUSED */
-uint32_t
-atomic_or_32_nv(volatile uint32_t *target, uint32_t value)
-{ return (0); }
-
-/* ARGSUSED */
-uint_t
-atomic_or_uint_nv(volatile uint_t *target, uint_t value)
-{ return (0); }
-
-/* ARGSUSED */
-ulong_t
-atomic_or_ulong_nv(volatile ulong_t *target, ulong_t value)
-{ return (0); }
-
-/* ARGSUSED */
-uint64_t
-atomic_or_64_nv(volatile uint64_t *target, uint64_t value)
-{ return (0); }
-
-/* ARGSUSED */
-uint8_t
-atomic_and_8_nv(volatile uint8_t *target, uint8_t value)
-{ return (0); }
-
-/* ARGSUSED */
-uchar_t
-atomic_and_uchar_nv(volatile uchar_t *target, uchar_t value)
-{ return (0); }
-
-/* ARGSUSED */
-uint16_t
-atomic_and_16_nv(volatile uint16_t *target, uint16_t value)
-{ return (0); }
-
-/* ARGSUSED */
-ushort_t
-atomic_and_ushort_nv(volatile ushort_t *target, ushort_t value)
-{ return (0); }
-
-/* ARGSUSED */
-uint32_t
-atomic_and_32_nv(volatile uint32_t *target, uint32_t value)
-{ return (0); }
-
-/* ARGSUSED */
-uint_t
-atomic_and_uint_nv(volatile uint_t *target, uint_t value)
-{ return (0); }
-
-/* ARGSUSED */
-ulong_t
-atomic_and_ulong_nv(volatile ulong_t *target, ulong_t value)
-{ return (0); }
-
-/* ARGSUSED */
-uint64_t
-atomic_and_64_nv(volatile uint64_t *target, uint64_t value)
-{ return (0); }
-
-/* ARGSUSED */
-uint8_t
-atomic_cas_8(volatile uint8_t *target, uint8_t cmp, uint8_t new)
-{ return (0); }
-
-/* ARGSUSED */
-uchar_t
-atomic_cas_uchar(volatile uchar_t *target, uchar_t cmp, uchar_t new)
-{ return (0); }
-
-/* ARGSUSED */
-uint16_t
-atomic_cas_16(volatile uint16_t *target, uint16_t cmp, uint16_t new)
-{ return (0); }
-
-/* ARGSUSED */
-ushort_t
-atomic_cas_ushort(volatile ushort_t *target, ushort_t cmp, ushort_t new)
-{ return (0); }
-
-/* ARGSUSED */
-uint32_t
-atomic_cas_32(volatile uint32_t *target, uint32_t cmp, uint32_t new)
-{ return (0); }
-
-/* ARGSUSED */
-uint_t
-atomic_cas_uint(volatile uint_t *target, uint_t cmp, uint_t new)
-{ return (0); }
-
-/* ARGSUSED */
-ulong_t
-atomic_cas_ulong(volatile ulong_t *target, ulong_t cmp, ulong_t new)
-{ return (0); }
-
-/* ARGSUSED */
-uint64_t
-atomic_cas_uint64(volatile uint64_t *target, ulong_t cmp, uint64_t new)
-{ return (0); }
-
-/* ARGSUSED */
-void *
-atomic_cas_ptr(volatile void *target, void *cmp, void *new)
-{ return (0); }
-
-/* ARGSUSED */
-uint8_t
-atomic_swap_8(volatile uint8_t *target, uint8_t new)
-{ return (0); }
-
-/* ARGSUSED */
-uchar_t
-atomic_swap_char(volatile uchar_t *target, uchar_t new)
-{ return (0); }
-
-/* ARGSUSED */
-uint16_t
-atomic_swap_16(volatile uint16_t *target, uint16_t new)
-{ return (0); }
-
-/* ARGSUSED */
-ushort_t
-atomic_swap_ushort(volatile ushort_t *target, ushort_t new)
-{ return (0); }
-
-/* ARGSUSED */
-uint32_t
-atomic_swap_32(volatile uint32_t *target, uint32_t new)
-{ return (0); }
-
-/* ARGSUSED */
-uint_t
-atomic_swap_uint(volatile uint_t *target, uint_t new)
-{ return (0); }
-
-/* ARGSUSED */
-uint64_t
-atomic_swap_64(volatile uint64_t *target, uint64_t new)
-{ return (0); }
-
-/* ARGSUSED */
-void *
-atomic_swap_ptr(volatile void *target, void *new)
-{ return (0); }
-
-/* ARGSUSED */
-ulong_t
-atomic_swap_ulong(volatile ulong_t *target, ulong_t new)
-{ return (0); }
-
-/* ARGSUSED */
-int
-atomic_set_long_excl(volatile ulong_t *target, uint_t value)
-{ return (0); }
-
-/* ARGSUSED */
-int
-atomic_clear_long_excl(volatile ulong_t *target, uint_t value)
-{ return (0); }
-
-#else	/* lint */
-
-	/*
-	 * Legacy kernel interfaces; they will go away (eventually).
-	 */
-	ANSI_PRAGMA_WEAK2(cas8,atomic_cas_8,function)
-	ANSI_PRAGMA_WEAK2(cas32,atomic_cas_32,function)
-	ANSI_PRAGMA_WEAK2(cas64,atomic_cas_64,function)
-	ANSI_PRAGMA_WEAK2(caslong,atomic_cas_ulong,function)
-	ANSI_PRAGMA_WEAK2(casptr,atomic_cas_ptr,function)
-	ANSI_PRAGMA_WEAK2(atomic_and_long,atomic_and_ulong,function)
-	ANSI_PRAGMA_WEAK2(atomic_or_long,atomic_or_ulong,function)
-	ANSI_PRAGMA_WEAK2(swapl,atomic_swap_32,function)
-
-	ENTRY(atomic_inc_8)
-	ALTENTRY(atomic_inc_8_nv)
-	ALTENTRY(atomic_inc_uchar)
-	ALTENTRY(atomic_inc_uchar_nv)
-	ba	add_8
-	  add	%g0, 1, %o1
-	SET_SIZE(atomic_inc_uchar_nv)
-	SET_SIZE(atomic_inc_uchar)
-	SET_SIZE(atomic_inc_8_nv)
-	SET_SIZE(atomic_inc_8)
-
-	ENTRY(atomic_dec_8)
-	ALTENTRY(atomic_dec_8_nv)
-	ALTENTRY(atomic_dec_uchar)
-	ALTENTRY(atomic_dec_uchar_nv)
-	ba	add_8
-	  sub	%g0, 1, %o1
-	SET_SIZE(atomic_dec_uchar_nv)
-	SET_SIZE(atomic_dec_uchar)
-	SET_SIZE(atomic_dec_8_nv)
-	SET_SIZE(atomic_dec_8)
-
-	ENTRY(atomic_add_8)
-	ALTENTRY(atomic_add_8_nv)
-	ALTENTRY(atomic_add_char)
-	ALTENTRY(atomic_add_char_nv)
-add_8:	chkpt (add08_tmfail)		! Enter transaction
-	ldub	[%o0], %o2              ! read old value
-	add     %o2, %o1, %o3           ! add value to the old value
-	stub	%o3, [%o0]		! Store back
-	commit				! Commit transaction
-	retl
-	 mov	%o3, %o0		! Retrun result for free in delay slot
-add08_tmfail:
-	and     %o0, 0x3, %o4           ! %o4 = byte offset, left-to-right
-	xor     %o4, 0x3, %g1           ! %g1 = byte offset, right-to-left
-	sll     %g1, 3, %g1             ! %g1 = bit offset, right-to-left
-	set     0xff, %o3               ! %o3 = mask
-	sll     %o3, %g1, %o3           ! %o3 = shifted to bit offset
-	sll     %o1, %g1, %o1           ! %o1 = shifted to bit offset
-	and     %o1, %o3, %o1           ! %o1 = single byte value
-	andn    %o0, 0x3, %o0           ! %o0 = word address
-	ld      [%o0], %o2              ! read old value
-1:
-	add     %o2, %o1, %o5           ! add value to the old value
-	and     %o5, %o3, %o5           ! clear other bits
-	andn    %o2, %o3, %o4           ! clear target bits
-	or      %o4, %o5, %o5           ! insert the new value
-	cas     [%o0], %o2, %o5
-	cmp     %o2, %o5
-	bne,a,pn %icc, 1b
-	  mov   %o5, %o2                ! %o2 = old value
-	add     %o2, %o1, %o5
-	and     %o5, %o3, %o5
-	retl
-	srl     %o5, %g1, %o0           ! %o0 = new value
-	SET_SIZE(atomic_add_char_nv)
-	SET_SIZE(atomic_add_char)
-	SET_SIZE(atomic_add_8_nv)
-	SET_SIZE(atomic_add_8)
-
-
-
-	ENTRY(atomic_inc_16)
-	ALTENTRY(atomic_inc_16_nv)
-	ALTENTRY(atomic_inc_ushort)
-	ALTENTRY(atomic_inc_ushort_nv)
-	ba	add_16
-	  add	%g0, 1, %o1
-	SET_SIZE(atomic_inc_ushort_nv)
-	SET_SIZE(atomic_inc_ushort)
-	SET_SIZE(atomic_inc_16_nv)
-	SET_SIZE(atomic_inc_16)
-
-	ENTRY(atomic_dec_16)
-	ALTENTRY(atomic_dec_16_nv)
-	ALTENTRY(atomic_dec_ushort)
-	ALTENTRY(atomic_dec_ushort_nv)
-	ba	add_16
-	  sub	%g0, 1, %o1
-	SET_SIZE(atomic_dec_ushort_nv)
-	SET_SIZE(atomic_dec_ushort)
-	SET_SIZE(atomic_dec_16_nv)
-	SET_SIZE(atomic_dec_16)
-
-	ENTRY(atomic_add_16)
-	ALTENTRY(atomic_add_16_nv)
-	ALTENTRY(atomic_add_short)
-	ALTENTRY(atomic_add_short_nv)
-add_16:	chkpt (add16_tmfail)		! Enter transaction
-	lduh	[%o0], %o2              ! read old value
-	add     %o2, %o1, %o3           ! add value to the old value
-	stuh	%o3, [%o0]		! Store back
-	commit				! Commit transaction
-	retl
-	 mov	%o3, %o0		! Retrun result for free in delay slot
-add16_tmfail:
-	and     %o0, 0x2, %o4           ! %o4 = byte offset, left-to-right
-	xor     %o4, 0x2, %g1           ! %g1 = byte offset, right-to-left
-	sll     %o4, 3, %o4             ! %o4 = bit offset, left-to-right
-	sll     %g1, 3, %g1             ! %g1 = bit offset, right-to-left
-	sethi   %hi(0xffff0000), %o3    ! %o3 = mask
-	srl     %o3, %o4, %o3           ! %o3 = shifted to bit offset
-	sll     %o1, %g1, %o1           ! %o1 = shifted to bit offset
-	and     %o1, %o3, %o1           ! %o1 = single short value
-	andn    %o0, 0x2, %o0           ! %o0 = word address
-	! if low-order bit is 1, we will properly get an alignment fault here
-	ld      [%o0], %o2              ! read old value
-1:
-	add     %o1, %o2, %o5           ! add value to the old value
-	and     %o5, %o3, %o5           ! clear other bits
-	andn    %o2, %o3, %o4           ! clear target bits
-	or      %o4, %o5, %o5           ! insert the new value
-	cas     [%o0], %o2, %o5
-	cmp     %o2, %o5
-	bne,a,pn %icc, 1b
-	  mov   %o5, %o2                ! %o2 = old value
-	add     %o1, %o2, %o5
-	and     %o5, %o3, %o5
-	retl
-	srl     %o5, %g1, %o0           ! %o0 = new value
-	SET_SIZE(atomic_add_short_nv)
-	SET_SIZE(atomic_add_short)
-	SET_SIZE(atomic_add_16_nv)
-	SET_SIZE(atomic_add_16)
-
-
-
-	ENTRY(atomic_inc_32)
-	ALTENTRY(atomic_inc_32_nv)
-	ALTENTRY(atomic_inc_uint)
-	ALTENTRY(atomic_inc_uint_nv)
-	ba	add_32
-	  add	%g0, 1, %o1
-	SET_SIZE(atomic_inc_uint_nv)
-	SET_SIZE(atomic_inc_uint)
-	SET_SIZE(atomic_inc_32_nv)
-	SET_SIZE(atomic_inc_32)
-
-	ENTRY(atomic_dec_32)
-	ALTENTRY(atomic_dec_32_nv)
-	ALTENTRY(atomic_dec_uint)
-	ALTENTRY(atomic_dec_uint_nv)
-	ba	add_32
-	  sub	%g0, 1, %o1
-	SET_SIZE(atomic_dec_uint_nv)
-	SET_SIZE(atomic_dec_uint)
-	SET_SIZE(atomic_dec_32_nv)
-	SET_SIZE(atomic_dec_32)
-
-	ENTRY(atomic_add_32)
-	ALTENTRY(atomic_add_32_nv)
-	ALTENTRY(atomic_add_int)
-	ALTENTRY(atomic_add_int_nv)
-add_32: chkpt (add32_tmfail)		! Enter transaction
-	lduw    [%o0], %o2              ! read old value
-	add     %o2, %o1, %o3           ! add value to the old value
-	stuw    %o3, [%o0]              ! Store back
-	commit                          ! Commit transaction
-	retl
-	 mov    %o3, %o0                ! Retrun result for free in delay slot
-add32_tmfail:
-	ld	[%o0], %o2
-1:
-	add	%o2, %o1, %o3
-	cas	[%o0], %o2, %o3
-	cmp	%o2, %o3
-	bne,a,pn %icc, 1b
-	  mov	%o3, %o2
-	retl
-	add	%o2, %o1, %o0		! return new value
-	SET_SIZE(atomic_add_int_nv)
-	SET_SIZE(atomic_add_int)
-	SET_SIZE(atomic_add_32_nv)
-	SET_SIZE(atomic_add_32)
-
-
-
-	ENTRY(atomic_inc_64)
-	ALTENTRY(atomic_inc_64_nv)
-	ALTENTRY(atomic_inc_ulong)
-	ALTENTRY(atomic_inc_ulong_nv)
-	ba	add_64
-	  add	%g0, 1, %o1
-	SET_SIZE(atomic_inc_ulong_nv)
-	SET_SIZE(atomic_inc_ulong)
-	SET_SIZE(atomic_inc_64_nv)
-	SET_SIZE(atomic_inc_64)
-
-	ENTRY(atomic_dec_64)
-	ALTENTRY(atomic_dec_64_nv)
-	ALTENTRY(atomic_dec_ulong)
-	ALTENTRY(atomic_dec_ulong_nv)
-	ba	add_64
-	  sub	%g0, 1, %o1
-	SET_SIZE(atomic_dec_ulong_nv)
-	SET_SIZE(atomic_dec_ulong)
-	SET_SIZE(atomic_dec_64_nv)
-	SET_SIZE(atomic_dec_64)
-
-	ENTRY(atomic_add_64)
-	ALTENTRY(atomic_add_64_nv)
-	ALTENTRY(atomic_add_ptr)
-	ALTENTRY(atomic_add_ptr_nv)
-	ALTENTRY(atomic_add_long)
-	ALTENTRY(atomic_add_long_nv)
-add_64: chkpt (add64_tmfail)		! Enter transaction
-	ldx     [%o0], %o2              ! read old value
-	add     %o2, %o1, %o3           ! add value to the old value
-	stx     %o3, [%o0]              ! Store back
-	commit                          ! Commit transaction
-	retl
-	 mov    %o3, %o0                ! Retrun result for free in delay slot
-add64_tmfail:
-	ldx     [%o0], %o2
-1:
-	add     %o2, %o1, %o3
-	casx    [%o0], %o2, %o3
-	cmp     %o2, %o3
-	bne,a,pn %xcc, 1b
-	  mov   %o3, %o2
-	retl
-	add     %o2, %o1, %o0           ! return new value
-	SET_SIZE(atomic_add_long_nv)
-	SET_SIZE(atomic_add_long)
-	SET_SIZE(atomic_add_ptr_nv)
-	SET_SIZE(atomic_add_ptr)
-	SET_SIZE(atomic_add_64_nv)
-	SET_SIZE(atomic_add_64)
-
-
-
-	ENTRY(atomic_or_8)
-	ALTENTRY(atomic_or_8_nv)
-	ALTENTRY(atomic_or_uchar)
-	ALTENTRY(atomic_or_uchar_nv)
-	chkpt (or08_tmfail)	
-	ldub	[%o0], %o2     
-	or	%o2, %o1, %o3 
-	stub	%o3, [%o0]
-	commit		
-	retl
-	 mov	%o3, %o0
-or08_tmfail:
-	and     %o0, 0x3, %o4           ! %o4 = byte offset, left-to-right
-	xor     %o4, 0x3, %g1           ! %g1 = byte offset, right-to-left
-	sll     %g1, 3, %g1             ! %g1 = bit offset, right-to-left
-	set     0xff, %o3               ! %o3 = mask
-	sll     %o3, %g1, %o3           ! %o3 = shifted to bit offset
-	sll     %o1, %g1, %o1           ! %o1 = shifted to bit offset
-	and     %o1, %o3, %o1           ! %o1 = single byte value
-	andn    %o0, 0x3, %o0           ! %o0 = word address
-	ld      [%o0], %o2              ! read old value
-1:
-	or      %o2, %o1, %o5           ! or in the new value
-	cas     [%o0], %o2, %o5
-	cmp     %o2, %o5
-	bne,a,pn %icc, 1b
-	  mov   %o5, %o2                ! %o2 = old value
-	or      %o2, %o1, %o5
-	and     %o5, %o3, %o5
-	retl
-	srl     %o5, %g1, %o0           ! %o0 = new value
-	SET_SIZE(atomic_or_uchar_nv)
-	SET_SIZE(atomic_or_uchar)
-	SET_SIZE(atomic_or_8_nv)
-	SET_SIZE(atomic_or_8)
-
-
-
-	ENTRY(atomic_or_16)
-	ALTENTRY(atomic_or_16_nv)
-	ALTENTRY(atomic_or_ushort)
-	ALTENTRY(atomic_or_ushort_nv)
-	chkpt (or16_tmfail)	
-	lduh	[%o0], %o2     
-	or	%o2, %o1, %o3 
-	stuh	%o3, [%o0]
-	commit		
-	retl
-	 mov	%o3, %o0
-or16_tmfail:
-	and     %o0, 0x2, %o4           ! %o4 = byte offset, left-to-right
-	xor     %o4, 0x2, %g1           ! %g1 = byte offset, right-to-left
-	sll     %o4, 3, %o4             ! %o4 = bit offset, left-to-right
-	sll     %g1, 3, %g1             ! %g1 = bit offset, right-to-left
-	sethi   %hi(0xffff0000), %o3    ! %o3 = mask
-	srl     %o3, %o4, %o3           ! %o3 = shifted to bit offset
-	sll     %o1, %g1, %o1           ! %o1 = shifted to bit offset
-	and     %o1, %o3, %o1           ! %o1 = single short value
-	andn    %o0, 0x2, %o0           ! %o0 = word address
-	! if low-order bit is 1, we will properly get an alignment fault here
-	ld      [%o0], %o2              ! read old value
-1:
-	or      %o2, %o1, %o5           ! or in the new value
-	cas     [%o0], %o2, %o5
-	cmp     %o2, %o5
-	bne,a,pn %icc, 1b
-	  mov   %o5, %o2                ! %o2 = old value
-	or      %o2, %o1, %o5           ! or in the new value
-	and     %o5, %o3, %o5
-	retl
-	srl     %o5, %g1, %o0           ! %o0 = new value
-	SET_SIZE(atomic_or_ushort_nv)
-	SET_SIZE(atomic_or_ushort)
-	SET_SIZE(atomic_or_16_nv)
-	SET_SIZE(atomic_or_16)
-
-
-
-	ENTRY(atomic_or_32)
-	ALTENTRY(atomic_or_32_nv)
-	ALTENTRY(atomic_or_uint)
-	ALTENTRY(atomic_or_uint_nv)
-	chkpt (or32_tmfail)	
-	lduw	[%o0], %o2     
-	or	%o2, %o1, %o3 
-	stuw	%o3, [%o0]
-	commit		
-	retl
-	 mov	%o3, %o0
-or32_tmfail:
-	ld      [%o0], %o2
-1:
-	or      %o2, %o1, %o3
-	cas     [%o0], %o2, %o3
-	cmp     %o2, %o3
-	bne,a,pn %icc, 1b
-	  mov   %o3, %o2
-	retl
-	or      %o2, %o1, %o0           ! return new value
-	SET_SIZE(atomic_or_uint_nv)
-	SET_SIZE(atomic_or_uint)
-	SET_SIZE(atomic_or_32_nv)
-	SET_SIZE(atomic_or_32)
-
-
-
-	ENTRY(atomic_or_64)
-	ALTENTRY(atomic_or_64_nv)
-	ALTENTRY(atomic_or_ulong)
-	ALTENTRY(atomic_or_ulong_nv)
-	chkpt (or64_tmfail)	
-	ldx	[%o0], %o2     
-	or	%o2, %o1, %o3 
-	stx	%o3, [%o0]
-	commit		
-	retl
-	 mov	%o3, %o0
-or64_tmfail:
-	ldx     [%o0], %o2
-1:
-	or      %o2, %o1, %o3
-	casx    [%o0], %o2, %o3
-	cmp     %o2, %o3
-	bne,a,pn %xcc, 1b
-	  mov   %o3, %o2
-	retl
-	or      %o2, %o1, %o0           ! return new value
-	SET_SIZE(atomic_or_ulong_nv)
-	SET_SIZE(atomic_or_ulong)
-	SET_SIZE(atomic_or_64_nv)
-	SET_SIZE(atomic_or_64)
-
-
-
-	ENTRY(atomic_and_8)
-	ALTENTRY(atomic_and_8_nv)
-	ALTENTRY(atomic_and_uchar)
-	ALTENTRY(atomic_and_uchar_nv)
-	chkpt (and08_tmfail)	
-	ldub	[%o0], %o2     
-	and	%o2, %o1, %o3 
-	stub	%o3, [%o0]
-	commit		
-	retl
-	 mov	%o3, %o0
-and08_tmfail:
-	and     %o0, 0x3, %o4           ! %o4 = byte offset, left-to-right
-	xor     %o4, 0x3, %g1           ! %g1 = byte offset, right-to-left
-	sll     %g1, 3, %g1             ! %g1 = bit offset, right-to-left
-	set     0xff, %o3               ! %o3 = mask
-	sll     %o3, %g1, %o3           ! %o3 = shifted to bit offset
-	sll     %o1, %g1, %o1           ! %o1 = shifted to bit offset
-	orn     %o1, %o3, %o1           ! all ones in other bytes
-	andn    %o0, 0x3, %o0           ! %o0 = word address
-	ld      [%o0], %o2              ! read old value
-1:
-	and     %o2, %o1, %o5           ! and in the new value
-	cas     [%o0], %o2, %o5
-	cmp     %o2, %o5
-	bne,a,pn %icc, 1b
-	  mov   %o5, %o2                ! %o2 = old value
-	and     %o2, %o1, %o5
-	and     %o5, %o3, %o5
-	retl
-	srl     %o5, %g1, %o0           ! %o0 = new value
-	SET_SIZE(atomic_and_uchar_nv)
-	SET_SIZE(atomic_and_uchar)
-	SET_SIZE(atomic_and_8_nv)
-	SET_SIZE(atomic_and_8)
-
-
-
-	ENTRY(atomic_and_16)
-	ALTENTRY(atomic_and_16_nv)
-	ALTENTRY(atomic_and_ushort)
-	ALTENTRY(atomic_and_ushort_nv)
-	chkpt (and16_tmfail)	
-	lduh	[%o0], %o2     
-	and	%o2, %o1, %o3 
-	stuh	%o3, [%o0]
-	commit		
-	retl
-	 mov	%o3, %o0
-and16_tmfail:
-	and     %o0, 0x2, %o4           ! %o4 = byte offset, left-to-right
-	xor     %o4, 0x2, %g1           ! %g1 = byte offset, right-to-left
-	sll     %o4, 3, %o4             ! %o4 = bit offset, left-to-right
-	sll     %g1, 3, %g1             ! %g1 = bit offset, right-to-left
-	sethi   %hi(0xffff0000), %o3    ! %o3 = mask
-	srl     %o3, %o4, %o3           ! %o3 = shifted to bit offset
-	sll     %o1, %g1, %o1           ! %o1 = shifted to bit offset
-	orn     %o1, %o3, %o1           ! all ones in the other half
-	andn    %o0, 0x2, %o0           ! %o0 = word address
-	! if low-order bit is 1, we will properly get an alignment fault here
-	ld      [%o0], %o2              ! read old value
-1:
-	and     %o2, %o1, %o5           ! and in the new value
-	cas     [%o0], %o2, %o5
-	cmp     %o2, %o5
-	bne,a,pn %icc, 1b
-	  mov   %o5, %o2                ! %o2 = old value
-	and     %o2, %o1, %o5
-	and     %o5, %o3, %o5
-	retl
-	srl     %o5, %g1, %o0           ! %o0 = new value
-	SET_SIZE(atomic_and_ushort_nv)
-	SET_SIZE(atomic_and_ushort)
-	SET_SIZE(atomic_and_16_nv)
-	SET_SIZE(atomic_and_16)
-
-
-
-	ENTRY(atomic_and_32)
-	ALTENTRY(atomic_and_32_nv)
-	ALTENTRY(atomic_and_uint)
-	ALTENTRY(atomic_and_uint_nv)
-	chkpt (and32_tmfail)	
-	lduw	[%o0], %o2     
-	and	%o2, %o1, %o3 
-	stuw	%o3, [%o0]
-	commit		
-	retl
-	 mov	%o3, %o0
-and32_tmfail:
-	ld      [%o0], %o2
-1:
-	and     %o2, %o1, %o3
-	cas     [%o0], %o2, %o3
-	cmp     %o2, %o3
-	bne,a,pn %icc, 1b
-	  mov   %o3, %o2
-	retl
-	and     %o2, %o1, %o0           ! return new value
-	SET_SIZE(atomic_and_uint_nv)
-	SET_SIZE(atomic_and_uint)
-	SET_SIZE(atomic_and_32_nv)
-	SET_SIZE(atomic_and_32)
-
-
-
-	ENTRY(atomic_and_64)
-	ALTENTRY(atomic_and_64_nv)
-	ALTENTRY(atomic_and_ulong)
-	ALTENTRY(atomic_and_ulong_nv)
-	chkpt (and64_tmfail)	
-	ldx	[%o0], %o2     
-	and	%o2, %o1, %o3 
-	stx	%o3, [%o0]
-	commit		
-	retl
-	 mov	%o3, %o0
-and64_tmfail:
-	ldx     [%o0], %o2
-1:
-	and     %o2, %o1, %o3
-	casx    [%o0], %o2, %o3
-	cmp     %o2, %o3
-	bne,a,pn %xcc, 1b
-	  mov   %o3, %o2
-	retl
-	and     %o2, %o1, %o0           ! return new value
-	SET_SIZE(atomic_and_ulong_nv)
-	SET_SIZE(atomic_and_ulong)
-	SET_SIZE(atomic_and_64_nv)
-	SET_SIZE(atomic_and_64)
-
-
-
-	ENTRY(atomic_cas_8)
-	ALTENTRY(atomic_cas_uchar)
-	chkpt (cas08_tmfail)
-	ldub    [%o0], %o3
-	cmp     %o3, %o1
-	bne,a,pn %icc, ret_cas8
-	nop
-	stub    %o2, [%o0]
-	commit
-ret_cas8:    retl
-	mov     %o3, %o2
-cas08_tmfail:
-	and     %o0, 0x3, %o4           ! %o4 = byte offset, left-to-right
-	xor     %o4, 0x3, %g1           ! %g1 = byte offset, right-to-left
-	sll     %g1, 3, %g1             ! %g1 = bit offset, right-to-left
-	set     0xff, %o3               ! %o3 = mask
-	sll     %o3, %g1, %o3           ! %o3 = shifted to bit offset
-	sll     %o1, %g1, %o1           ! %o1 = shifted to bit offset
-	and     %o1, %o3, %o1           ! %o1 = single byte value
-	sll     %o2, %g1, %o2           ! %o2 = shifted to bit offset
-	and     %o2, %o3, %o2           ! %o2 = single byte value
-	andn    %o0, 0x3, %o0           ! %o0 = word address
-	ld      [%o0], %o4              ! read old value
-1:
-	andn    %o4, %o3, %o4           ! clear target bits
-	or      %o4, %o2, %o5           ! insert the new value
-	or      %o4, %o1, %o4           ! insert the comparison value
-	cas     [%o0], %o4, %o5
-	cmp     %o4, %o5                ! did we succeed?
-	be,pt   %icc, 2f
-	  and   %o5, %o3, %o4           ! isolate the old value
-	cmp     %o1, %o4                ! should we have succeeded?
-	be,a,pt %icc, 1b                ! yes, try again
-	  mov   %o5, %o4                ! %o4 = old value
-2:
-	retl
-	srl     %o4, %g1, %o0           ! %o0 = old value
-	SET_SIZE(atomic_cas_uchar)
-	SET_SIZE(atomic_cas_8)
-
-
-
-	ENTRY(atomic_cas_16)
-	ALTENTRY(atomic_cas_ushort)
-	chkpt (cas16_tmfail)
-	lduh    [%o0], %o3
-	cmp     %o3, %o1
-	bne,a,pn %icc, ret_cas16
-	nop
-	stuh    %o2, [%o0]
-	commit
-ret_cas16:    retl
-	mov     %o3, %o2
-cas16_tmfail:
-	and     %o0, 0x2, %o4           ! %o4 = byte offset, left-to-right
-	xor     %o4, 0x2, %g1           ! %g1 = byte offset, right-to-left
-	sll     %o4, 3, %o4             ! %o4 = bit offset, left-to-right
-	sll     %g1, 3, %g1             ! %g1 = bit offset, right-to-left
-	sethi   %hi(0xffff0000), %o3    ! %o3 = mask
-	srl     %o3, %o4, %o3           ! %o3 = shifted to bit offset
-	sll     %o1, %g1, %o1           ! %o1 = shifted to bit offset
-	and     %o1, %o3, %o1           ! %o1 = single short value
-	sll     %o2, %g1, %o2           ! %o2 = shifted to bit offset
-	and     %o2, %o3, %o2           ! %o2 = single short value
-	andn    %o0, 0x2, %o0           ! %o0 = word address
-	! if low-order bit is 1, we will properly get an alignment fault here
-	ld      [%o0], %o4              ! read old value
-1:
-	andn    %o4, %o3, %o4           ! clear target bits
-	or      %o4, %o2, %o5           ! insert the new value
-	or      %o4, %o1, %o4           ! insert the comparison value
-	cas     [%o0], %o4, %o5
-	cmp     %o4, %o5                ! did we succeed?
-	be,pt   %icc, 2f
-	  and   %o5, %o3, %o4           ! isolate the old value
-	cmp     %o1, %o4                ! should we have succeeded?
-	be,a,pt %icc, 1b                ! yes, try again
-	  mov   %o5, %o4                ! %o4 = old value
-2:
-	retl
-	srl     %o4, %g1, %o0           ! %o0 = old value
-	SET_SIZE(atomic_cas_ushort)
-	SET_SIZE(atomic_cas_16)
-
-
-	ENTRY(atomic_cas_32)
-	ALTENTRY(atomic_cas_uint)
-	cas	[%o0], %o1, %o2
-	retl
-	mov	%o2, %o0
-	SET_SIZE(atomic_cas_uint)
-	SET_SIZE(atomic_cas_32)
-
-
-	ENTRY(atomic_cas_64)
-	ALTENTRY(atomic_cas_ptr)
-	ALTENTRY(atomic_cas_ulong)
-	casx	[%o0], %o1, %o2
-	retl
-	mov	%o2, %o0
-	SET_SIZE(atomic_cas_ulong)
-	SET_SIZE(atomic_cas_ptr)
-	SET_SIZE(atomic_cas_64)
-
-
-	ENTRY(atomic_swap_8)
-	ALTENTRY(atomic_swap_uchar)
-	chkpt (swp08_tmfail)
-	ldub    [%o0], %o2
-	stub    %o1, [%o0]
-	commit
-	retl
-	mov     %o2, %o1
-swp08_tmfail:
-	and     %o0, 0x3, %o4           ! %o4 = byte offset, left-to-right
-	xor     %o4, 0x3, %g1           ! %g1 = byte offset, right-to-left
-	sll     %g1, 3, %g1             ! %g1 = bit offset, right-to-left
-	set     0xff, %o3               ! %o3 = mask
-	sll     %o3, %g1, %o3           ! %o3 = shifted to bit offset
-	sll     %o1, %g1, %o1           ! %o1 = shifted to bit offset
-	and     %o1, %o3, %o1           ! %o1 = single byte value
-	andn    %o0, 0x3, %o0           ! %o0 = word address
-	ld      [%o0], %o2              ! read old value
-1:
-	andn    %o2, %o3, %o5           ! clear target bits
-	or      %o5, %o1, %o5           ! insert the new value
-	cas     [%o0], %o2, %o5
-	cmp     %o2, %o5
-	bne,a,pn %icc, 1b
-	  mov   %o5, %o2                ! %o2 = old value
-	and     %o5, %o3, %o5
-	retl
-	srl     %o5, %g1, %o0           ! %o0 = old value
-	SET_SIZE(atomic_swap_uchar)
-	SET_SIZE(atomic_swap_8)
-
-
-	ENTRY(atomic_swap_16)
-	ALTENTRY(atomic_swap_ushort)
-	chkpt (swp16_tmfail)
-	lduh    [%o0], %o2
-	stuh    %o1, [%o0]
-	commit
-	retl
-	mov     %o2, %o1
-swp16_tmfail:
-	and     %o0, 0x2, %o4           ! %o4 = byte offset, left-to-right
-	xor     %o4, 0x2, %g1           ! %g1 = byte offset, right-to-left
-	sll     %o4, 3, %o4             ! %o4 = bit offset, left-to-right
-	sll     %g1, 3, %g1             ! %g1 = bit offset, right-to-left
-	sethi   %hi(0xffff0000), %o3    ! %o3 = mask
-	srl     %o3, %o4, %o3           ! %o3 = shifted to bit offset
-	sll     %o1, %g1, %o1           ! %o1 = shifted to bit offset
-	and     %o1, %o3, %o1           ! %o1 = single short value
-	andn    %o0, 0x2, %o0           ! %o0 = word address
-	! if low-order bit is 1, we will properly get an alignment fault here
-	ld      [%o0], %o2              ! read old value
-1:
-	andn    %o2, %o3, %o5           ! clear target bits
-	or      %o5, %o1, %o5           ! insert the new value
-	cas     [%o0], %o2, %o5
-	cmp     %o2, %o5
-	bne,a,pn %icc, 1b
-	  mov   %o5, %o2                ! %o2 = old value
-	and     %o5, %o3, %o5
-	retl
-	srl     %o5, %g1, %o0           ! %o0 = old value
-	SET_SIZE(atomic_swap_ushort)
-	SET_SIZE(atomic_swap_16)
-
-
-	ENTRY(atomic_swap_32)
-	ALTENTRY(atomic_swap_uint)
-	chkpt (swp32_tmfail)
-	lduw    [%o0], %o2
-	stuw    %o1, [%o0]
-	commit
-	retl
-	mov     %o2, %o1
-swp32_tmfail:
-	ld      [%o0], %o2
-1:
-	mov     %o1, %o3
-	cas     [%o0], %o2, %o3
-	cmp     %o2, %o3
-	bne,a,pn %icc, 1b
-	  mov   %o3, %o2
-	retl
-	mov     %o3, %o0
-	SET_SIZE(atomic_swap_uint)
-	SET_SIZE(atomic_swap_32)
-
-
-	ENTRY(atomic_swap_64)
-	ALTENTRY(atomic_swap_ptr)
-	ALTENTRY(atomic_swap_ulong)
-
-	chkpt (swp64_tmfail)
-	ldx    [%o0], %o2
-	stx    %o1, [%o0]
-	commit
-	retl
-	mov     %o2, %o1
-swp64_tmfail:
-	ldx     [%o0], %o2
-1:
-	mov     %o1, %o3
-	casx    [%o0], %o2, %o3
-	cmp     %o2, %o3
-	bne,a,pn %xcc, 1b
-	  mov   %o3, %o2
-	retl
-	mov     %o3, %o0
-	SET_SIZE(atomic_swap_ulong)
-	SET_SIZE(atomic_swap_ptr)
-	SET_SIZE(atomic_swap_64)
-
-
-	ENTRY(atomic_set_long_excl)
-	mov	1, %o3
-	slln	%o3, %o1, %o3
-	chkpt(slong_tmfail)
-	ldn	[%o0], %o2
-	or	%o2, %o3, %o4		! set the bit, and try to commit it
-	stn	%o4, [%o0]
-	commit
-	retl
-	mov	%o4, %o0
-slong_tmfail:
-	ldn	[%o0], %o2
-1:
-	andcc	%o2, %o3, %g0		! test if the bit is set
-	bnz,a,pn %ncc, 2f		! if so, then fail out
-	  mov	-1, %o0
-	or	%o2, %o3, %o4		! set the bit, and try to commit it
-	casn	[%o0], %o2, %o4
-	cmp	%o2, %o4
-	bne,a,pn %ncc, 1b		! failed to commit, try again
-	  mov	%o4, %o2
-	mov	%g0, %o0
-2:
-	retl
-	nop
-	SET_SIZE(atomic_set_long_excl)
-
-
-	ENTRY(atomic_clear_long_excl)
-	mov	1, %o3
-	slln	%o3, %o1, %o3
-	chkpt(clong_tmfail)
-	ldn	[%o0], %o2
-	andn	%o2, %o3, %o4		! clear the bit, and try to commit it
-	stn	%o4, [%o0]
-	commit
-	retl
-	mov	%o4, %o0
-clong_tmfail:
-	ldn	[%o0], %o2
-1:
-	andncc	%o3, %o2, %g0		! test if the bit is clear
-	bnz,a,pn %ncc, 2f		! if so, then fail out
-	  mov	-1, %o0
-	andn	%o2, %o3, %o4		! clear the bit, and try to commit it
-	casn	[%o0], %o2, %o4
-	cmp	%o2, %o4
-	bne,a,pn %ncc, 1b		! failed to commit, try again
-	  mov	%o4, %o2
-	mov	%g0, %o0
-2:
-	retl
-	nop
-	SET_SIZE(atomic_clear_long_excl)
-
-#endif	/* lint */
--- a/usr/src/uts/sun4v/io/ldc.c	Fri Jun 05 10:27:16 2009 -0400
+++ b/usr/src/uts/sun4v/io/ldc.c	Fri Jun 05 10:28:40 2009 -0400
@@ -644,6 +644,10 @@
 		return (EIO);
 	}
 
+	/* If the queue is already empty just return success. */
+	if (rx_head == rx_tail)
+		return (0);
+
 	/* flush contents by setting the head = tail */
 	return (i_ldc_set_rx_head(ldcp, rx_tail));
 }
@@ -788,7 +792,7 @@
 		drv_usecwait(ldc_delay);
 	}
 
-	cmn_err(CE_WARN, "ldc_rx_set_qhead: (0x%lx) cannot set qhead 0x%lx",
+	cmn_err(CE_WARN, "ldc_set_rx_qhead: (0x%lx) cannot set qhead 0x%lx",
 	    ldcp->id, head);
 	mutex_enter(&ldcp->tx_lock);
 	i_ldc_reset(ldcp, B_TRUE);
--- a/usr/src/uts/sun4v/rock/Makefile	Fri Jun 05 10:27:16 2009 -0400
+++ b/usr/src/uts/sun4v/rock/Makefile	Fri Jun 05 10:28:40 2009 -0400
@@ -20,7 +20,7 @@
 #
 
 #
-# Copyright 2008 Sun Microsystems, Inc.  All rights reserved.
+# Copyright 2009 Sun Microsystems, Inc.  All rights reserved.
 # Use is subject to license terms.
 #
 #	This makefile drives the production of the UltraSPARC-AT10 cpu module.
@@ -62,15 +62,21 @@
 INSTALL_TARGET	= def $(BINARY) $(ROOTMODULE)
 
 #
+# The ATOMIC_BO_ENABLE_SHIFT enables backoff in atomic routines.
+# ATOMIC_SIMPLE_BO_ENABLE enables simple backoff required for rock
+#
+ATOMIC_BO_FLAG = -DATOMIC_BO_ENABLE_SHIFT=14 -DATOMIC_SIMPLE_BO_ENABLE
+
+#
 # lint pass one enforcement
 #
-CFLAGS += $(CCVERBOSE)
+CFLAGS += $(CCVERBOSE) $(ATOMIC_BO_FLAG)
 
 #
 # cpu-module-specific flags
 #
-CPPFLAGS +=    -DCPU_MODULE
-AS_CPPFLAGS += -DCPU_MODULE -DCUSTOM_FPZERO
+CPPFLAGS +=    -DCPU_MODULE $(ATOMIC_BO_FLAG)
+AS_CPPFLAGS += -DCPU_MODULE -DCUSTOM_FPZERO $(ATOMIC_BO_FLAG)
 LINTFLAGS   += -DCUSTOM_FPZERO
 
 #