10129 Blocker bug for Driver Update project
authorJack Schwartz <Jack.A.Schwartz@Sun.COM>
Wed, 12 May 2010 22:18:21 -0700
changeset 822 2ba2b98f055e
parent 821 a3040ce58418
child 823 fb97332a0122
10129 Blocker bug for Driver Update project 15872 Driver Update support for Text Install 14935 Driver Update support for Automated Install 11968 Driver Update support for Live CD 5860 can not add 3rd party disk drivers 15788 auto-installer with Driver Update bits errs when Driver Update has nothing to do
usr/src/cmd/auto-install/Makefile
usr/src/cmd/auto-install/ai_manifest.rng
usr/src/cmd/auto-install/ai_manifest.xml
usr/src/cmd/auto-install/ai_parse_manifest.py
usr/src/cmd/auto-install/auto_ddu_lib.c
usr/src/cmd/auto-install/auto_install.c
usr/src/cmd/auto-install/auto_install.h
usr/src/cmd/auto-install/auto_parse.c
usr/src/cmd/auto-install/auto_parse_manifest.c
usr/src/cmd/auto-install/default.xml
usr/src/cmd/distro_const/auto_install/ai_sparc_image.xml
usr/src/cmd/distro_const/auto_install/ai_x86_image.xml
usr/src/cmd/distro_const/slim_cd/all_lang_slim_cd_x86.xml
usr/src/cmd/distro_const/slim_cd/slim_cd_x86.xml
usr/src/cmd/distro_const/text_install/text_mode_sparc.xml
usr/src/cmd/distro_const/text_install/text_mode_x86.xml
usr/src/cmd/slim-install/svc/Makefile
usr/src/cmd/slim-install/svc/live-var-pkg-move
usr/src/cmd/slim-install/svc/live-var-pkg-move.xml
usr/src/cmd/slim-install/user/jack/Makefile
usr/src/cmd/slim-install/user/jack/ddu_silent.desktop
usr/src/cmd/text-install/svc/text-mode-menu.ksh
usr/src/lib/install_utils/ManifestServ.py
usr/src/pkgdefs/SUNWslim-utils/prototype_com
--- a/usr/src/cmd/auto-install/Makefile	Fri May 07 11:46:54 2010 -0700
+++ b/usr/src/cmd/auto-install/Makefile	Wed May 12 22:18:21 2010 -0700
@@ -19,8 +19,7 @@
 # CDDL HEADER END
 
 #
-# Copyright 2009 Sun Microsystems, Inc.  All rights reserved.
-# Use is subject to license terms.
+# Copyright (c) 2008, 2010, Oracle and/or its affiliates. All rights reserved.
 #
 include ../Makefile.cmd
 
@@ -35,7 +34,8 @@
 SRCS =	auto_install.c \
 	auto_td.c \
 	auto_parse.c \
-	auto_parse_manifest.c
+	auto_parse_manifest.c \
+	auto_ddu_lib.c
 
 PROGS=		ai_get_manifest ai_sd
 
--- a/usr/src/cmd/auto-install/ai_manifest.rng	Fri May 07 11:46:54 2010 -0700
+++ b/usr/src/cmd/auto-install/ai_manifest.rng	Wed May 12 22:18:21 2010 -0700
@@ -19,8 +19,7 @@
 
 CDDL HEADER END
 
-Copyright (c) 2010, Oracle and/or its affiliates. All rights reserved.
-Use is subject to license terms.
+Copyright (c) 2008, 2010, Oracle and/or its affiliates. All rights reserved.
 -->
 
 <!--
@@ -53,6 +52,12 @@
         <define name="auto_installation_manifest">
                 <interleave>      <!-- Any element order is OK. -->
 			<optional>
+				<element name="ai_add_drivers">
+					<ref name="ai_add_drivers_contents"/>
+				</element>
+			</optional>
+
+			<optional>
                 		<element name="ai_target_device">
 					<ref name="ai_target_device_contents"/>
 				</element>
@@ -163,6 +168,70 @@
 
         <!--
         =======================================================================
+        Selections for AI Driver Update (add_drivers) specification
+        =======================================================================
+        -->
+	<define name="ai_add_drivers_contents">
+		<choice>
+			<oneOrMore>
+				<ref name="ai_bundle_contents"/>
+			</oneOrMore>
+			<group>
+				<zeroOrMore>
+					<ref name="ai_bundle_contents"/>
+				</zeroOrMore>
+				<ref name="ai_searchall_contents"/>
+			</group>
+		</choice>
+	</define>
+
+	<define name="ai_bundle_contents">
+		<element name="bundle">
+			<choice>
+				<attribute name="type">
+					<choice>
+						<value>P5I</value>
+						<value>DU</value>
+					</choice>
+				</attribute>
+				<group>
+					<attribute name="type">
+						<value>SVR4</value>
+					</attribute>
+					<attribute name="name"/>
+				</group>
+			</choice>
+			<attribute name="location"/>
+			<optional>
+				<attribute name="noinstall">
+					<!--
+					lower case true/false
+					-->
+					<data type="boolean"/>
+				</attribute>
+			</optional>
+		</element>
+	</define>
+
+	<define name="ai_searchall_contents">
+		<element name="searchall">
+			<optional>
+				<attribute name="publisher"/>
+				<attribute name="location"/>
+			</optional>
+			<optional>
+				<attribute name="addall">
+					<!--
+					lower case true/false
+					-->
+					<data type="boolean"/>
+				</attribute>
+			</optional>
+		</element>
+	</define>
+
+        <!--
+        =======================================================================
         Selections for AI target Device specification
 
 	Disk criteria are divided into two mutually exclusive groups:
--- a/usr/src/cmd/auto-install/ai_manifest.xml	Fri May 07 11:46:54 2010 -0700
+++ b/usr/src/cmd/auto-install/ai_manifest.xml	Wed May 12 22:18:21 2010 -0700
@@ -18,8 +18,7 @@
 
 CDDL HEADER END
 
-Copyright 2010 Sun Microsystems, Inc.  All rights reserved.
-Use is subject to license terms.
+Copyright (c) 2008, 2010, Oracle and/or its affiliates. All rights reserved.
 -->
 
 <!--
@@ -110,10 +109,14 @@
 		    partition instead of creating a new one.
 		-->
 		<!--
-		    <target_device_use_solaris_partition>true</target_device_use_solaris_partition>
+		    <target_device_use_solaris_partition>
+			true
+		    </target_device_use_solaris_partition>
 		-->
 
-		<target_device_install_slice_number>0</target_device_install_slice_number>
+		<target_device_install_slice_number>
+		    0
+		</target_device_install_slice_number>
 	</ai_target_device>
 	<ai_device_partitioning>
 		<partition_action>create</partition_action>
@@ -142,7 +145,8 @@
 		<slice_size>20480</slice_size>
 	</ai_device_vtoc_slices>
 	<ai_pkg_repo_default_publisher>
-		<main url="http://pkg.opensolaris.org/release" publisher="opensolaris.org"/>
+		<main url="http://pkg.opensolaris.org/release"
+		    publisher="opensolaris.org"/>
 		<mirror url=""/>
 	</ai_pkg_repo_default_publisher>
 
@@ -168,4 +172,40 @@
 	<ai_auto_reboot>
 		false
 	</ai_auto_reboot>
+        <ai_add_drivers>
+		<!--
+		bundles are specifications of packages needed in order to
+		perform the install.  types are as follows:
+
+		P5I: A pkg(5) P5I file, full path is in the "location" attr.
+		Path may be to a local file or an http or ftp specification.
+		<bundle type="P5I"
+		    location=
+		    "http://pkg.opensolaris.org/release/p5i/0/SUNW1394.p5i"/>
+
+		SVR4: An SVR4 package spec.  location corresponds to what
+		pkgadd -d would refer to.  Name refers to the package name.
+		<bundle type="SVR4" location="/export/package_dir"
+		    name="SUNW1394h"/>
+
+		DU: An ITU (Install Time Update) or Driver Update image.
+		location refers to the path just above the image's DU directory.
+		<bundle type="DU" location="/export/duimages/mydriver"
+		    name="mydriverDU"/>
+
+		A <searchall> entry performs a search for devices which are
+		missing their drivers.  It attempts to locate the correct
+		drivers via a database.  A repository publisher and location
+		may be specified, and that repository and its database will
+		be used.  If no publisher and location is specified, the
+		system's configured repositories will be used.
+		(See pkg publisher command.)  If <addall> is specified as
+		"true", then drivers the database says are third-party drivers
+		will be added like all others;  otherwise third-party drivers
+		will not be added.
+		    <searchall publisher="opensolaris.org"
+			location="http://ipkg.sfbay/dev" addall="true"/>
+		-->
+		<searchall/>
+	</ai_add_drivers>
 </ai_manifest>
--- a/usr/src/cmd/auto-install/ai_parse_manifest.py	Fri May 07 11:46:54 2010 -0700
+++ b/usr/src/cmd/auto-install/ai_parse_manifest.py	Wed May 12 22:18:21 2010 -0700
@@ -18,8 +18,7 @@
 #
 # CDDL HEADER END
 #
-# Copyright 2009 Sun Microsystems, Inc.  All rights reserved.
-# Use is subject to license terms.
+# Copyright (c) 2008, 2010, Oracle and/or its affiliates. All rights reserved.
 #
 """ ai_parse_manifest.py - AI XML manifest parser
 """
@@ -27,96 +26,71 @@
 from osol_install.ManifestServ import ManifestServ
 
 #~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
-def ai_verify_manifest_filename(manifest_file):
+def ai_create_manifestserv(manifest_file):
 #~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
     """
-	Verify that the specified manifest file is readable.
-	Returns
-	   0 - success
-	  -1 on error
-	"""
-    try:
-        file_name = open(manifest_file, "r")
-    except (IOError):
-        print "You have specified a file (%s) that is unable to " \
-            "be read." % manifest_file
-        return -1
-    file_name.close()
-    return 0
-	     
-#~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
-def ai_get_manifest_server_obj(manifest_file):
-#~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
-    """
-        Create manifest server object
+        Create an unvalidated manifest object.
+
+        Args:
+          manifest_file: file containing the data to read into memory.
+
         Returns
             ManifestServ object on success
-            -1 on error
+            None on error
+
+        Raises: None
     """
-      
-    err = ai_verify_manifest_filename(manifest_file)
-    if err != 0:
-        return -1
-    return  ManifestServ(manifest_file)
-		
+
+    try:
+        manifest_obj = ManifestServ(manifest_file, full_init=False)
+    except StandardError, err:
+        print "Error creating in-memory copy of Manifest data."
+        print str(err)
+        return None
+
+    return manifest_obj
+
 
 #~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
-def ai_start_manifest_server(manifest_server_obj):
-#~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
-    """
-        Opens communication socket to previously created
-        manifest server
-    """
-    
-    manifest_server_obj.start_socket_server()
-
-#~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
-def ai_stop_manifest_server(manifest_server_obj):
+def ai_setup_manifestserv(manifest_obj):
 #~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
     """
-        Closes communication socket to previously created
-        manifest server
-    """
-    
-    manifest_server_obj.stop_socket_server()
+        Validates a manifest server object
 
-#~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
-def ai_create_manifestserv(ai_manifest_file):
-#~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
-    """
-        Create manifest server object and opens communication socket to it
+        Args:
+	  manifest_server_obj: ManifestServ object containing data to validate.
+
+        Returns: 0 on success, -1 on error
+
+        Raises: None
     """
-    
+
     try:
-        # Create the object used to extract the data
-        manifest_server_obj = \
-            ai_get_manifest_server_obj(ai_manifest_file)
+        manifest_obj.schema_validate()
+        manifest_obj.semantic_validate()
+        return 0
 
-        # Start the socket server
-        ai_start_manifest_server(manifest_server_obj)
-    except StandardError:
-        return None
+    except StandardError, err:
+        print "Error setting up manifest data for use"
+        print str(err)
+        return -1
 
-    # return the socket object that was created
-    return (manifest_server_obj)
 
 #~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
 def ai_lookup_manifest_values(manifest_server_obj, path):
 #~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
-    """ Read the value of the path specified.
-	This returns only the first value of the list
+    """ Read and return all values of the path specified.
 
 	Args:
 	    manifest_server_obj: ManifestServ object
-	    path: path to read
+	    path: nodepath to find values for
 
 	Returns:
-	    the first value found (as a string) if there is at least
-	    one value to retrieve.
+            A list of strings found at the nodepath given.
 	    None: if no value is found
 
 	Raises:
-	    None
+	    ParserError: Error generated while parsing the nodepath
 	"""
 
     node_list = manifest_server_obj.get_values(path, False)
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/usr/src/cmd/auto-install/auto_ddu_lib.c	Wed May 12 22:18:21 2010 -0700
@@ -0,0 +1,1847 @@
+/*
+ * 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 (c) 2010, Oracle and/or its affiliates. All rights reserved.
+ */
+
+#include "Python.h"	/* Must be the first header file. */
+#include <alloca.h>
+#include <libintl.h>
+#include <strings.h>
+#include <auto_install.h>
+
+/* Python DDU function module related definitions. */
+#define	DDU_FUNCTION_MODULE	"DDU.ddu_function"
+#define	DDU_PACKAGE_LOOKUP	"ddu_package_lookup"
+#define	DDU_INSTALL_PACKAGE	"ddu_install_package"
+#define	DDU_DEVSCAN		"ddu_devscan"
+#define	DDU_BUILD_REPO_LIST	"ddu_build_repo_list"
+
+/* Python DDU package module related definitions. */
+#define	DDU_PACKAGE_MODULE	"DDU.ddu_package"
+#define	DDU_PACKAGE_OBJECT	"ddu_package_object"
+
+/* DDU error log */
+#define	DDU_ERRLOG		"/tmp/ddu_err.log"
+
+/* ICT module related definitions. */
+#define	ICT_MODULE		"osol_install.ict"
+#define	ICT_CLASS		"ICT"
+#define	ICT_UPDATE_ARCHIVE	"update_boot_archive"
+
+/* AI Manifest (AIM) related path definitions. */
+#define	AIM_PREFACE		"ai_manifest/ai_add_drivers/"
+#define	BUNDLE_NODEPATH		"bundle"
+#define	LOCN_NODEPATH		"bundle/location"
+#define	TYPE_NODEPATH		"bundle[location=\"%s\"]/type"
+#define	NAME_NODEPATH		"bundle[location=\"%s\":type=\"%s\"]/name"
+#define	NOINSTALL_NONAME_NODEPATH \
+				"bundle[location=\"%s\":type=\"%s\"]/noinstall"
+#define	NOINSTALL_YESNAME_NODEPATH \
+				"bundle[location=\"%s\":type=" \
+				    "\"%s\":name=\"%s\"]/noinstall"
+
+#define	SEARCH_NODEPATH		"searchall"
+#define	SEARCH_LOCN_NODEPATH	"searchall/location"
+#define	SEARCH_PUB_NODEPATH	"searchall/publisher"
+#define	SEARCH_ADDALL_NODEPATH	"searchall/addall"
+
+#define	MAX_NODEPATH_SIZE	256
+
+typedef struct {
+	PyThreadState *myThreadState;
+	PyThreadState *mainThreadState;
+	PyObject *pFunctionModule;
+	PyObject *pPackageModule;
+	PyObject *pICTModule;
+} py_state_t;
+
+typedef struct {
+	char path_str[MAX_NODEPATH_SIZE];
+	char *post_prefix_start;
+	int post_prefix_len;
+} path_t;
+
+static py_state_t *auto_ddu_lib_init();
+static void auto_ddu_lib_fini(py_state_t *py_state_p);
+static PyObject *ai_call_ddu_devscan(py_state_t *py_state_p,
+    boolean_t get_only_missing_drivers, char *dev_type);
+static PyObject *ai_call_ddu_package_lookup(py_state_t *py_state_p,
+    PyObject *pDevObj, PyObject *pRepoList);
+static int ai_call_ddu_install_package(py_state_t *py_state_p,
+    PyObject *ddu_package_obj, char *install_root, boolean_t third_party_ok);
+static PyObject *ai_new_ddu_package_object(py_state_t *py_state_p,
+    char *type, char *name, char *location);
+static int ai_get_ddu_package_object_values(PyObject *pDDUPackageObject,
+    char **type, char **location, char **name, char **descr, char **inf_link,
+    boolean_t *third_party);
+static int ai_get_ddu_dev_data_values(PyObject *pDDUDevData,
+    char **dev_type, char **descr);
+static void ai_du_process_manual_pkg(py_state_t *py_state_p,
+    PyObject *pPackageList, char *location, char *type, char *name,
+    char *noinstall);
+static void ai_du_process_manual_pkg_names(py_state_t *py_state_p,
+    PyObject *pPackageList, path_t *path_p, char *location, char *type,
+    char *name);
+static void ai_du_process_manual_pkg_types(py_state_t *py_state_p,
+    PyObject *pPackageList, path_t *path_p, char *location, char *type);
+static PyObject *ai_du_get_manual_pkg_list(py_state_t *py_state_p,
+    path_t *path_p);
+static PyObject *ai_du_get_searched_pkg_list(py_state_t *py_state_p,
+    path_t *path_p);
+static int ai_du_install_packages(py_state_t *py_state_p,
+    PyObject *pPkgTupleList, char *install_root, boolean_t honor_noinstall,
+    int *num_installed_pkgs_p);
+static char **ai_uniq_manifest_values(char **in, int *len_p);
+static int ai_du_call_update_archive_ict(py_state_t *py_state_p,
+    char *install_root);
+
+/*
+ * Stores the list of packages set up by ai_du_get_and_install() for use by
+ * ai_du_install().
+ */
+static PyObject *py_pkg_list;
+
+static char *empty_string = "";
+
+/* Private functions. */
+
+/*
+ * auto_ddu_lib_init:
+ * Initialize they python interpreter state so that python functions can be
+ * called from this module.  Initialize a few common things always used.
+ *
+ * Arguments: None
+ *
+ * Returns:
+ *   Success: A pointer to an initialized py_state_t object
+ *   Failure: NULL
+ *
+ * Note: Call auto_ddu_lib_fini(), passing it the item returned from this
+ * function, to undo the effects of this function.
+ */
+static py_state_t *
+auto_ddu_lib_init()
+{
+	PyObject *pName;
+	py_state_t *py_state_p = malloc(sizeof (py_state_t));
+
+	py_state_p->pFunctionModule = NULL;
+	py_state_p->pPackageModule = NULL;
+
+	/* Set up python interpreter state. */
+	PyEval_InitThreads();
+	py_state_p->mainThreadState = PyThreadState_Get();
+	py_state_p->myThreadState =
+	    PyThreadState_New(py_state_p->mainThreadState->interp);
+	PyThreadState_Swap(py_state_p->myThreadState);
+
+	/* Get names of modules for use by python/C interfaces. */
+	if ((pName = PyString_FromString(DDU_FUNCTION_MODULE)) != NULL) {
+		py_state_p->pFunctionModule = PyImport_Import(pName);
+		Py_DECREF(pName);
+	}
+	if ((pName = PyString_FromString(DDU_PACKAGE_MODULE)) != NULL) {
+		py_state_p->pPackageModule = PyImport_Import(pName);
+		Py_DECREF(pName);
+	}
+	if ((pName = PyString_FromString(ICT_MODULE)) != NULL) {
+		py_state_p->pICTModule = PyImport_Import(pName);
+		Py_DECREF(pName);
+	}
+
+	/* Cleanup and return NULL on error. */
+	if ((py_state_p->pFunctionModule == NULL) ||
+	    (py_state_p->pPackageModule == NULL) ||
+	    (py_state_p->pICTModule == NULL)) {
+		auto_debug_print(AUTO_DBGLVL_ERR, "auto_ddu_lib_init: "
+		    "error accessing DDU library or ICT modules.\n");
+		PyErr_Print();
+		auto_ddu_lib_fini(py_state_p);
+		py_state_p = NULL;
+	}
+
+	return (py_state_p);
+}
+
+/*
+ * auto_ddu_lib_fini:
+ * Undo initialization of python interpreter state set up by
+ * auto_ddu_lib_init().
+ *
+ * Arguments: A pointer to an initialized py_state_t object
+ *
+ * Returns: N/A
+ */
+static void
+auto_ddu_lib_fini(py_state_t *py_state_p)
+{
+	if (py_state_p == NULL) {
+		return;
+	}
+	Py_XDECREF(py_state_p->pFunctionModule);
+	Py_XDECREF(py_state_p->pPackageModule);
+	Py_XDECREF(py_state_p->pICTModule);
+	PyThreadState_Swap(py_state_p->mainThreadState);
+	PyThreadState_Clear(py_state_p->myThreadState);
+	PyThreadState_Delete(py_state_p->myThreadState);
+	free(py_state_p);
+}
+
+/*
+ * ai_call_ddu_build_repo_list:
+ * Call the DDU library ddu_build_repo_list function.  This sets up the
+ * list of repositories specified (as name/URL tuples) in the second argument,
+ * and returns a python list of ddu_repo_objects for use by ddu_package_lookup.
+ *
+ * Arguments:
+ *   py_state: Initialized py_state_t object.
+ *   pRepoTypleList: List of (pubname, URL) tuples, each tuple representing
+ *	a repository.
+ *
+ * Returns:
+ *   Success: A python object representing a list of ddu_repo_objects.
+ *   Failure: NULL
+ */
+static PyObject *
+ai_call_ddu_build_repo_list(py_state_t *py_state_p, PyObject *pRepoTupleList)
+{
+	PyObject *pRet = NULL;
+
+	/* Find the function */
+	PyObject *pFunc = PyObject_GetAttrString(py_state_p->pFunctionModule,
+	    DDU_BUILD_REPO_LIST);
+
+	if ((pFunc == NULL) || (!PyCallable_Check(pFunc))) {
+		auto_debug_print(AUTO_DBGLVL_ERR,
+		    "Function not callable: %s\n", DDU_BUILD_REPO_LIST);
+	} else {
+		PyObject *pArgs = PyTuple_New(1);
+
+		/*
+		 * INCREF here since PyTuple_SetItem steals the reference, and
+		 * can decrement the refcount when pArgs is DECREFed.
+		 */
+		Py_INCREF(pRepoTupleList);
+
+		/* Set up args to python function and call it. */
+		PyTuple_SetItem(pArgs, 0, pRepoTupleList);
+		pRet = PyObject_CallObject(pFunc, pArgs);
+		Py_DECREF(pArgs);
+
+		if ((PyErr_Occurred()) || (pRet == NULL) || (pRet == Py_None)) {
+			auto_debug_dump_file(AUTO_DBGLVL_ERR, DDU_ERRLOG);
+			auto_debug_print(AUTO_DBGLVL_ERR,
+			    "%s returned an error.\n", DDU_BUILD_REPO_LIST);
+			if (PyErr_Occurred()) {
+				PyErr_Print();
+			}
+			Py_XDECREF(pRet);
+			pRet = NULL;
+		}
+	}
+
+	Py_XDECREF(pFunc);
+	return (pRet);
+}
+
+/*
+ * ai_call_ddu_devscan:
+ * Call the DDU library ddu_devscan function.  This function performs a device
+ * scan on the system, to find out which devices are missing drivers.
+ *
+ * Arguments:
+ *   py_state_p: Initialized py_state_t object.
+ *   get_only_missing_drivers: Boolean: when true, return only the list of
+ *	devices which are missing drivers.  When false, return all devices.
+ *   dev_type: Type of devices to scan for.  See the DDU library ddu_devscan()
+ *	function for the list of device types.  "all" is an acceptable device
+ *	type.
+ *
+ * Returns:
+ *   Success:
+ *	A python object representing a list of ddu_dev_data objects is returned.
+ *	- NOTE: if no devices are missing drivers and get_only_missing_drivers
+ *	  is true, then an empty list is returned.
+ *	- A ddu_dev_data object represents a found device.
+ *   Failure:
+ *	NULL
+ */
+static PyObject *
+ai_call_ddu_devscan(py_state_t *py_state_p,
+    boolean_t get_only_missing_drivers, char *dev_type)
+{
+	PyObject *pRet = NULL;
+
+	/* Find the function */
+	PyObject *pFunc = PyObject_GetAttrString(py_state_p->pFunctionModule,
+	    DDU_DEVSCAN);
+
+	if ((pFunc == NULL) || (!PyCallable_Check(pFunc))) {
+		auto_debug_print(AUTO_DBGLVL_ERR,
+		    "Function not callable: %s\n", DDU_DEVSCAN);
+	} else {
+		/* Set up args to python function and call it. */
+		PyObject *pArgs = PyTuple_New(2);
+		PyTuple_SetItem(pArgs, 0,
+		    PyBool_FromLong((long)get_only_missing_drivers));
+		PyTuple_SetItem(pArgs, 1, PyString_FromString(dev_type));
+		pRet = PyObject_CallObject(pFunc, pArgs);
+
+		Py_DECREF(pArgs);
+		if ((PyErr_Occurred()) || (pRet == NULL) || (pRet == Py_None)) {
+			auto_debug_dump_file(AUTO_DBGLVL_ERR, DDU_ERRLOG);
+			auto_debug_print(AUTO_DBGLVL_ERR,
+			    "%s returned an error.\n", DDU_DEVSCAN);
+			if (PyErr_Occurred()) {
+				PyErr_Print();
+			}
+			Py_XDECREF(pRet);
+			pRet = NULL;
+		}
+	}
+
+	Py_XDECREF(pFunc);
+	return (pRet);
+}
+
+/*
+ * ai_call_ddu_package_lookup:
+ * Call the DDU library ddu_package_lookup function.  Given a list of
+ * repositories, this function attempts to find a pkg(5) package in one of the
+ * repositories.
+ *
+ * Arguments:
+ *   py_state_p: Initialized py_state_t object.
+ *   pDevObj: A python ddu_dev_data object representing a device.
+ *   pRepoList: A python list of ddu_repo_object objects.  This represents the
+ *	list of repositories to search through for a driver package.
+ *
+ * Returns:
+ *   Success: A python ddu_package_object representing a package to install for
+ *	the given device.
+ *   Failure: NULL
+ */
+static PyObject *
+ai_call_ddu_package_lookup(py_state_t *py_state_p,
+    PyObject *pDevObj, PyObject *pRepoList)
+{
+	PyObject *pRet = NULL;
+
+	/* Find the function */
+	PyObject *pFunc = PyObject_GetAttrString(py_state_p->pFunctionModule,
+	    DDU_PACKAGE_LOOKUP);
+
+	if ((pFunc == NULL) || (!PyCallable_Check(pFunc))) {
+		auto_debug_print(AUTO_DBGLVL_ERR,
+		    "Function not callable: %s\n", DDU_PACKAGE_LOOKUP);
+	} else {
+		/* Set up args to python function. */
+		PyObject *pArgs = PyTuple_New(2);
+		Py_INCREF(pDevObj);	/* PyTuple_SetItem steals reference. */
+		Py_INCREF(pRepoList);	/* PyTuple_SetItem steals reference. */
+		PyTuple_SetItem(pArgs, 0, pDevObj);
+		PyTuple_SetItem(pArgs, 1, pRepoList);
+
+		/* Call ddu_package_lookup() */
+		pRet = PyObject_CallObject(pFunc, pArgs);
+		Py_DECREF(pArgs);
+		if ((PyErr_Occurred()) || (pRet == NULL) || (pRet == Py_None)) {
+			auto_debug_dump_file(AUTO_DBGLVL_ERR, DDU_ERRLOG);
+			auto_debug_print(AUTO_DBGLVL_ERR,
+			    "%s returned an error.\n", DDU_PACKAGE_LOOKUP);
+			if (PyErr_Occurred()) {
+				PyErr_Print();
+			}
+			Py_XDECREF(pRet);
+			pRet = NULL;
+		}
+	}
+
+	Py_XDECREF(pFunc);
+	return (pRet);
+}
+
+/*
+ * ai_call_ddu_install_package:
+ * Call the DDU library ddu_install_package function.  Install the package
+ * represented by pDDUPackageObj under the tree or file system given by
+ * install_root.
+ *
+ * Arguments:
+ *   py_state_p: Initialized py_state_t object.
+ *   pDDUPackageObj: A python ddu_package_object representing the package to
+ *	install.
+ *   install_root: The root of the directory tree or file system in which to
+ *	install the package.
+ *   third_party_ok: Boolean: When true, it is OK to download and install
+ *	packages found at third-party websites (as opposed to pkg(5)
+ *	repositories).
+ *
+ * Returns:
+ *   AUTO_INSTALL_SUCCESS: Package was successfully installed.
+ *   AUTO_INSTALL_FAILURE: Package was not successfully installed.
+ *
+ * NOTE: check installer logfile for details of the failure.
+ */
+static int
+ai_call_ddu_install_package(py_state_t *py_state_p,
+    PyObject *pDDUPackageObj, char *install_root, boolean_t third_party_ok)
+{
+	int rval = AUTO_INSTALL_SUCCESS;
+
+	/* Find the function */
+	PyObject *pFunc = PyObject_GetAttrString(py_state_p->pFunctionModule,
+	    DDU_INSTALL_PACKAGE);
+
+	if ((pFunc == NULL) || (!PyCallable_Check(pFunc))) {
+		auto_debug_print(AUTO_DBGLVL_ERR,
+		    "Function not callable: %s\n", DDU_INSTALL_PACKAGE);
+		rval = AUTO_INSTALL_FAILURE;
+
+	} else {
+
+		/* Set up args to python function. */
+		PyObject *pArgs = PyTuple_New(3);
+		Py_INCREF(pDDUPackageObj);	/* PyTuple_SetItem steals ref */
+		PyTuple_SetItem(pArgs, 0, pDDUPackageObj);
+		PyTuple_SetItem(pArgs, 1, PyString_FromString(install_root));
+		PyTuple_SetItem(pArgs, 2,
+		    PyBool_FromLong((long)third_party_ok));
+
+		/* Call ddu_install_packages() */
+		PyObject_CallObject(pFunc, pArgs);
+		Py_DECREF(pArgs);
+
+		if (PyErr_Occurred()) {
+			auto_debug_dump_file(AUTO_DBGLVL_ERR, DDU_ERRLOG);
+			auto_debug_print(AUTO_DBGLVL_ERR,
+			    "%s returned an error\n", DDU_INSTALL_PACKAGE);
+			PyErr_Print();
+			rval = AUTO_INSTALL_FAILURE;
+		}
+	}
+
+	Py_XDECREF(pFunc);
+	return (rval);
+}
+
+/*
+ * ai_new_ddu_package_object:
+ * Create a new ddu_package_object of given type, name and location.
+ *
+ * Arguments:
+ *   py_state_p: Initialized py_state_t object.
+ *   type: type of package.
+ *   name: name of package. (Not used by all types of packages.)
+ *   location: directory of where package is located.
+ *
+ * Returns:
+ *   Success: A new python ddu_package_object object of the given
+ *	type/name/location.
+ *   Failure: NULL
+ */
+static PyObject *
+ai_new_ddu_package_object(py_state_t *py_state_p,
+    char *type, char *name, char *location)
+/*
+ * Construct and return a new python ddu_package_object based on arguments.
+ * Assumes auto_ddu_lib_init() has been called.
+ *
+ * Success: Returns the new object.
+ * Failure: NULL
+ */
+{
+	PyObject *pRet = NULL;
+
+	/* Find the function */
+	PyObject *pFunc = PyObject_GetAttrString(py_state_p->pPackageModule,
+	    DDU_PACKAGE_OBJECT);
+
+	if ((pFunc == NULL) || (!PyCallable_Check(pFunc))) {
+		auto_debug_print(AUTO_DBGLVL_ERR,
+		    "ddu_package_object constructor not callable\n");
+	} else {
+		/* Set up args to python function. */
+		PyObject *pArgs = PyTuple_New(3);
+		PyTuple_SetItem(pArgs, 0, PyString_FromString(type));
+		PyTuple_SetItem(pArgs, 1, PyString_FromString(name));
+		PyTuple_SetItem(pArgs, 2, PyString_FromString(location));
+
+		/* Call ddu_package_object constructor. */
+		pRet = PyObject_CallObject(pFunc, pArgs);
+		Py_DECREF(pArgs);
+		if ((PyErr_Occurred()) || (pRet == NULL) || (pRet == Py_None)) {
+			auto_debug_dump_file(AUTO_DBGLVL_ERR, DDU_ERRLOG);
+			auto_debug_print(AUTO_DBGLVL_ERR,
+			    "ddu_package_object constructor failed\n");
+			if (PyErr_Occurred()) {
+				PyErr_Print();
+			}
+			Py_XDECREF(pRet);
+			pRet = NULL;
+		}
+	}
+
+	Py_XDECREF(pFunc);
+	return (pRet);
+}
+
+/*
+ * ai_get_ddu_package_object_values:
+ * Return selected values from a given ddu_package_object.
+ *
+ * Values returned live on the python interpreter's heap, and should not be
+ * modified or freed.  They live only as long as the python obj they come from.
+ *
+ * Note: implementation is tied to the fields of the python object.
+ *
+ * Arguments:
+ *   pDDUPackageObject: Object to extract values from.  Assumed to be a
+ *	ddu_package_object;  not verified.
+ *   type: char string pointer returned filled in with "pkg_type" field.
+ *	Not processed if NULL.
+ *   location: char string pointer returned filled in with "pkg_location" field.
+ *	Not processed if NULL.
+ *   name: char string pointer returned filled in with "pkg_name" field.
+ *	Not processed if NULL.
+ *   descr: char string pointer returned filled in with "device_descriptor"
+ *	field.  Not processed if NULL.
+ *   inf_link: char string pointer returned filled in with "inf_link" field.
+ *	Not processed if NULL.
+ *   third_party: boolean pointer returned filled in with
+ *	"third_party_from_search" field.  Not processed if NULL.
+ *
+ * Returns:
+ *   AUTO_INSTALL_SUCCESS: when all requested fields are found and extracted.
+ *   AUTO_INSTALL_FAILURE: when one or more fields could not be found or
+ *	extracted.
+ */
+static int
+ai_get_ddu_package_object_values(PyObject *pDDUPackageObject,
+    char **type, char **location, char **name, char **descr, char **inf_link,
+    boolean_t *third_party)
+{
+	PyObject *pValue;
+
+	if (type != NULL) {
+		pValue = PyObject_GetAttrString(pDDUPackageObject, "pkg_type");
+		if (pValue == NULL) {
+			auto_debug_print(AUTO_DBGLVL_ERR,
+			    "ai_get_ddu_package_object_values: "
+			    "no ddu_package_object pkg_type field.\n");
+			return (AUTO_INSTALL_FAILURE);
+		}
+		*type = PyString_AsString(pValue);
+	}
+
+	if (location != NULL) {
+		pValue = PyObject_GetAttrString(pDDUPackageObject,
+		    "pkg_location");
+		if (pValue == NULL) {
+			auto_debug_print(AUTO_DBGLVL_ERR,
+			    "ai_get_ddu_package_object_values: "
+			    "no ddu_package_object pkg_location field.\n");
+			return (AUTO_INSTALL_FAILURE);
+		}
+		*location = PyString_AsString(pValue);
+	}
+
+	if (name != NULL) {
+		pValue = PyObject_GetAttrString(pDDUPackageObject, "pkg_name");
+		if (pValue == NULL) {
+			auto_debug_print(AUTO_DBGLVL_ERR,
+			    "ai_get_ddu_package_object_values: "
+			    "no ddu_package_object pkg_name field.\n");
+			return (AUTO_INSTALL_FAILURE);
+		}
+		*name = PyString_AsString(pValue);
+	}
+
+	if (descr != NULL) {
+		pValue = PyObject_GetAttrString(pDDUPackageObject,
+		    "device_descriptor");
+		if (pValue == NULL) {
+			auto_debug_print(AUTO_DBGLVL_ERR,
+			    "ai_get_ddu_package_object_values: "
+			    "no ddu_package_object device_descriptor field.\n");
+			return (AUTO_INSTALL_FAILURE);
+		}
+		*descr = PyString_AsString(pValue);
+	}
+
+	if (inf_link != NULL) {
+		pValue = PyObject_GetAttrString(pDDUPackageObject, "inf_link");
+		if (pValue == NULL) {
+			auto_debug_print(AUTO_DBGLVL_ERR,
+			    "ai_get_ddu_package_object_values: "
+			    "no ddu_package_object inf_link field.\n");
+			return (AUTO_INSTALL_FAILURE);
+		}
+		*inf_link = PyString_AsString(pValue);
+	}
+
+	if (third_party != NULL) {
+		pValue = PyObject_GetAttrString(pDDUPackageObject,
+		    "third_party_from_search");
+		if (pValue == NULL) {
+			auto_debug_print(AUTO_DBGLVL_ERR,
+			    "ai_get_ddu_package_object_values: "
+			    "no ddu_package_object "
+			    "third_party_from_search field.\n");
+			return (AUTO_INSTALL_FAILURE);
+		}
+		*third_party = (PyObject_IsTrue(pValue));
+	}
+
+	return (AUTO_INSTALL_SUCCESS);
+}
+
+/*
+ * ai_get_ddu_dev_data_values:
+ * Return selected values from a given ddu_dev_data object.
+ *
+ * Values returned live on the python interpreter's heap, and should not be
+ * modified or freed.  They live only as long as the python obj they come from.
+ *
+ * Note: implementation is tied to the fields of the python object.
+ *
+ * Arguments:
+ *   pDDUDevData: Object to extract values from.  Assumed to be a
+ *	ddu_dev_data object;  not verified.
+ *   dev_type: char string pointer returned filled in with "device_type" field.
+ *	Not processed if NULL.
+ *   descr: char string pointer returned filled in with "description" field.
+ *	Not processed if NULL.
+ *
+ * Returns:
+ *   AUTO_INSTALL_SUCCESS: when all requested fields are found and extracted.
+ *   AUTO_INSTALL_FAILURE: when one or more fields could not be found or
+ *	extracted.
+ */
+static int
+ai_get_ddu_dev_data_values(PyObject *pDDUDevData, char **dev_type, char **descr)
+{
+	PyObject *pValue;
+
+	if (dev_type != NULL) {
+		pValue = PyObject_GetAttrString(pDDUDevData, "device_type");
+		if (pValue == NULL) {
+			auto_debug_print(AUTO_DBGLVL_ERR,
+			    "ai_get_ddu_dev_data_values: "
+			    "no ddu_dev_data device_type field.\n");
+			return (AUTO_INSTALL_FAILURE);
+		}
+		*dev_type = PyString_AsString(pValue);
+	}
+
+	if (descr != NULL) {
+		pValue = PyObject_GetAttrString(pDDUDevData, "description");
+		if (pValue == NULL) {
+			auto_debug_print(AUTO_DBGLVL_ERR,
+			    "ai_get_ddu_dev_data_values: "
+			    "no ddu_dev_data description field.\n");
+			return (AUTO_INSTALL_FAILURE);
+		}
+		*descr = PyString_AsString(pValue);
+	}
+
+	return (AUTO_INSTALL_SUCCESS);
+}
+
+/*
+ * ai_du_process_manual_pkg:
+ * Create a ddu_package_object from parameters, and add it to the pPackageList.
+ *
+ * Arguments:
+ *   py_state_p: Initialized py_state_t object.
+ *   pPackageList: List of packages to append the new ddu_package_object to.
+ *   location: directory of where package is located.
+ *   type: type of package.
+ *   name: name of package.
+ *   noinstall: boolean whether package is to be installed only to booted
+ *	environment, not to target.
+ *
+ * Returns: None
+ *   Note 1: the pPackageList will be modified.
+ *   Note 2: Appropriate error messages are logged/displayed.
+ */
+static void
+ai_du_process_manual_pkg(py_state_t *py_state_p, PyObject *pPackageList,
+    char *location, char *type, char *name, char *noinstall)
+{
+	PyObject *pDDUPackageObject;
+	PyObject *pTuple;
+
+	auto_log_print(gettext("Found package listed in manifest:\n"));
+	if (name != empty_string) {
+		auto_log_print(gettext("  type:%s, location:%s, name:%s\n"),
+		    type, location, name);
+	} else {
+		auto_log_print(gettext("  type:%s, location:%s\n"),
+		    type, location);
+	}
+	if (strcmp(noinstall, "true") == 0) {
+		auto_log_print(gettext("    Package to be "
+		    "installed only in current booted environment.\n"));
+	}
+
+	/* Initialize a new ddu_package_object object */
+	pDDUPackageObject = ai_new_ddu_package_object(py_state_p,
+	    type, name, location);
+
+	if (pDDUPackageObject == NULL) {
+		auto_debug_print(AUTO_DBGLVL_ERR,
+		    "ai_du_get_pkg_list: <ai_add_drivers> error:\n"
+		    "Error creating new package object for "
+		    "location %s %s\n", location, name);
+		return;
+	}
+
+	pTuple = PyTuple_New(3);
+	PyTuple_SetItem(pTuple, 0, pDDUPackageObject);
+	PyTuple_SetItem(pTuple, 1, Py_True);	/* third party OK */
+	PyTuple_SetItem(pTuple, 2,
+	    (strcmp(noinstall, "true") == 0) ? Py_True : Py_False);
+
+	/*
+	 * NOTE: Don't decref pTuple here as PyList_Append doesn't
+	 * steal a reference to it.
+	 */
+	PyList_Append(pPackageList, pTuple);
+}
+
+/*
+ * ai_du_process_manual_pkg_names:
+ * Do any processing of packages for which unique location, type and name are
+ *	known.
+ *
+ * Arguments:
+ *   py_state_p: Initialized py_state_t object.
+ *   pPackageList: List of packages to append the new ddu_package_object to.
+ *   location: directory of where package is located.
+ *   type: type of package.
+ *   name: name of package.
+ *
+ * Returns: None
+ *   Note 1: the pPackageList will be modified.
+ *   Note 2: Appropriate error messages are logged/displayed.
+ */
+static void
+ai_du_process_manual_pkg_names(py_state_t *py_state_p, PyObject *pPackageList,
+    path_t *path_p, char *location, char *type, char *name)
+{
+	char **noinstalls;
+	int noinstlen;
+	char *nodespec;
+
+	/* Process "noinstall" entries. */
+
+	/* Search is different depending on whether a name is specified. */
+	if (name == empty_string) {
+		nodespec = NOINSTALL_NONAME_NODEPATH;
+	} else {
+		nodespec = NOINSTALL_YESNAME_NODEPATH;
+	}
+
+	if (snprintf(path_p->post_prefix_start, path_p->post_prefix_len,
+	    nodespec, location, type, name) >= path_p->post_prefix_len) {
+		auto_debug_print(AUTO_DBGLVL_ERR, "ai_du_get_pkg_list: "
+		    "<ai_add_drivers> manifest error:\n"
+		    "noinstall path buffer overflow for location \"%s\", "
+		    "type \"%s\", name \"%s\"\n", location, type, name);
+		return;
+	}
+
+	noinstalls = ai_get_manifest_values(path_p->path_str, &noinstlen);
+
+	/* Note: this does not have to be filled in for any location. */
+	if (noinstlen <= 0) {
+		/* Obj pointed to by pPackageList will be modified. */
+		ai_du_process_manual_pkg(py_state_p, pPackageList, location,
+		    type, name, empty_string);
+
+	} else if (noinstlen > 1) {
+		auto_log_print(gettext("ai_du_get_pkg_list: "
+		    "<ai_add_drivers> manifest error:\n"
+		    "multiple noinstall values for location \"%s\", "
+		    "type \"%s\", name \"%s\"\n"), location, type, name);
+		return;
+
+	} else if ((strcmp(noinstalls[0], "true") != 0) &&
+	    (strcmp(noinstalls[0], "false") != 0)) {
+		auto_log_print(gettext("ai_du_get_pkg_list: "
+		    "<ai_add_drivers> manifest error:\n"
+		    "non-boolean noinstall value for location \"%s\", "
+		    "type \"%s\", name \"%s\"\n"), location, type, name);
+		return;
+
+	} else {
+		/* Obj pointed to by pPackageList will be modified. */
+		ai_du_process_manual_pkg(py_state_p, pPackageList, location,
+		    type, name, noinstalls[0]);
+	}
+	ai_free_manifest_values(noinstalls);
+}
+
+/*
+ * ai_du_process_manual_pkg_types:
+ * Do any processing of packages for which unique location and type are known.
+ *
+ * Arguments:
+ *   py_state_p: Initialized py_state_t object.
+ *   pPackageList: List of packages to append the new ddu_package_object to.
+ *   location: directory of where package is located.
+ *   type: type of package.
+ *
+ * Returns: None
+ *   Note 1: the pPackageList will be modified.
+ *   Note 2: Appropriate error messages are logged/displayed.
+ */
+static void
+ai_du_process_manual_pkg_types(py_state_t *py_state_p, PyObject *pPackageList,
+    path_t *path_p, char *location, char *type)
+{
+	char **names;
+	char **uniq_names;
+	int namelen;
+	int k;
+
+	if ((strcmp(type, "P5I") != 0) &&
+	    (strcmp(type, "SVR4") != 0) &&
+	    (strcmp(type, "DU") != 0)) {
+		auto_log_print(gettext("ai_du_get_pkg_list: "
+		    "<ai_add_drivers> manifest error:\n"
+		    "invalid type %s given for location %s\n"),
+		    type, location);
+		return;
+	}
+
+	/* Get all names assocated with type and location. */
+
+	if (snprintf(path_p->post_prefix_start, path_p->post_prefix_len,
+	    NAME_NODEPATH, location, type) >= path_p->post_prefix_len) {
+		auto_debug_print(AUTO_DBGLVL_ERR, "ai_du_get_pkg_list: "
+		    "<ai_add_drivers> manifest error:\n"
+		    "name path buffer overflow for location "
+		    "%s, type %s\n", location, type);
+		return;
+	}
+
+	names = ai_get_manifest_values(path_p->path_str, &namelen);
+	uniq_names = ai_uniq_manifest_values(names, &namelen);
+	ai_free_manifest_values(names);
+	names = uniq_names;
+
+	/* P5I and DU type entries don't have a "name" entry. */
+	if (strcmp(type, "SVR4") != 0) {
+		if (namelen > 0) {
+			auto_log_print(gettext(
+			    "ai_du_get_pkg_list: <ai_add_drivers> "
+			    "manifest error:\n"
+			    "name given to P5I or DU bundle at "
+			    "location %s\n"), location);
+			return;
+		} else {
+			/* Obj pointed to by pPackageList will be modified. */
+			ai_du_process_manual_pkg_names(py_state_p, pPackageList,
+			    path_p, location, type, empty_string);
+		}
+
+	/* There must be at least one "name" entry per bundle for SVR4 type. */
+	} else if (namelen <= 0) {
+		auto_log_print(gettext("ai_du_get_pkg_list: "
+		    "<ai_add_drivers> manifest error:\n"
+		    "no name given for SVR4 bundle at location %s, type %s\n"),
+		    location, type);
+		return;
+
+	} else {
+		/* Process each location/type/name entry. */
+		for (k = 0; k < namelen; k++) {
+
+			/* Obj pointed to by pPackageList will be modified. */
+			ai_du_process_manual_pkg_names(py_state_p, pPackageList,
+			    path_p, location, type, names[k]);
+		}
+
+	}
+	ai_free_manifest_values(names);
+}
+
+/*
+ * ai_du_get_manual_pkg_list:
+ * Read the AI ai_manifest.xml file and process the <bundles> under the
+ * <ai_add_drivers> section.  A <bundle> is a manual specification of a package
+ * to install.  Do error checking of the manifest as necessary, as this function
+ * reads the manifest before it is validated against a schema.
+ *
+ * Validates syntax and processes the following from the manifest:
+ *	<ai_add_drivers>
+ *		<bundle type="type" location="location" name="name"
+ *		    noinstall="true or false" />
+ *	</ai_add_drivers>
+ *
+ *	type can be "SVR4", "P5I" or "DU".
+ *	name not required if type is "P5I"
+ *
+ * Arguments:
+ *   py_state_p: Initialized py_state_t object.
+ *   path_p: Used to build nodepath strings for manifest checking.
+ *
+ * Returns:
+ *   Success: A python list of (ddu_package_object, third_party_ok, noinstall)
+ *	tuples suitable for input to ai_du_install_packages().
+ *	NOTE: if the manifest shows no packages to install for Driver Update,
+ *	this function will return an empty list.
+ *   Failure: NULL
+ *
+ * NOTE: check installer logfile for details of the failure.
+ */
+static PyObject *
+ai_du_get_manual_pkg_list(py_state_t *py_state_p, path_t *path_p)
+{
+	PyObject *pPackageList = NULL;
+	char **uniq_locns = NULL;
+	char **types = NULL;
+	int locnlen, typelen;
+	char **locations;
+	char **uniq_types;
+	int num_bundles;
+	int i, j;
+
+	/* Read manifest for specific package requests. */
+
+	/* Get the number of bundle entries. */
+	if (strlcpy(path_p->post_prefix_start, BUNDLE_NODEPATH,
+	    path_p->post_prefix_len) > path_p->post_prefix_len) {
+		auto_debug_print(AUTO_DBGLVL_ERR,
+		    "ai_du_get_pkg_list: bundle path buffer overflow\n");
+		return (NULL);
+	}
+
+	/* Use "locations" like a dummy here.  Interest only in num_bundles. */
+	locations = ai_get_manifest_values(path_p->path_str, &num_bundles);
+	ai_free_manifest_values(locations);
+
+	/* No bundles.  Return an empty list. */
+	if (num_bundles <= 0) {
+		return (PyList_New(0));
+	}
+
+	/* Retrieve a list of all specific package request locations. */
+	if (strlcpy(path_p->post_prefix_start, LOCN_NODEPATH,
+	    path_p->post_prefix_len) > path_p->post_prefix_len) {
+		auto_debug_print(AUTO_DBGLVL_ERR,
+		    "ai_du_get_pkg_list: location path buffer overflow\n");
+		return (NULL);
+	}
+
+	/* Get real locations list here for use below. */
+	locations = ai_get_manifest_values(path_p->path_str, &locnlen);
+
+	/*
+	 * Not a perfect test to validate bundles and locations in manifest,
+	 * but it will do...
+	 */
+	if (locnlen != num_bundles) {
+		auto_debug_print(AUTO_DBGLVL_ERR,
+		    "ai_du_get_pkg_list: <ai_add_drivers> manifest error:\n"
+		    "There is not a 1-1 location-bundle mapping.\n");
+		return (NULL);
+	}
+
+	uniq_locns = ai_uniq_manifest_values(locations, &locnlen);
+	ai_free_manifest_values(locations);
+	locations = uniq_locns;
+
+	/*
+	 * Initialize a zero-length list.
+	 * This will be returned empty if nothing to install has been found.
+	 */
+	pPackageList = PyList_New(0);
+
+	/*
+	 * For each location, get types.  Note it is possible for there to be
+	 * more than one type at a location.  There can also be more than one
+	 * item of a given type at a location.
+	 */
+	for (i = 0; i < locnlen; i++) {
+
+		/* Process "type" entries. */
+
+		if (snprintf(path_p->post_prefix_start, path_p->post_prefix_len,
+		    TYPE_NODEPATH, locations[i]) >= path_p->post_prefix_len) {
+			auto_debug_print(AUTO_DBGLVL_ERR,
+			    "ai_du_get_pkg_list: "
+			    "<ai_add_drivers> manifest error:\n"
+			    "type path buffer overflow for location %s\n",
+			    locations[i]);
+			continue;
+		}
+
+		types = ai_get_manifest_values(path_p->path_str, &typelen);
+		if (typelen <= 0) {
+			auto_log_print(gettext("ai_du_get_pkg_list: "
+			    "<ai_add_drivers> manifest error:\n"
+			    "no type given for location %s\n"), locations[i]);
+			continue;
+		}
+
+		uniq_types = ai_uniq_manifest_values(types, &typelen);
+		ai_free_manifest_values(types);
+		types = uniq_types;
+
+		/* Loop for all types found at this location... */
+		for (j = 0; j < typelen; j++) {
+
+			/* Obj pointed to by pPackageList will be modified. */
+			ai_du_process_manual_pkg_types(py_state_p, pPackageList,
+			    path_p, locations[i], types[j]);
+		}
+	}
+	ai_free_manifest_values(locations);
+	ai_free_manifest_values(types);
+	return (pPackageList);
+}
+
+/*
+ * ai_du_get_searched_pkg_list:
+ * Read the AI ai_manifest.xml file and process the <searchall> tag under the
+ * <ai_add_drivers> section.  Do the needful to scan for missing devices and to
+ * perform package searches and installs for missing drivers.  Do error
+ * checking of the manifest as necessary, as this function reads the manifest
+ * before it is validated against a schema.
+ *
+ * Validates syntax and processes the following from the manifest:
+ *	<ai_add_drivers>
+ *		<searchall publisher="pub" location="location"
+ *		    addall="true or false"/>
+ *	</ai_add_drivers>
+ *
+ *	publisher and location are both optional, but both must be specified
+ *	    together.
+ *	addall is optional.  Defaults to "false" if not specified.
+ *
+ * Arguments:
+ *   py_state_p: Initialized py_state_t object.
+ *   path_p: Used to build nodepath strings for manifest checking.
+ *
+ * Returns:
+ *   Success: A python list of (ddu_package_object, third_party_ok, noinstall)
+ *	tuples suitable for input to ai_du_install_packages().
+ *	NOTE: if the system is missing no drivers, this function will return
+ *	an empty list.
+ *   Failure: NULL
+ *
+ * NOTE: check installer logfile for details of the failure.
+ */
+static PyObject *
+ai_du_get_searched_pkg_list(py_state_t *py_state_p, path_t *path_p)
+{
+	PyObject *pPackageList = NULL;
+	PyObject *pDeviceList = NULL;
+	PyObject *pTuple;
+	PyObject *pRepoTupleList;
+	int len, sublen;
+	PyObject *pSearchRepoList = NULL;
+	char *search_locn, *search_pub;
+	PyObject *py_search_addall = NULL;
+	char **searches = NULL;
+	char **search_locns = NULL;
+	char **search_pubs = NULL;
+	char **search_addalls = NULL;
+	Py_ssize_t i, listlen;
+	PyObject *pRval = NULL;
+
+	pPackageList = PyList_New(0); /* Initialize a zero-length list. */
+
+	/* Read manifest for search requests. */
+
+	if (strlcpy(path_p->post_prefix_start, SEARCH_NODEPATH,
+	    path_p->post_prefix_len) > path_p->post_prefix_len) {
+		auto_debug_print(AUTO_DBGLVL_ERR,
+		    "ai_du_get_searched_pkg_list: "
+		    "search pathname buffer overflow.\n");
+		return (NULL);
+	}
+
+	searches = ai_get_manifest_values(path_p->path_str, &len);
+	ai_free_manifest_values(searches);
+	if (len > 1) {
+		auto_log_print(gettext("ai_du_get_searched_pkg_list: "
+		    "too many <searchall> entries in manifest\n"));
+		return (NULL);
+
+	} else if (len <= 0) {
+		return (pPackageList);
+	}
+
+	auto_log_print(gettext("ai_du_get_searched_pkg_list: Doing a device "
+	    "scan for devices which are missing drivers...\n"));
+
+	/*
+	 * Call ddu_devscan() to do search if requested.
+	 * The boolean value is to scan only for missing drivers.
+	 */
+	pDeviceList = ai_call_ddu_devscan(py_state_p, B_TRUE, "all");
+	if (pDeviceList == NULL) {
+		auto_debug_print(AUTO_DBGLVL_ERR,
+		    "ai_du_get_searched_pkg_list: "
+		    "Error scanning for missing devices.\n");
+		return (NULL);
+
+	/* An empty list is perfectly acceptable here.  No missing drivers. */
+	} else if (PyList_Size(pDeviceList) == 0) {
+		return (pPackageList);
+	}
+
+	/* Get repo location, if specified. */
+
+	if (strlcpy(path_p->post_prefix_start, SEARCH_LOCN_NODEPATH,
+	    path_p->post_prefix_len) > path_p->post_prefix_len) {
+		auto_debug_print(AUTO_DBGLVL_ERR,
+		    "ai_du_get_searched_pkg_list: search repo location path "
+		    "buffer overflow.\n");
+		return (NULL);
+	}
+
+	auto_log_print(gettext("ai_du_get_searched_pkg_list: Querying manifest "
+	    "for explicit repo for getting missing driver packages...\n"));
+
+	search_locns = ai_get_manifest_values(path_p->path_str, &sublen);
+	if (sublen == 1) {
+		search_locn = search_locns[0];
+	} else if (sublen <= 0) {
+		search_locn = empty_string;
+	} else {
+		auto_log_print(gettext("ai_du_get_searched_pkg_list: "
+		    "<ai_add_drivers> manifest error:\n"
+		    "Only one <searchall> entry allowed\n"));
+		goto done;
+	}
+
+	/* Get repo publisher, if specified. */
+
+	if (strlcpy(path_p->post_prefix_start, SEARCH_PUB_NODEPATH,
+	    path_p->post_prefix_len) > path_p->post_prefix_len) {
+		auto_debug_print(AUTO_DBGLVL_ERR,
+		    "ai_du_get_searched_pkg_list: search repo publisher path "
+		    "buffer overflow.\n");
+		goto done;
+	}
+
+	search_pubs = ai_get_manifest_values(path_p->path_str, &sublen);
+	if (sublen == 1) {
+		search_pub = search_pubs[0];
+	} else if (sublen <= 0) {
+		search_pub = empty_string;
+	} else {
+		auto_log_print(gettext("ai_du_get_searched_pkg_list: "
+		    "<ai_add_drivers> manifest error:\n"
+		    "Only one publisher allowed for a <searchall> entry\n"));
+		goto done;
+	}
+
+	/* Can't have one without the other. */
+	if ((search_pub == empty_string) ^
+	    (search_locn == empty_string)) {
+		auto_log_print(gettext("ai_du_get_searched_pkg_list: "
+		    "<ai_add_drivers> manifest error:\n"
+		    "search repo location and "
+		    "publisher must be specified together.\n"));
+		goto done;
+	}
+
+	/*
+	 * if publisher and location provided, create tuple from them and
+	 * build a repo list from it.
+	 */
+	if (search_pub != empty_string) {
+
+		auto_log_print(gettext("ai_du_get_searched_pkg_list: "
+		    "Found repo in manifest: publisher:%s, location:%s\n"),
+		    search_pub, search_locn);
+
+		pTuple = PyTuple_New(2);
+		PyTuple_SetItem(pTuple, 0, PyString_FromString(search_pub));
+		PyTuple_SetItem(pTuple, 1, PyString_FromString(search_locn));
+		pRepoTupleList = PyList_New(0);
+		PyList_Append(pRepoTupleList, pTuple);
+		pSearchRepoList = ai_call_ddu_build_repo_list(py_state_p,
+		    pRepoTupleList);
+		Py_DECREF(pTuple);
+		Py_DECREF(pRepoTupleList);
+
+		if (pSearchRepoList == NULL) {
+			auto_debug_print(AUTO_DBGLVL_ERR,
+			    "ai_du_get_searched_pkg_list:"
+			    "Error building search repo list.\n");
+			goto done;
+		}
+	} else {
+		/* No publisher/URL provided.  Return an empty repo list. */
+
+		auto_debug_print(AUTO_DBGLVL_INFO,
+		    "ai_du_get_searched_pkg_list: "
+		    "No <searchall> repo found in manifest\n");
+
+		pSearchRepoList = PyList_New(0);
+	}
+
+	/* Find out if <addall> was specified. */
+
+	if (strlcpy(path_p->post_prefix_start, SEARCH_ADDALL_NODEPATH,
+	    path_p->post_prefix_len) > path_p->post_prefix_len) {
+		auto_debug_print(AUTO_DBGLVL_ERR,
+		    "ai_du_get_searched_pkg_list: search addall path "
+		    "buffer overflow.\n");
+		goto done;
+	}
+
+	/* No more than a single true/false value is allowed. */
+	search_addalls = ai_get_manifest_values(path_p->path_str, &sublen);
+	if ((sublen > 1) ||
+	    ((sublen == 1) &&
+	    ((strcmp(search_addalls[0], "true") != 0) &&
+	    (strcmp(search_addalls[0], "false") != 0)))) {
+		auto_log_print(gettext("ai_du_get_searched_pkg_list: "
+		    "<ai_add_drivers> manifest error:\n"
+		    "invalid addall value for <searchall> entry\n"));
+		goto done;
+
+	/* Default to false if not provided. */
+	} else if ((sublen <= 0) || (strcmp(search_addalls[0], "false") == 0)) {
+		Py_INCREF(Py_False);
+		py_search_addall = Py_False;
+
+	} else {
+		auto_log_print(gettext("ai_du_get_searched_pkg_list: Manifest "
+		    "allows adding of third-party drivers\n"));
+		Py_INCREF(Py_True);
+		py_search_addall = Py_True;
+	}
+
+	/*
+	 * Append packages found for missing devices, to the list of packages
+	 * to install.
+	 */
+	listlen = PyList_Size(pDeviceList);
+	for (i = 0; i < listlen; i++) {
+
+		PyObject *pDDUPackageObject;
+		PyObject *pDDUDevData;
+		char *dev_type;
+		char *descr;
+		boolean_t third_party = B_FALSE;
+
+		pDDUDevData = PyList_GetItem(pDeviceList, i);
+
+		/* Find the package containing the driver for this device. */
+		pDDUPackageObject = ai_call_ddu_package_lookup(py_state_p,
+		    pDDUDevData, pSearchRepoList);
+
+		/* Get info for display / logging purposes, and log it. */
+		if (ai_get_ddu_dev_data_values(pDDUDevData,
+		    &dev_type, &descr) != AUTO_INSTALL_SUCCESS) {
+			auto_debug_print(AUTO_DBGLVL_ERR,
+			    "ai_du_get_searched_pkg_list: Error retrieving "
+			    "device information for display\n");
+			dev_type = descr = empty_string;
+		}
+
+		if (pDDUPackageObject == NULL) {
+			auto_log_print(gettext("ai_du_get_searched_pkg_list: "
+			    "Error retrieving package for "
+			    "\"%s\" type device \"%s\".\n"), dev_type, descr);
+			continue;
+		} else {
+			auto_log_print(gettext("ai_du_get_searched_pkg_list: "
+			    "Found package for \"%s\" type device \"%s\".\n"),
+			    dev_type, descr);
+		}
+
+		(void) ai_get_ddu_package_object_values(pDDUPackageObject,
+		    NULL, NULL, NULL, NULL, NULL, &third_party);
+		if (third_party) {
+			auto_log_print(gettext("  This is a third-party "
+			    "package.\n"));
+		}
+
+		/* Append the package info to the returned list. */
+
+		/*
+		 * NOTE: Don't decref pTuple here as PyList_Append doesn't
+		 * steal a reference to it.
+		 */
+		pTuple = PyTuple_New(3);
+		PyTuple_SetItem(pTuple, 0, pDDUPackageObject);
+		Py_INCREF(py_search_addall);
+		PyTuple_SetItem(pTuple, 1, py_search_addall); /* 3rd party OK */
+		Py_INCREF(Py_False);
+		PyTuple_SetItem(pTuple, 2, Py_False);	/* always install */
+
+		PyList_Append(pPackageList, pTuple);
+	}
+
+	/* Success.  Prepare to return the package list just prepared. */
+	pRval = pPackageList;
+
+done:
+	/* Cleanup time, whether an error occured or not. */
+	ai_free_manifest_values(search_locns);
+	ai_free_manifest_values(search_pubs);
+	Py_XDECREF(py_search_addall);
+	Py_XDECREF(pSearchRepoList);
+	Py_XDECREF(pDeviceList);
+	return (pRval);
+}
+
+/*
+ * ai_du_install_packages:
+ * Install packages provided by the pPkgTupleList.  Install in the filesystem /
+ * tree under install_root, skipping packages with noinstall flag set if
+ * honor_noinstall is set.
+ *
+ * Arguments:
+ *   py_state_p: Initialized py_state_t object.
+ *   pPkgTupleList: Python list of (ddu_package_object, third_party_ok,
+ *	noinstall) tuples which define packages to add and their parameters for
+ *	adding them.  Guaranteed not to be NULL.  When third_party_ok is true,
+ *	add the corresponding package even if it is to be downloaded from a
+ *	third party website.  When noinstall is true in the tuple and the
+ *	honor_noinstall argument is also true, skip adding the corresponding
+ *	package.  (This may be used to skip installation onto the target disk,
+ *	after having installed into the booted install environment.)
+ *   install_root: Top of the filesystem or tree where the packages are to be
+ *	installed.
+ *   honor_noinstall: When true and the noinstall flag is set in a package
+ *	tuple, skip installing that package.
+ *   num_installed_pkgs_p: Returns the value passed in, plus the number of
+ *	packages actually installed.
+ *
+ * Returns:
+ *   AUTO_INSTALL_SUCCESS: All packages were able to be installed.
+ *   AUTO_INSTALL_FAILURE: At least one package was not able to be installed.
+ *
+ * NOTE: check installer logfile for details of the failure.
+ */
+static int
+ai_du_install_packages(py_state_t *py_state_p, PyObject *pPkgTupleList,
+    char *install_root, boolean_t honor_noinstall, int *num_installed_pkgs_p)
+{
+	Py_ssize_t len;
+	Py_ssize_t i;
+	int rval = AUTO_INSTALL_SUCCESS;
+
+	auto_log_print(gettext("ai_du_install_packages: "
+	    "Installing packages to %s\n"), install_root);
+
+	len = PyList_Size(pPkgTupleList);
+	for (i = 0; i < len; i++) {
+
+		/* Break out the tuple. */
+
+		PyObject *pTuple = PyList_GetItem(pPkgTupleList, i);
+		PyObject *pDDUPackageObject = PyTuple_GetItem(pTuple, 0);
+		PyObject *pThirdPartyOK = PyTuple_GetItem(pTuple, 1);
+		PyObject *pNoInstall = PyTuple_GetItem(pTuple, 2);
+		char *type = empty_string;
+		char *location = empty_string;
+		char *name = empty_string;
+		char *descr = empty_string;
+		char *inf_link = empty_string;
+		boolean_t third_party;
+
+		if (ai_get_ddu_package_object_values(pDDUPackageObject,
+		    &type, &location, &name, &descr, &inf_link, &third_party) !=
+		    AUTO_INSTALL_SUCCESS) {
+			auto_debug_print(AUTO_DBGLVL_ERR,
+			    "ai_du_install_packages: Error extracting package "
+			    "information for ddu_package_object.\n");
+			type = location = name = descr = inf_link =
+			    empty_string;
+			third_party = B_FALSE;
+		} else {
+			if (strcmp(name, empty_string) == 0) {
+				auto_log_print(gettext(
+				    "  %s package at location:%s\n"),
+				    type, location);
+			} else {
+				auto_log_print(gettext(
+				    "  %s package at location:%s, name:%s\n"),
+				    type, location, name);
+			}
+		}
+
+		if (PyObject_IsTrue(pNoInstall) && honor_noinstall) {
+			auto_log_print(gettext("ai_du_install_packages: "
+			    "    honoring noinstall: skipping package.\n"));
+			continue;
+		}
+
+		/* Display any third-party package errors to console. */
+		if ((! PyObject_IsTrue(pThirdPartyOK)) && third_party) {
+			char *msg1_3p = gettext("  Manifest is not allowing "
+			    "third party packages found through search for "
+			    "installation to %s\n");
+			char *msg2_3p = gettext("  Info on the package to "
+			    "install to make device \"%s\"\n"
+			    "    operational is available:\n"
+			    "    %s\n");
+			(void) fprintf(stderr, msg1_3p, install_root);
+			auto_log_print(msg1_3p, install_root);
+			(void) fprintf(stderr, msg2_3p, descr, inf_link);
+			auto_log_print(msg2_3p, descr, inf_link);
+			rval = AUTO_INSTALL_FAILURE;
+			continue;
+		}
+
+		/* Handle uninstallable package objects. */
+		if (strcmp(location, empty_string) == 0) {
+			if (strcmp(inf_link, empty_string) == 0) {
+				auto_log_print(gettext(
+				    "ai_du_install_packages: Package not "
+				    "found for device: \"%s\"\n"), descr);
+			} else {
+				auto_log_print(gettext(
+				    "ai_du_install_packages: Package for "
+				    "device: \"%s\" must be installed "
+				    "manually.\n"
+				    "For more information go to:\n %s\n"),
+				    descr, inf_link);
+			}
+			rval = AUTO_INSTALL_FAILURE;
+			continue;
+		}
+
+		/* All is well.  Install the package. */
+		if (ai_call_ddu_install_package(py_state_p, pDDUPackageObject,
+		    install_root, PyObject_IsTrue(pThirdPartyOK)) ==
+		    AUTO_INSTALL_FAILURE) {
+			auto_log_print(gettext("ai_du_install_packages: "
+			    "Error installing package to %s\n"), install_root);
+			rval = AUTO_INSTALL_FAILURE;
+		} else {
+			(*num_installed_pkgs_p)++;
+		}
+	}
+	return (rval);
+}
+
+/*
+ * ai_uniq_manifest_values:
+ * Remove duplicate values in lists returned by ai_lookup_manifest_values().
+ *
+ * Arguments:
+ *   in: the input list of values.
+ *   len_p: The length of the input list, on input.  Returns the length of the
+ *	list returned.
+ *
+ * Returns:
+ *   Success: The resulting list of unique values.
+ *   Failure: NULL
+ */
+static char **
+ai_uniq_manifest_values(char **in, int *len_p)
+{
+	int in_len = *len_p;
+	int dup_count = 0;
+	char **out;
+	char *comp;
+	boolean_t *is_dup = (boolean_t *)alloca(in_len * sizeof (boolean_t));
+	int i, j;
+
+	if ((in_len == 0) || (in == NULL)) {
+		return (NULL);
+	}
+
+	bzero(is_dup, (in_len * sizeof (boolean_t)));
+
+	for (i = 0; i < in_len - 1; i++) {
+		comp = in[i];
+		for (j = i + 1; j < in_len; j++) {
+			if ((!is_dup[j]) && (strcmp(comp, in[j]) == 0)) {
+				is_dup[j] = B_TRUE;
+				dup_count++;
+			}
+		}
+	}
+
+	out = (char **)malloc((in_len - dup_count + 1) * sizeof (char *));
+	for (i = 0, j = 0; i < in_len; i++) {
+		if (!is_dup[i]) {
+			out[j++] = strdup(in[i]);
+		}
+	}
+	out[j] = NULL;
+
+	*len_p = j;
+	return (out);
+}
+
+/*
+ * ai_du_call_update_archive_ict:
+ * Call the bootadm update_archive ICT
+ *
+ * Arguments:
+ *   py_state_p: Initialized py_state_t object.
+ *   install_root: root of the tree where to call the ICT.
+ *
+ * Returns:
+ *   AUTO_INSTALL_SUCCESS: ICT was successfully executed.
+ *   AUTO_INSTALL_FAILURE: ICT was not successfully executed.
+ */
+static int
+ai_du_call_update_archive_ict(py_state_t *py_state_p, char *install_root)
+{
+	int rval = AUTO_INSTALL_FAILURE;
+	PyObject *pICT_instance = NULL;
+	PyObject *pICT_rval = NULL;
+
+	/* Find the constructor. */
+	PyObject *pFunc = PyObject_GetAttrString(py_state_p->pICTModule,
+	    ICT_CLASS);
+
+	if ((pFunc == NULL) || (!PyCallable_Check(pFunc))) {
+		auto_debug_print(AUTO_DBGLVL_ERR,
+		    "ICT constructor not callable\n");
+	} else {
+		/* Set up args to python function. */
+		PyObject *pArgs = PyTuple_New(1);
+		PyTuple_SetItem(pArgs, 0, PyString_FromString(install_root));
+
+		/* Call constructor. */
+		pICT_instance = PyObject_CallObject(pFunc, pArgs);
+		Py_XDECREF(pFunc);
+		Py_DECREF(pArgs);
+		if ((PyErr_Occurred()) || (pICT_instance == NULL) ||
+		    (pICT_instance == Py_None)) {
+			auto_debug_dump_file(AUTO_DBGLVL_ERR, DDU_ERRLOG);
+			auto_debug_print(AUTO_DBGLVL_ERR,
+			    "ICT constructor failed\n");
+			if (PyErr_Occurred()) {
+				PyErr_Print();
+			}
+			Py_CLEAR(pICT_instance);
+		}
+	}
+
+	if (pICT_instance != NULL) {
+		pICT_rval = PyObject_CallMethod(
+		    pICT_instance, ICT_UPDATE_ARCHIVE, NULL);
+		if (pICT_rval == NULL) {
+			auto_debug_print(AUTO_DBGLVL_ERR,
+			    "Error running update_boot_archive ICT.\n");
+		} else if (PyInt_AsLong(pICT_rval) != 0) {
+			auto_debug_print(AUTO_DBGLVL_ERR,
+			    "update_boot_archive ICT returned an error.\n");
+		} else {
+			rval = AUTO_INSTALL_SUCCESS;
+		}
+		Py_XDECREF(pICT_rval);
+		Py_DECREF(pICT_instance);
+	}
+
+	return (rval);
+}
+
+/* Exported functions. */
+
+/*
+ * ai_du_get_and_install:
+ * Query the manifest for the entire <ai_add_drivers> section, and add packages
+ * accordingly.  Add packages to install_root.  If a package has its noinstall
+ * flag set and the honor_noinstall argument is set, skip adding that package.
+ * Save the list of packages to install to the module global py_pkg_list, so
+ * that the same list of packages can be installed to a different target with
+ * ai_du_install().
+ *
+ * Install all explicitly-stated packages first.  Then do <searchall> last.
+ * This is to handle any explicit requests for matching a special driver to a
+ * device, before <searchall> finds the first available one.
+ *
+ * Assumes ai_create_manifest_image() has set up the manifest data.
+ * Does not assume any data has been verified though.
+ *
+ * Arguments:
+ *   install_root: Top of the filesystem or tree where the packages are to be
+ *	installed.
+ *   honor_noinstall: When true and the noinstall flag is set in a package
+ *	tuple, skip installing that package.
+ *   update_boot_archive: When true, run the ICT to update the boot archive.
+ *
+ * Returns:
+ *   The number of packages successfully processed if all are successfully
+ *	processed.  NOTE: this can be zero if there are none to process.
+ *   -1: One or more packages could not be successfully processed.
+ *   Boot archive update status is not reflected in this return status.
+ *   NOTE: this routine will continue on most errors, in order to install as
+ *	many packages as possible.
+ *
+ * NOTE: check installer logfile for details of the failure.
+ *
+ * Side effects:
+ *   module global py_pkg_list is set to point to list of packages to install.
+ */
+int
+ai_du_get_and_install(char *install_root, boolean_t honor_noinstall,
+    boolean_t update_boot_archive)
+{
+	PyObject *manual_pkg_list;
+	PyObject *searched_pkg_list;
+	py_state_t *py_state_p;
+	path_t path;
+	char **dummy_list;
+	int num_entries;
+	int len;
+	Py_ssize_t manual_size = 0;
+	Py_ssize_t searched_size = 0;
+	int rval = 0;
+	int num_pkgs_installed = 0;
+
+	/* Initialize path, post_prefix_start and post_prefix_len for later. */
+	(void) strncpy(path.path_str, AIM_PREFACE, MAX_NODEPATH_SIZE);
+	len = strlen(path.path_str);
+	path.post_prefix_start = &path.path_str[len];
+	path.post_prefix_len = MAX_NODEPATH_SIZE - len;
+
+	/*
+	 * Set up an empty py_pkg_list so ai_du_install() knows this function
+	 * was called first.
+	 */
+	if (py_pkg_list != NULL) {
+		Py_CLEAR(py_pkg_list);
+	}
+
+	py_pkg_list = PyList_New(0);
+
+	/*
+	 * See if the manifest has at least one bundle or searchall entry.
+	 * If not, just return success (e.g. no-op).
+	 */
+
+	/* Get the number of bundle entries. */
+	if (strlcpy(path.post_prefix_start, BUNDLE_NODEPATH,
+	    path.post_prefix_len) > path.post_prefix_len) {
+		auto_debug_print(AUTO_DBGLVL_ERR,
+		    "ai_du_get_and_install: bundle path buffer overflow\n");
+		return (-1);
+	}
+
+	/* Get number of bundle entries in the manifest. */
+	dummy_list = ai_get_manifest_values(path.path_str, &num_entries);
+	ai_free_manifest_values(dummy_list);
+
+	if (num_entries <= 0) {
+		/* See if there is a searchall entry in the manifest. */
+		if (strlcpy(path.post_prefix_start, SEARCH_NODEPATH,
+		    path.post_prefix_len) > path.post_prefix_len) {
+			auto_debug_print(AUTO_DBGLVL_ERR,
+			    "ai_du_get_and_install: "
+			    "search path buffer overflow\n");
+			return (-1);
+		}
+
+		dummy_list = ai_get_manifest_values(path.path_str,
+		    &num_entries);
+		ai_free_manifest_values(dummy_list);
+		if (num_entries <= 0) {
+			return (0);
+		}
+	}
+
+	/*
+	 * Install all explicitly specified packages first.
+	 *
+	 * Do the search for missing devices afterward, as an independent step,
+	 * to account for newly-operational devices as a result of
+	 * explicitly-specified package installation.
+	 */
+
+	if ((py_state_p = auto_ddu_lib_init()) == NULL) {
+		auto_debug_print(AUTO_DBGLVL_ERR,
+		    "ai_du_get_and_install: "
+		    "Error initializing auto_ddu_lib.\n");
+		rval = -1;
+		goto done;
+	}
+
+	manual_pkg_list = ai_du_get_manual_pkg_list(py_state_p, &path);
+	if (manual_pkg_list == NULL) {
+		auto_debug_print(AUTO_DBGLVL_ERR,
+		    "ai_du_get_and_install: "
+		    "Error getting package <bundle>.\n");
+		rval = -1;
+		/* Keep going.  Don't abort. */
+	} else {
+		manual_size = PyList_Size(manual_pkg_list);
+		if (manual_size > 0) {
+			if (ai_du_install_packages(py_state_p, manual_pkg_list,
+			    install_root, honor_noinstall,
+			    &num_pkgs_installed) != AUTO_INSTALL_SUCCESS) {
+				auto_debug_print(AUTO_DBGLVL_ERR,
+				    "ai_du_get_and_install: Error installing "
+				    "at least one package <bundle>.\n");
+				rval = -1;
+				/* Keep going.  Don't abort. */
+			}
+		}
+	}
+
+	searched_pkg_list = ai_du_get_searched_pkg_list(py_state_p, &path);
+	if (searched_pkg_list == NULL) {
+		auto_debug_print(AUTO_DBGLVL_ERR,
+		    "ai_du_get_and_install: "
+		    "Error searching for inoperable devices and "
+		    "missing driver packages.\n");
+		rval = -1;
+		/* Keep going.  Don't abort. */
+	} else {
+		searched_size = PyList_Size(searched_pkg_list);
+		if (searched_size > 0) {
+			if (ai_du_install_packages(py_state_p,
+			    searched_pkg_list, install_root, honor_noinstall,
+			    &num_pkgs_installed) != AUTO_INSTALL_SUCCESS) {
+				auto_debug_print(AUTO_DBGLVL_ERR,
+				    "ai_du_get_and_install: Error installing "
+				    "at least one searched package "
+				    "for <searchall>.\n");
+				rval = -1;
+				/* Keep going.  Don't abort. */
+			}
+		}
+	}
+
+	if ((update_boot_archive) && (num_pkgs_installed > 0)) {
+		if (ai_du_call_update_archive_ict(py_state_p, install_root) !=
+		    AUTO_INSTALL_SUCCESS) {
+			auto_debug_print(AUTO_DBGLVL_ERR,
+			    "ai_du_get_and_install: Warning: could not update "
+			    "boot archive for %s.\n", install_root);
+		}
+	}
+
+	/*
+	 * Save the manual and searched package lists in py_pkg_list.
+	 * The new list can be used in a later call to ai_du_install().
+	 */
+
+	if (manual_size > 0) {
+		PyList_SetSlice(py_pkg_list, 0, manual_size - 1,
+		    manual_pkg_list);
+	}
+
+	if (searched_size > 0) {
+		PyList_SetSlice(py_pkg_list, manual_size,
+		    manual_size + searched_size - 1, searched_pkg_list);
+	}
+done:
+	auto_ddu_lib_fini(py_state_p);
+
+	if (rval != -1) {
+		rval = num_pkgs_installed;
+	}
+	return (rval);
+}
+
+/*
+ * ai_du_install:
+ * Install additional packages based on driver update parameters fetched from a
+ * previous ai_du_get_and_install() call.  The module global py_pkg_list
+ * supplies the list (and order) of packages to install.  Add packages to
+ * install_root.  If a package has its noinstall flag set and the
+ * honor_noinstall argument is set, skip adding that package.
+ *
+ * This routine assumes the py_pkg_list was set up via a prior call to
+ * ai_du_get_and_install_packages().
+ *
+ * The availability and location of all packages to be installed is assumed the
+ * same as when the py_pkg_list was built (i.e. the most recent call to
+ * ai_du_get_and_install()).
+ *
+ * Arguments:
+ *   install_root: Top of the filesystem or tree where the packages are to be
+ *	installed.
+ *   honor_noinstall: When true and the noinstall flag is set in a package
+ *	tuple, skip installing that package.
+ *   update_boot_archive: When true, run the ICT to update the boot archive.
+ *   NOTE: the modular global py_pkg_list specifies the packages to install.
+ *
+ * Returns:
+ *   The number of packages successfully processed if all are successfully
+ *	processed.  NOTE: this can be zero if there are none to process.
+ *   -1: At least one package was not able to be installed.
+ *   Boot archive update status is not reflected in this return status.
+ *
+ * NOTE: check installer logfile for details of the failure.
+ */
+int
+ai_du_install(char *install_root, boolean_t honor_noinstall,
+    boolean_t update_boot_archive)
+{
+	int rval = 0;
+	int num_pkgs_installed = 0;
+
+	py_state_t *py_state_p;
+
+	if (py_pkg_list == NULL) {
+		auto_debug_print(AUTO_DBGLVL_ERR,
+		    "ai_du_install: ai_du_get_and_install needs to be "
+		    "called first.\n");
+		return (-1);
+
+	} else if (PyList_Size(py_pkg_list) == 0) {
+		return (num_pkgs_installed);
+	}
+
+	if ((py_state_p = auto_ddu_lib_init()) == NULL) {
+		auto_debug_print(AUTO_DBGLVL_ERR,
+		    "ai_du_install: "
+		    "Error initializing auto_ddu_lib.\n");
+		return (-1);
+	}
+
+	if ((rval = ai_du_install_packages(py_state_p, py_pkg_list,
+	    install_root, honor_noinstall, &num_pkgs_installed)) !=
+	    AUTO_INSTALL_SUCCESS) {
+		auto_debug_print(AUTO_DBGLVL_ERR,
+		    "ai_du_install: Error installing packages.\n");
+		rval = -1;
+	}
+
+	if (update_boot_archive && (num_pkgs_installed > 0)) {
+		if (ai_du_call_update_archive_ict(py_state_p,
+		    install_root) != AUTO_INSTALL_SUCCESS) {
+			auto_debug_print(AUTO_DBGLVL_ERR,
+			    "ai_du_install: Warning: could not update boot "
+			    "archive for %s.\n", install_root);
+		}
+	}
+
+	if (rval != -1) {
+		rval = num_pkgs_installed;
+	}
+
+	auto_ddu_lib_fini(py_state_p);
+
+	return (rval);
+}
--- a/usr/src/cmd/auto-install/auto_install.c	Fri May 07 11:46:54 2010 -0700
+++ b/usr/src/cmd/auto-install/auto_install.c	Wed May 12 22:18:21 2010 -0700
@@ -19,8 +19,7 @@
  * CDDL HEADER END
  */
 /*
- * Copyright (c) 2010, Oracle and/or its affiliates. All rights reserved.
- * Use is subject to license terms.
+ * Copyright (c) 2008, 2010, Oracle and/or its affiliates. All rights reserved.
  */
 
 #include <alloca.h>
@@ -121,7 +120,7 @@
 
 	va_start(ap, fmt);
 	/*LINTED*/
-	(void) vsprintf(buf, fmt, ap);
+	(void) vsnprintf(buf, MAXPATHLEN+1, fmt, ap);
 	(void) ls_write_dbg_message("AI", dbg_lvl, buf);
 	va_end(ap);
 }
@@ -138,7 +137,7 @@
 
 	va_start(ap, fmt);
 	/*LINTED*/
-	(void) vsprintf(buf, fmt, ap);
+	(void) vsnprintf(buf, MAXPATHLEN+1, fmt, ap);
 	(void) ls_write_log_message("AI", buf);
 	va_end(ap);
 }
@@ -170,6 +169,43 @@
 }
 
 /*
+ * auto_debug_dump_file()
+ * Description: dumps a file using auto_debug_print()
+ */
+void
+auto_debug_dump_file(ls_dbglvl_t level, char *filename)
+{
+	FILE *file_ptr;
+	char buffer[MAXPATHLEN];
+
+	/* Logfile does not exist.  Nothing to print. */
+	if (access(filename, F_OK) < 0) {
+		return;
+	}
+
+	if (access(filename, R_OK) < 0) {
+		auto_debug_print(AUTO_DBGLVL_ERR,
+		    "ddu errlog %s does not have read permissions.\n");
+		return;
+	}
+
+	/* Use buffer to set up the command. */
+	snprintf(buffer, MAXPATHLEN, "/usr/bin/cat %s", filename);
+	if ((file_ptr = popen(buffer, "r")) == NULL) {
+		auto_debug_print(AUTO_DBGLVL_ERR,
+		    "Error opening ddu errlog %s to dump errors: %s\n",
+		    filename, strerror(errno));
+		return;
+	}
+
+	/* Reuse buffer to get the file data. */
+	while (fgets(buffer, MAXPATHLEN, file_ptr) != NULL) {
+		auto_debug_print(level, "%s", buffer);
+	}
+	(void) pclose(file_ptr);
+}
+
+/*
  * This function splits the file that is passed to AI manifest and SC manifest
  *
  * Input:
@@ -1760,9 +1796,11 @@
 	char		profile[MAXNAMELEN];
 	char		diskname[MAXNAMELEN];
 	char		slicename[MAXNAMELEN];
+	int		num_du_pkgs_installed;
 	boolean_t	auto_reboot_enabled = B_FALSE;
 	nvlist_t	*ls_init_attr = NULL;
 	boolean_t	auto_install_failed = B_FALSE;
+	int		retries;
 
 	(void) setlocale(LC_ALL, "");
 	(void) textdomain(TEXT_DOMAIN);
@@ -1856,15 +1894,60 @@
 		 * it up in an in-memory tree so searches can be
 		 * done on it in the future to retrieve the values
 		 */
-		if (ai_validate_and_setup_manifest(AI_MANIFEST_FILE) ==
+		if (ai_create_manifest_image(AI_MANIFEST_FILE) ==
 		    AUTO_VALID_MANIFEST) {
-			auto_log_print(gettext("%s is a valid manifest\n"),
+			auto_log_print(gettext("%s manifest created\n"),
 			    profile);
 		} else {
-			auto_log_print(gettext("Auto install failed. Invalid "
-			    "manifest %s specified\n"), profile);
+			auto_log_print(gettext("Auto install failed. Error "
+			    "creating manifest %s\n"), profile);
 			exit(AI_EXIT_FAILURE_AIM);
 		}
+
+		/*
+		 * Install any drivers required for installation, in the
+		 * booted environment.  This must be done before semantic
+		 * validation, since this may add required devices which
+		 * are needed to pass validation.
+		 */
+
+		/*
+		 * First boolean: do not honor noinstall flag.
+		 * Second boolean: do not update the boot archive.
+		 */
+		num_du_pkgs_installed =
+		    ai_du_get_and_install("/", B_FALSE, B_FALSE);
+
+		/*
+		 * Note: Print no messages if num_du_pkgs_installed = 0
+		 * This means no packages and no errors, or no-op.
+		 */
+		if (num_du_pkgs_installed > 0) {
+			auto_log_print(gettext("All additional "
+			    "driver packages successfully installed "
+			    "to booted installation environment.\n"));
+		} else if (num_du_pkgs_installed < 0) {
+			char *du_warning = gettext("Warning: some additional "
+			    "driver packages could not be installed\n"
+			    "  to booted installation environment.\n"
+			    "  These drivers may or may not be required for "
+			    "the installation to proceed.\n"
+			    "  Will continue anyway...\n");
+			auto_log_print(du_warning);
+			(void) fprintf(stderr, du_warning);
+		}
+
+		if (ai_setup_manifest_image() == AUTO_VALID_MANIFEST) {
+			auto_log_print(gettext(
+			    "%s manifest setup and validated\n"), profile);
+		} else {
+			char *setup_err = gettext("Auto install failed. Error "
+			    "setting up and validating manifest %s\n");
+			auto_log_print(setup_err, profile);
+			(void) fprintf(stderr, setup_err, profile);
+			exit(AI_EXIT_FAILURE_AIM);
+		}
+
 		diskname[0] = '\0';
 
 		/*
@@ -1896,6 +1979,33 @@
 
 		auto_install_failed = B_TRUE;
 	} else {
+		/*
+		 * Install additional drivers on target.
+		 * First boolean: honor noinstall flag.
+		 * Second boolean: update boot archive.
+		 */
+		num_du_pkgs_installed =
+		    ai_du_install(INSTALLED_ROOT_DIR, B_TRUE, B_TRUE);
+		if (num_du_pkgs_installed < 0) {
+			char *tgt_inst_err = gettext("Basic installation was "
+			    "successful.  However, there was an error\n");
+			auto_log_print(tgt_inst_err, profile);
+			(void) fprintf(stderr, tgt_inst_err);
+			tgt_inst_err = gettext("installing at least one "
+			    "additional driver package on target.\n");
+			auto_log_print(tgt_inst_err, profile);
+			(void) fprintf(stderr, tgt_inst_err);
+			tgt_inst_err = gettext("Please verify that all driver "
+			    "packages required for reboot are installed "
+			    "before rebooting.\n");
+			auto_log_print(tgt_inst_err, profile);
+			(void) fprintf(stderr, tgt_inst_err);
+			auto_install_failed = B_TRUE;
+		}
+	}
+
+	if (! auto_install_failed) {
+
 		if (auto_reboot_enabled) {
 			printf(gettext("Automated Installation succeeded."
 			    " System will be rebooted now\n"));
@@ -1931,8 +2041,6 @@
 	if (ls_transfer("/", INSTALLED_ROOT_DIR) != LS_E_SUCCESS) {
 		auto_log_print(gettext(
 		    "Could not transfer log file to the target\n"));
-
-		auto_install_failed = B_TRUE;
 	}
 
 	/*
@@ -1951,6 +2059,9 @@
 	 *  AI_EXIT_AUTO_REBOOT - installation succeeded, reboot automatically
 	 */
 
+	if (auto_install_failed)
+		exit(AI_EXIT_FAILURE);
+
 	if (auto_reboot_enabled)
 		exit(AI_EXIT_AUTO_REBOOT);
 
--- a/usr/src/cmd/auto-install/auto_install.h	Fri May 07 11:46:54 2010 -0700
+++ b/usr/src/cmd/auto-install/auto_install.h	Wed May 12 22:18:21 2010 -0700
@@ -19,8 +19,7 @@
  * CDDL HEADER END
  */
 /*
- * Copyright (c) 2010, Oracle and/or its affiliates. All rights reserved.
- * Use is subject to license terms.
+ * Copyright (c) 2008, 2010, Oracle and/or its affiliates. All rights reserved.
  */
 
 #ifndef _AUTO_INSTALL_H
@@ -362,14 +361,18 @@
 
 void	auto_log_print(char *fmt, ...);
 void	auto_debug_print(ls_dbglvl_t dbg_lvl, char *fmt, ...);
+void	auto_debug_dump_file(ls_dbglvl_t dbg_lvl, char *filename);
 
 int	auto_target_discovery(void);
 int	auto_select_install_target(char **diskname, auto_disk_info *adi);
 
 int	auto_parse_sc_manifest(char *profile_file, auto_sc_params *sp);
 
-int	ai_validate_and_setup_manifest(char *filename);
+int	ai_create_manifest_image(char *filename);
+int	ai_setup_manifest_image();
 void	ai_teardown_manifest_state();
+char	**ai_get_manifest_values(char *path, int *len);
+void	ai_free_manifest_values(char **value_list);
 int 	ai_get_manifest_disk_info(auto_disk_info *);
 int 	ai_get_manifest_swap_device_info(auto_swap_device_info *adsi);
 int 	ai_get_manifest_dump_device_info(auto_dump_device_info *addi);
@@ -390,8 +393,15 @@
 void	free_repo_mirror_list(auto_mirror_repo_t *mirror);
 
 PyObject *ai_create_manifestserv(char *filename);
+int	ai_setup_manifestserv(PyObject *server_obj);
 void	ai_destroy_manifestserv(PyObject *server_obj);
 char	**ai_lookup_manifest_values(PyObject *server_obj, char *path, int *len);
+void	ai_free_manifest_value_list(char **value_list);
+
+int	ai_du_get_and_install(char *install_root, boolean_t honor_noinstall,
+	    boolean_t update_boot_archive);
+int	ai_du_install(char *install_root, boolean_t honor_noinstall,
+	    boolean_t update_boot_archive);
 
 int	mount_iscsi_target_if_requested(auto_disk_info *, char *, int);
 
--- a/usr/src/cmd/auto-install/auto_parse.c	Fri May 07 11:46:54 2010 -0700
+++ b/usr/src/cmd/auto-install/auto_parse.c	Wed May 12 22:18:21 2010 -0700
@@ -19,8 +19,7 @@
  * CDDL HEADER END
  */
 /*
- * Copyright (c) 2010, Oracle and/or its affiliates. All rights reserved.
- * Use is subject to license terms.
+ * Copyright (c) 2008, 2010, Oracle and/or its affiliates. All rights reserved.
  */
 
 #include <fcntl.h>
@@ -37,7 +36,8 @@
 
 #include "auto_install.h"
 
-PyObject *manifest_serv_obj = NULL;
+static PyObject *manifest_serv_obj;
+static char *manifest_filename;
 
 /*
  * Dump errors found during syntactic validation of AI manifest -
@@ -103,22 +103,24 @@
 }
 
 /*
- * Validate the manifest syntactically as well as
- * semantically.
+ * Create the manifest data image in memory.  (Does not validate it.)
  *
- * As part of the validation process, fill in the
- * defaults for the attributes that aren't specified
- * and import the manifest into an in-memory tree
+ * Import the manifest into an in-memory tree
  * that can subsequently be queried for the various
  * attributes. A handle to the in-memory tree is stored
  * as a ManifestServ object pointed to by manifest_serv_obj.
  *
+ * The manifest filename is saved for later use, in manifest_filename.
+ *
+ * Note that this function must be called before anything else which
+ * references manifest_serv_obj or manifest_filename in this module.
+ *
  * Returns
  * 	AUTO_VALID_MANIFEST if it's a valid manifest
  * 	AUTO_INVALID_MANIFEST if it's an invalid manifest
  */
 int
-ai_validate_and_setup_manifest(char *filename)
+ai_create_manifest_image(char *filename)
 {
 	/*
 	 * If the manifest_serv_obj is set it means that
@@ -128,9 +130,34 @@
 	if (manifest_serv_obj != NULL)
 		return (AUTO_VALID_MANIFEST);
 
+	manifest_filename = NULL;
 	manifest_serv_obj = ai_create_manifestserv(filename);
-	if (manifest_serv_obj != NULL)
+	if (manifest_serv_obj != NULL) {
+		manifest_filename = strdup(filename);
 		return (AUTO_VALID_MANIFEST);
+	}
+
+	auto_log_print(gettext("Failure to create manifest data in memory.\n"));
+	return (AUTO_INVALID_MANIFEST);
+}
+
+/*
+ * Validate the manifest syntactically as well as
+ * semantically.
+ *
+ * As part of the validation process, fill in the
+ * defaults for the attributes that aren't specified.
+ *
+ * Returns
+ * 	AUTO_VALID_MANIFEST if it's a valid manifest
+ * 	AUTO_INVALID_MANIFEST if it's an invalid manifest
+ */
+int
+ai_setup_manifest_image()
+{
+	if (ai_setup_manifestserv(manifest_serv_obj) == AUTO_INSTALL_SUCCESS) {
+		return (AUTO_VALID_MANIFEST);
+	}
 
 	/*
 	 * if the validation process failed, capture output of syntactic
@@ -139,7 +166,8 @@
 	auto_log_print(gettext("Syntactic validation of the manifest failed "
 	    "with following errors\n"));
 
-	if (dump_ai_manifest_errors(filename, AI_MANIFEST_SCHEMA) == -1) {
+	if (dump_ai_manifest_errors(
+	    manifest_filename, AI_MANIFEST_SCHEMA) == -1) {
 		auto_log_print(gettext("Failed to obtain result of syntactic "
 		    "validation\n"));
 	}
@@ -166,6 +194,15 @@
 	return (ai_lookup_manifest_values(manifest_serv_obj, path, len));
 }
 
+/*
+ * Free memory allocated by ai_get_manifest_values().
+ */
+void
+ai_free_manifest_values(char **value_list)
+{
+	ai_free_manifest_value_list(value_list);
+}
+
 static char **
 ai_get_manifest_partition_action(int *len)
 {
--- a/usr/src/cmd/auto-install/auto_parse_manifest.c	Fri May 07 11:46:54 2010 -0700
+++ b/usr/src/cmd/auto-install/auto_parse_manifest.c	Wed May 12 22:18:21 2010 -0700
@@ -20,8 +20,7 @@
  */
 
 /*
- * Copyright 2009 Sun Microsystems, Inc.  All rights reserved.
- * Use is subject to license terms.
+ * Copyright (c) 2008, 2010, Oracle and/or its affiliates. All rights reserved.
  */
 
 #include <Python.h>
@@ -29,15 +28,19 @@
 
 #define	AI_PARSE_MANIFEST_SCRIPT "osol_install.auto_install.ai_parse_manifest"
 #define	AI_CREATE_MANIFESTSERV "ai_create_manifestserv"
+#define	AI_SETUP_MANIFESTSERV "ai_setup_manifestserv"
 #define	AI_LOOKUP_MANIFEST_VALUES "ai_lookup_manifest_values"
 
 static PyThreadState * mainThreadState = NULL;
 
 /*
- * The C interface to ai_create_manifestserv (python module)
+ * The C interface to ai_create_manifestserv (python module).
  * This function takes a manifest file and hands it off to
- * the ManifestServ. It returns the socket thus created which
- * can then be used to lookup various paths.
+ * the ManifestServ. It returns the ManifestServ object thus
+ * created which can then be used to lookup various paths.
+ *
+ * Note that the ManifestServ object created here has not been
+ * validated before being returned.
  *
  * ai_destroy_manifestserv() must be called after all the
  * processing has been done to destroy the ManifestServ object
@@ -48,10 +51,13 @@
 PyObject *
 ai_create_manifestserv(char *filename)
 {
-	PyObject	*pFunc, *pModule = NULL, *pName;
-	PyObject	*pArgs, *pValues;
+	PyObject	*pFunc;
+	PyObject	*pName;
+	PyObject	*pArgs;
 	PyThreadState	*myThreadState;
+	PyObject	*pModule = NULL;
 	PyObject	*rv = NULL;
+	PyObject	*pRet = NULL;
 
 	if (!Py_IsInitialized()) {
 		Py_Initialize();
@@ -66,51 +72,43 @@
 		pModule = PyImport_Import(pName);
 		Py_DECREF(pName);
 	}
-	if (pModule == NULL) {
-		PyErr_Print();
-		auto_debug_print(AUTO_DBGLVL_INFO, "Call failed: %s\n",
-		    AI_CREATE_MANIFESTSERV);
-		Py_Finalize();
-		return (NULL);
-	}
+	if (pModule != NULL) {
+		/* Load the ai_parse_manifest module */
+		pFunc = PyObject_GetAttrString(pModule, AI_CREATE_MANIFESTSERV);
+		/* pFunc is a new reference */
+		if (pFunc && PyCallable_Check(pFunc)) {
 
-	/* Load the ai_parse_manifest module */
-	pFunc = PyObject_GetAttrString(pModule, AI_CREATE_MANIFESTSERV);
-	/* pFunc is a new reference */
-	if (pFunc && PyCallable_Check(pFunc)) {
-		PyObject *pRet;
-
-		pArgs = PyTuple_New(1);
-		PyTuple_SetItem(pArgs, 0, PyString_FromString(filename));
+			pArgs = PyTuple_New(1);
+			PyTuple_SetItem(pArgs, 0,
+			    PyString_FromString(filename));
 
-		/* Call the ai_parse_manifest */
-		pRet = PyObject_CallObject(pFunc, pArgs);
-		Py_DECREF(pArgs);
-		if (pRet != NULL) {
-			/*
-			 * A reference is getting stolen here.
-			 * We intentionally don't do a DECREF
-			 * so that future calls using this object
-			 * have a valid ManifestServ object to work
-			 * with.
-			 */
-			if (pRet != Py_None)
-				rv = pRet;
+			/* Call the ai_parse_manifest */
+			pRet = PyObject_CallObject(pFunc, pArgs);
+			Py_DECREF(pArgs);
+			if ((pRet != NULL) && (!PyErr_Occurred())) {
+				/*
+				 * A reference is getting stolen here.
+				 * We intentionally don't do a DECREF
+				 * so that future calls using this object
+				 * have a valid ManifestServ object to work
+				 * with.
+				 */
+				if (pRet != Py_None)
+					rv = pRet;
+			} else {
+				PyErr_Print();
+				auto_debug_print(AUTO_DBGLVL_ERR,
+				    "Call failed: %s\n",
+				    AI_CREATE_MANIFESTSERV);
+			}
 		} else {
-			Py_DECREF(pFunc);
-			Py_DECREF(pModule);
-			PyErr_Print();
-			auto_debug_print(AUTO_DBGLVL_INFO, "Call failed: %s\n",
+			auto_debug_print(AUTO_DBGLVL_ERR, "Python function "
+			    "does not appear callable: %s\n",
 			    AI_CREATE_MANIFESTSERV);
-			rv = NULL;
 		}
-	} else {
-		if (PyErr_Occurred())
-			PyErr_Print();
-		rv = NULL;
 	}
 	Py_XDECREF(pFunc);
-	Py_DECREF(pModule);
+	Py_XDECREF(pModule);
 	PyThreadState_Swap(mainThreadState);
 	PyThreadState_Clear(myThreadState);
 	PyThreadState_Delete(myThreadState);
@@ -118,20 +116,89 @@
 }
 
 /*
- * Lookup an path from the socket that was
- * created by calling ai_create_manifestserv
+ * The C interface to ai_setup_manifestserv (python module).
+ * Sets up and validates the data of a ManifestServ object.
+ * Must be called after ai_create_manifestserv has set up a
+ * ManifestServ object in memory.
+ *
+ * This function can only be invoked from a single threaded
+ * context.
+ */
+int
+ai_setup_manifestserv(PyObject *server_obj)
+{
+	PyObject	*pFunc;
+	PyObject	*pName;
+	PyObject	*pArgs;
+	PyThreadState	*myThreadState;
+	PyObject	*pRet;
+	PyObject	*pModule = NULL;
+	int		rval = AUTO_INSTALL_SUCCESS;
+
+	if (!Py_IsInitialized()) {
+		Py_Initialize();
+	}
+
+	PyEval_InitThreads();
+	mainThreadState = PyThreadState_Get();
+	myThreadState = PyThreadState_New(mainThreadState->interp);
+	PyThreadState_Swap(myThreadState);
+
+	if ((pName = PyString_FromString(AI_PARSE_MANIFEST_SCRIPT)) != NULL) {
+		pModule = PyImport_Import(pName);
+		Py_DECREF(pName);
+	}
+	if (pModule != NULL) {
+		/* Load the ai_parse_manifest module */
+		pFunc = PyObject_GetAttrString(pModule, AI_SETUP_MANIFESTSERV);
+		/* pFunc is a new reference */
+		if (pFunc && PyCallable_Check(pFunc)) {
+			pArgs = PyTuple_New(1);
+			Py_INCREF(server_obj);
+			PyTuple_SetItem(pArgs, 0, server_obj);
+
+			/* Call the ai_parse_manifest */
+			pRet = PyObject_CallObject(pFunc, pArgs);
+			rval = PyInt_AS_LONG(pRet);
+			Py_DECREF(pRet);
+			Py_DECREF(pArgs);
+		} else {
+			auto_debug_print(AUTO_DBGLVL_ERR, "Python function "
+			    "does not appear callable: %s\n",
+			    AI_SETUP_MANIFESTSERV);
+			rval = AUTO_INSTALL_FAILURE;
+		}
+	} else {
+		PyErr_Print();
+		auto_debug_print(AUTO_DBGLVL_ERR, "Call failed: %s\n",
+		    AI_SETUP_MANIFESTSERV);
+		rval = AUTO_INSTALL_FAILURE;
+	}
+	Py_XDECREF(pFunc);
+	Py_XDECREF(pModule);
+	PyThreadState_Swap(mainThreadState);
+	PyThreadState_Clear(myThreadState);
+	PyThreadState_Delete(myThreadState);
+	return (rval);
+}
+
+/*
+ * Lookup a nodepath.
  *
  * The caller is responsible for freeing up
- * memory associated with the return value
+ * memory associated with the return value.
+ * ai_free_manifest_values() is provided for this.
  */
 char **
 ai_lookup_manifest_values(PyObject *server_obj, char *path, int *len)
 {
-	PyObject 	*pFunc, *pName, *pModule = NULL;
+	PyObject	*pFunc;
+	PyObject	*pName;
 	PyObject 	*pArgs;
 	PyThreadState	*myThreadState;
 	PyObject 	*item;
 	char		**rv;
+	PyObject	*pModule = NULL;
 
 	if (!Py_IsInitialized()) {
 		Py_Initialize();
@@ -169,17 +236,24 @@
 	if (pFunc && PyCallable_Check(pFunc)) {
 		PyObject *pRet = NULL;
 
+
 		pArgs = PyTuple_New(2);
+		/*
+		 * INCREF server_obj as PyTuple_SetItem steals its reference.
+		 * A stolen reference here means that pArgs owns the reference,
+		 * and so when pArgs gets DECREFed, so does server_obj.
+		 * INCREF server_obj so it remains intact after DECREFing pArgs.
+		 *
+		 * Note: no INCREF is needed for the second arg, as the thing
+		 * which gets DECREFed via pArgs is an interim python object
+		 * created from a native C string.
+		 */
+		Py_INCREF(server_obj);
 		PyTuple_SetItem(pArgs, 0, server_obj);
 		PyTuple_SetItem(pArgs, 1, PyString_FromString(path));
 
 		pRet = PyObject_CallObject(pFunc, pArgs);
-		/*
-		 * We intentionally don't do a DECREF on
-		 * pArgs because it somehow decrements a
-		 * reference count on the server_obj which
-		 * results in it being garbage collected
-		 */
+		Py_DECREF(pArgs);
 		if (pRet != NULL) {
 			Py_ssize_t list_ln = PyList_Size(pRet);
 			Py_ssize_t i;
@@ -187,28 +261,13 @@
 			/* pass number of list elements to the caller */
 			*len = (int)list_ln;
 
-			/*
-			 * XXX this memory needs to be freed --
-			 * where might that be?
-			 */
 			if (list_ln > 0) {
-				rv = malloc(list_ln * sizeof (char *));
+				rv = malloc((list_ln + 1) * sizeof (char *));
 				for (i = 0; i < list_ln; i++) {
 					item = PyList_GetItem(pRet, i);
-					rv[i] = PyString_AsString(item);
-					/*
-					 * We intentionally don't do a DECREF
-					 * on item here because it somehow
-					 * results in the value in rv[i] being
-					 * garbage collected. So, if rv[i] is
-					 * passed as an argument to the transfer
-					 * module it keels over as it tries to
-					 * dereference rv[i]
-					 *
-					 * XXX needs investigation longer term
-					 * Py_DECREF(item);
-					 */
+					rv[i] = strdup(PyString_AsString(item));
 				}
+				rv[list_ln] = NULL;
 			}
 			else
 				rv = NULL;
@@ -236,6 +295,25 @@
 }
 
 /*
+ * Free up memory associated with lists returned from ai_get_manifest_values()
+ */
+void
+ai_free_manifest_value_list(char **value_list)
+{
+	char **curr;
+
+	if (value_list == NULL) {
+		return;
+	}
+
+	for (curr = value_list; *curr != NULL; curr++) {
+		free (*curr);
+	}
+
+	free(value_list);
+}
+
+/*
  * This function must be called to delete all
  * state created by ai_create_manifestserv
  */
--- a/usr/src/cmd/auto-install/default.xml	Fri May 07 11:46:54 2010 -0700
+++ b/usr/src/cmd/auto-install/default.xml	Wed May 12 22:18:21 2010 -0700
@@ -18,12 +18,20 @@
 
 CDDL HEADER END
 
-Copyright (c) 2008, 2010, Oracle and/or its affiliates. All rights reserved. 
+Copyright (c) 2008, 2010, Oracle and/or its affiliates. All rights reserved.
 -->
 
 <ai_criteria_manifest>
     <ai_embedded_manifest>
         <ai_manifest name="default">
+	    <!--
+                Add missing driver packages to a booted install image so an
+                installation can complete.  Add packages to target as well.
+                <searchall> searches and installs from configured repo.
+            -->
+            <ai_add_drivers>
+                <searchall/>
+            </ai_add_drivers>
             <ai_pkg_repo_default_publisher>
                 <main url="http://pkg.opensolaris.org/release" publisher="opensolaris.org"/>
                 <mirror url=""/>
--- a/usr/src/cmd/distro_const/auto_install/ai_sparc_image.xml	Fri May 07 11:46:54 2010 -0700
+++ b/usr/src/cmd/distro_const/auto_install/ai_sparc_image.xml	Wed May 12 22:18:21 2010 -0700
@@ -295,6 +295,7 @@
 			<pkg name="pkg:/developer/macro/cpp"/>
 			<pkg name="pkg:/developer/object-file"/>
 			<pkg name="pkg:/diagnostic/cpu-counters"/>
+			<pkg name="pkg:/diagnostic/ddu/library"/>
 			<pkg name="pkg:/driver/cpu/us"/>
 			<pkg name="pkg:/driver/crypto/n2cp"/>
 			<pkg name="pkg:/driver/crypto/tpm"/>
@@ -593,13 +594,19 @@
 			<base_include type="dir">devices</base_include>
 			<base_include type="dir">root</base_include>
 			<base_include type="dir">jack</base_include>
+			<base_include type="dir">etc</base_include>
 			<base_include type="dir">var/svc/manifest</base_include>
 			<base_include type="dir">var/svc/profile</base_include>
-			<base_include type="file">var/pkg/cfg_cache</base_include>
-			<base_include type="dir">etc</base_include>
+			<base_include type="dir">var/pkg</base_include>
+			<base_include type="dir">var/sadm</base_include>
+			<base_include type="file" fiocompress="false">var/pkg/cfg_cache</base_include>
 			<base_include type="file" fiocompress="false">etc/svc/repository.db</base_include>
 			<base_include type="file" fiocompress="false">etc/dev/.devfsadm_dev.lock</base_include>
 			<base_include type="file" fiocompress="false">etc/name_to_major</base_include>
+			<base_include type="file" fiocompress="false">etc/minor_perm</base_include>
+			<base_include type="file" fiocompress="false">etc/driver_aliases</base_include>
+			<base_include type="file" fiocompress="false">etc/driver_classes</base_include>
+			<base_include type="file" fiocompress="false">etc/path_to_inst</base_include>
 			<base_include type="file" fiocompress="false">etc/default/init</base_include>
 			<base_include type="file" fiocompress="false">etc/nsswitch.conf</base_include>
 			<base_include type="file" fiocompress="false">etc/passwd</base_include>
--- a/usr/src/cmd/distro_const/auto_install/ai_x86_image.xml	Fri May 07 11:46:54 2010 -0700
+++ b/usr/src/cmd/distro_const/auto_install/ai_x86_image.xml	Wed May 12 22:18:21 2010 -0700
@@ -310,6 +310,7 @@
 			<pkg name="pkg:/developer/macro/cpp"/>
 			<pkg name="pkg:/developer/object-file"/>
 			<pkg name="pkg:/diagnostic/cpu-counters"/>
+			<pkg name="pkg:/diagnostic/ddu/library"/>
 			<pkg name="pkg:/driver/crypto/tpm"/>
 			<pkg name="pkg:/driver/firewire"/>
 			<pkg name="pkg:/driver/graphics/agpgart"/>
@@ -659,10 +660,15 @@
 			<base_include type="dir">devices</base_include>
 			<base_include type="dir">root</base_include>
 			<base_include type="dir">jack</base_include>
+			<base_include type="dir">etc</base_include>
 			<base_include type="dir">var/svc/manifest</base_include>
 			<base_include type="dir">var/svc/profile</base_include>
-			<base_include type="file">var/pkg/cfg_cache</base_include>
-			<base_include type="dir">etc</base_include>
+			<base_include type="dir">var/pkg</base_include>
+			<base_include type="dir">var/sadm</base_include>
+
+			<base_exclude type="dir">var/pkg/pkg</base_exclude>
+			<base_exclude type="dir">var/pkg/history</base_exclude>
+			<base_exclude type="dir">var/sadm/pkg</base_exclude>
 		</boot_archive_contents>
 	</img_params>
 </distribution>
--- a/usr/src/cmd/distro_const/slim_cd/all_lang_slim_cd_x86.xml	Fri May 07 11:46:54 2010 -0700
+++ b/usr/src/cmd/distro_const/slim_cd/all_lang_slim_cd_x86.xml	Wed May 12 22:18:21 2010 -0700
@@ -443,12 +443,16 @@
 			<base_include type="dir">jack</base_include>
 			<base_include type="dir">var/svc/manifest</base_include>
 			<base_include type="dir">var/svc/profile</base_include>
-			<base_include type="file">var/pkg/cfg_cache</base_include>
+			<base_include type="dir">var/pkg</base_include>
+			<base_include type="dir">var/sadm</base_include>
 			<base_include type="dir">etc</base_include>
 			<base_exclude type="dir">etc/gconf</base_exclude>
 			<base_exclude type="dir">etc/brltty</base_exclude>
 			<base_exclude type="dir">etc/gtk-2.0</base_exclude>
 			<base_exclude type="dir">etc/notices</base_exclude>
+			<base_exclude type="dir">var/pkg/pkg</base_exclude>
+			<base_exclude type="dir">var/pkg/history</base_exclude>
+			<base_exclude type="dir">var/sadm/pkg</base_exclude>
 		</boot_archive_contents>
 	</img_params>
 	<key_value_pairs>
--- a/usr/src/cmd/distro_const/slim_cd/slim_cd_x86.xml	Fri May 07 11:46:54 2010 -0700
+++ b/usr/src/cmd/distro_const/slim_cd/slim_cd_x86.xml	Wed May 12 22:18:21 2010 -0700
@@ -438,12 +438,16 @@
 			<base_include type="dir">jack</base_include>
 			<base_include type="dir">var/svc/manifest</base_include>
 			<base_include type="dir">var/svc/profile</base_include>
-			<base_include type="file">var/pkg/cfg_cache</base_include>
+			<base_include type="dir">var/pkg</base_include>
+			<base_include type="dir">var/sadm</base_include>
 			<base_include type="dir">etc</base_include>
 			<base_exclude type="dir">etc/gconf</base_exclude>
 			<base_exclude type="dir">etc/brltty</base_exclude>
 			<base_exclude type="dir">etc/gtk-2.0</base_exclude>
 			<base_exclude type="dir">etc/notices</base_exclude>
+			<base_exclude type="dir">var/pkg/pkg</base_exclude>
+			<base_exclude type="dir">var/pkg/history</base_exclude>
+			<base_exclude type="dir">var/sadm/pkg</base_exclude>
 		</boot_archive_contents>
 	</img_params>
 	<key_value_pairs>
--- a/usr/src/cmd/distro_const/text_install/text_mode_sparc.xml	Fri May 07 11:46:54 2010 -0700
+++ b/usr/src/cmd/distro_const/text_install/text_mode_sparc.xml	Wed May 12 22:18:21 2010 -0700
@@ -166,6 +166,10 @@
 					<profile path="/var/svc/profile/ns_files.xml"
 					         use_build_sys_file="false"/>
 				</smf_service_profile>
+				<!--
+				Needed so pkgadd will run for Driver Update.
+				-->
+				<size_pad_mb>5</size_pad_mb>
 			</boot_archive>
 		</output_image>
 	</distro_constr_params>
@@ -279,6 +283,7 @@
 			<pkg name="pkg:/developer/macro/cpp"/>
 			<pkg name="pkg:/developer/object-file"/>
 			<pkg name="pkg:/diagnostic/cpu-counters"/>
+			<pkg name="pkg:/diagnostic/ddu/text"/>
 			<pkg name="pkg:/diagnostic/powertop"/>
 			<pkg name="pkg:/diagnostic/scanpci"/>
 			<pkg name="pkg:/diagnostic/top"/>
@@ -753,14 +758,19 @@
 			<base_include type="dir">devices</base_include>
 			<base_include type="dir">root</base_include>
 			<base_include type="dir">jack</base_include>
+			<base_include type="dir">etc</base_include>
 			<base_include type="dir">var/svc/manifest</base_include>
 			<base_include type="dir">var/svc/profile</base_include>
-			<base_include type="dir">var/pkg/catalog</base_include>
-			<base_include type="file">var/pkg/cfg_cache</base_include>
-			<base_include type="dir">etc</base_include>
+			<base_include type="dir">var/pkg</base_include>
+			<base_include type="dir">var/sadm</base_include>
+			<base_include type="file" fiocompress="false">var/pkg/cfg_cache</base_include>
 			<base_include type="file" fiocompress="false">etc/svc/repository.db</base_include>
 			<base_include type="file" fiocompress="false">etc/dev/.devfsadm_dev.lock</base_include>
 			<base_include type="file" fiocompress="false">etc/name_to_major</base_include>
+			<base_include type="file" fiocompress="false">etc/minor_perm</base_include>
+			<base_include type="file" fiocompress="false">etc/driver_aliases</base_include>
+			<base_include type="file" fiocompress="false">etc/driver_classes</base_include>
+			<base_include type="file" fiocompress="false">etc/path_to_inst</base_include>
 			<base_include type="file" fiocompress="false">etc/default/init</base_include>
 			<base_include type="file" fiocompress="false">etc/nsswitch.conf</base_include>
 		</boot_archive_contents>
--- a/usr/src/cmd/distro_const/text_install/text_mode_x86.xml	Fri May 07 11:46:54 2010 -0700
+++ b/usr/src/cmd/distro_const/text_install/text_mode_x86.xml	Wed May 12 22:18:21 2010 -0700
@@ -180,6 +180,10 @@
 					<profile path="/var/svc/profile/ns_files.xml"
 					         use_build_sys_file="false"/>
 				</smf_service_profile>
+				<!--
+				Needed so pkgadd will run for Driver Update.
+				-->
+				<size_pad_mb>5</size_pad_mb>
 			</boot_archive>
 		</output_image>
 	</distro_constr_params>
@@ -292,6 +296,7 @@
 			<pkg name="pkg:/developer/macro/cpp"/>
 			<pkg name="pkg:/developer/object-file"/>
 			<pkg name="pkg:/diagnostic/cpu-counters"/>
+			<pkg name="pkg:/diagnostic/ddu/text"/>
 			<pkg name="pkg:/diagnostic/powertop"/>
 			<pkg name="pkg:/diagnostic/scanpci"/>
 			<pkg name="pkg:/diagnostic/top"/>
@@ -754,13 +759,16 @@
 			<base_include type="dir">jack</base_include>
 			<base_include type="dir">var/svc/manifest</base_include>
 			<base_include type="dir">var/svc/profile</base_include>
-			<base_include type="dir">var/pkg/catalog</base_include>
-			<base_include type="file">var/pkg/cfg_cache</base_include>
+			<base_include type="dir">var/pkg</base_include>
+			<base_include type="dir">var/sadm</base_include>
 			<base_include type="dir">etc</base_include>
 			<base_exclude type="dir">etc/gconf</base_exclude>
 			<base_exclude type="dir">etc/brltty</base_exclude>
 			<base_exclude type="dir">etc/gtk-2.0</base_exclude>
 			<base_exclude type="dir">etc/notices</base_exclude>
+			<base_exclude type="dir">var/pkg/pkg</base_exclude>
+			<base_exclude type="dir">var/pkg/history</base_exclude>
+			<base_exclude type="dir">var/sadm/pkg</base_exclude>
 		</boot_archive_contents>
 	</img_params>
 	<key_value_pairs>
--- a/usr/src/cmd/slim-install/svc/Makefile	Fri May 07 11:46:54 2010 -0700
+++ b/usr/src/cmd/slim-install/svc/Makefile	Wed May 12 22:18:21 2010 -0700
@@ -19,12 +19,10 @@
 # CDDL HEADER END
 #
 #
-# Copyright 2010 Sun Microsystems, Inc.  All rights reserved.
-# Use is subject to license terms.
-#
+# Copyright (c) 2008, 2010, Oracle and/or its affiliates. All rights reserved.
 #
 
-SYSMANIFESTSRC=	live-sysidtool.xml live-io-tracing.xml
+SYSMANIFESTSRC=	live-sysidtool.xml live-io-tracing.xml live-var-pkg-move.xml
 
 SYSFSMANIFESTSRC=	live-root-fs.xml live-usr-fs.xml \
 			live-root-fs-minimal.xml
@@ -33,7 +31,7 @@
                 
 SVCMETHODSRC=	media-fs-root live-fs-root-minimal \
 		live-sysidtool-system  live-io-tracing live-a11y \
-		net-fs-root
+		net-fs-root live-var-pkg-move
 
 SVCSHARESRC=	live_fs_include.sh
 
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/usr/src/cmd/slim-install/svc/live-var-pkg-move	Wed May 12 22:18:21 2010 -0700
@@ -0,0 +1,73 @@
+#!/sbin/sh
+#
+# 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.
+#
+# 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 (c) 2010, Oracle and/or its affiliates. All rights reserved.
+
+# Copy /var/pkg to /tmp, to allow pkg(5) utilities to expand catalogs without
+# running out of boot_archive space.  Copy symlinks as symlinks.
+# Free unneeded space used in boot_archive.
+
+# Copy the /var/pkg directory to /tmp/pkg.
+
+. /lib/svc/share/smf_include.sh
+
+OLD_DIR=/var/pkg
+NEW_DIR=/tmp/pkg
+
+cd $OLD_DIR
+if [ $PWD != $OLD_DIR  ] ; then
+	echo "Error finding $OLD_DIR" > /dev/msglog
+
+	exit $SMF_EXIT_ERR_FATAL
+fi
+
+mkdir $NEW_DIR
+if [ $? -ne 0 ] ; then
+	echo "Error creating $NEW_DIR" > /dev/msglog
+
+	exit $SMF_EXIT_ERR_FATAL
+fi
+
+find . -depth -print | cpio -pdum $NEW_DIR
+if [ $? -ne 0 ] ; then
+	echo "Error copying $OLD_DIR contents to $NEW_DIR" > /dev/msglog
+
+	exit $SMF_EXIT_ERR_FATAL
+fi
+
+cd /
+rm -rf $OLD_DIR
+if [ $? -ne 0 ] ; then
+	echo "Error removing original $OLD_DIR tree" > /dev/msglog
+
+	exit $SMF_EXIT_ERR_FATAL
+fi
+
+ln -s $NEW_DIR $OLD_DIR
+if [ $? -ne 0 ] ; then
+	echo "Error making symlink from $OLD_DIR to $NEW_DIR" > /dev/msglog
+
+	exit $SMF_EXIT_ERR_FATAL
+fi
+
+exit $SMF_EXIT_OK
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/usr/src/cmd/slim-install/svc/live-var-pkg-move.xml	Wed May 12 22:18:21 2010 -0700
@@ -0,0 +1,95 @@
+<?xml version="1.0"?>
+<!DOCTYPE service_bundle SYSTEM "/usr/share/lib/xml/dtd/service_bundle.dtd.1">
+<!--
+ Copyright (c) 2010, Oracle and/or its affiliates. All rights reserved.
+
+ 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.
+
+ 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
+-->
+
+<service_bundle type='manifest' name='live-var-pkg-move'>
+
+<service
+	name='system/live-var-pkg-move'
+	type='service'
+	version='1'>
+
+	<create_default_instance enabled='true' />
+
+	<!--
+	  We depend on filesystem/minimal so that /tmp is available.
+	-->
+	<dependency name='fs'
+	    grouping='require_all'
+	    restart_on='none'
+	    type='service'>
+		<service_fmri
+		    value='svc:/system/filesystem/minimal:default' />
+	</dependency>
+
+	<dependent
+		name='lvpm-system-install-setup'
+		grouping='require_all'
+		restart_on='none'>
+		<service_fmri value='svc:/system/install-setup' />
+	</dependent>
+
+	<dependent
+		name='lvpm-system-console-login'
+		grouping='require_all'
+		restart_on='none'>
+		<service_fmri value='svc:/system/console-login' />
+	</dependent>
+
+	<exec_method
+		type='method'
+		name='start'
+		exec='/lib/svc/method/live-var-pkg-move'
+		timeout_seconds='60' />
+
+	<exec_method
+		type='method'
+		name='stop'
+		exec=':true'
+		timeout_seconds='0' />
+
+	<property_group name='startd' type='framework'>
+		<propval name='duration' type='astring' value='transient' />
+	</property_group>
+
+	<stability value='Unstable' />
+
+	<template>
+		<common_name>
+			<loctext xml:lang='C'>
+				Move /var/pkg contents to /tmp/pkg
+			</loctext>
+		</common_name>
+		<description>
+			<loctext xml:lang='C'>
+				Intended for live images, this service moves
+				/var/pkg to /tmp/pkg to allow for pkg catalog
+				expansion.
+			</loctext>
+		</description>
+	</template>
+</service>
+
+</service_bundle>
--- a/usr/src/cmd/slim-install/user/jack/Makefile	Fri May 07 11:46:54 2010 -0700
+++ b/usr/src/cmd/slim-install/user/jack/Makefile	Wed May 12 22:18:21 2010 -0700
@@ -19,12 +19,10 @@
 # CDDL HEADER END
 #
 #
-# Copyright 2010 Sun Microsystems, Inc.  All rights reserved.
-# Use is subject to license terms.
-#
+# Copyright (c) 2008, 2010, Oracle and/or its affiliates. All rights reserved.
 #
 
-JACKCONFIGAUTOFILESRC = iotrace.desktop
+JACKCONFIGAUTOFILESRC = iotrace.desktop ddu_silent.desktop
 JACKFILESRC644 = xscreensaver xorg.conf.vesa
 JACKFILESRC755 = Xclients local.bashrc local.profile
 JACKDESKTOPFILESRC = 	install_opensolaris.desktop ddu.desktop \
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/usr/src/cmd/slim-install/user/jack/ddu_silent.desktop	Wed May 12 22:18:21 2010 -0700
@@ -0,0 +1,8 @@
+[Desktop Entry]
+Name=Device Driver Utility (silent mode)
+Type=Application
+Terminal=false
+Encoding=UTF-8
+Version=1.0
+Exec=/usr/ddu/ddu.py --silent
+X-GNOME-Autostart-enabled=true
--- a/usr/src/cmd/text-install/svc/text-mode-menu.ksh	Fri May 07 11:46:54 2010 -0700
+++ b/usr/src/cmd/text-install/svc/text-mode-menu.ksh	Wed May 12 22:18:21 2010 -0700
@@ -48,6 +48,10 @@
 	cmds=("/usr/bin/text-install")					 \
 	do_subprocess="true"						 \
 	msg_str="")							 \
+    (menu_str=`gettext "Install Additional Drivers"`			 \
+	cmds=("/usr/bin/ddu-text")					 \
+	do_subprocess="true"						 \
+	msg_str="")							 \
     (menu_str=`gettext "Shell"`						 \
 	cmds=("$ROOT_SHELL")						 \
 	do_subprocess="true"						 \
--- a/usr/src/lib/install_utils/ManifestServ.py	Fri May 07 11:46:54 2010 -0700
+++ b/usr/src/lib/install_utils/ManifestServ.py	Wed May 12 22:18:21 2010 -0700
@@ -79,11 +79,12 @@
     DEFVAL_XML_SUFFIX = ".defval.xml"
 
     # ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
-    def __init__(self, manifest, valfile_base=None, out_manifest=None,
-                 verbose=False, keep_temp_files=False):
+    def __init__(self, manifest_name, valfile_base=None,
+                 out_manifest_name=None, verbose=False,
+                 keep_temp_files=False, full_init=True):
     # ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
-        """ Constructor.  Validate and initialize all XML data for
-            retrieval from an in-memory tree.
+        """ Constructor.  Initialize the in-memory data tree.  Take care
+            of other initialization tasks if full_init = True.
 
         Validation and initialization consists of the following steps:
         - Initialize and validate defaults/content-validation tree.
@@ -99,21 +100,25 @@
         Note: Socket server is not started from this method.
 
         Args:
-          manifest: Name of the project manifest file.  If it doesn't
-            have a suffix, one will be appended.  Names of the
-            schema and defaults/content-validation manifest files
-            are keyed off the basename of this file (without the
-            suffix).
+          manifest_name: Name of the project manifest file.  If it
+            doesn't have a suffix, one will be appended.  Default names
+            of the schema and defaults/content-validation manifest
+            files are keyed off the basename of this file (without
+            the suffix).
+
+          full_init: (optional): if True, the data is read into memory, and
+            data processing (verification or default setting) is done.
+            If False, no data processing is done.  Defaults to True.
 
-        valfile_base: rootname (excluding suffix) of the defval
-            manifest XML and manifest schema files.  defval-manifest
-            will be called <valfile_base>.defval.xml and manifest
-            schema will be called <valfile_base>.rng valfile_base
-            may contain prepended directory structure.  If given as
-            None, valfile_base will take <manifest> as its value.
+          valfile_base: (optional): rootname (excluding suffix) of the
+            defval manifest XML and manifest schema files.  defval-manifest
+            will be called <valfile_base>.defval.xml and manifest schema
+            will be called <valfile_base>.rng valfile_base may contain
+            prepended directory structure.  If given as None,
+            valfile_base will take <manifest> as its value.
 
-          out_manifest: (optional): Name of the nicely-formatted output
-            manifest file.  Defaults to None if not provided.
+          out_manifest_name: (optional): Name of the nicely-formatted
+            output manifest file.  Defaults to None if not provided.
 
           verbose: (optional): When True, enables on-screen printout of
             defaults, content validation and schema validation.
@@ -123,7 +128,9 @@
             file around after termination.  Default is False, to
             delete the temporary file.
 
-        Raises: 
+        Raises:
+          - TreeAccError exceptions during initialization of the project
+            manifest data tree.
           - Exceptions during initialization of the
             defaults/content-validation tree.
           - Exceptions during initialization of the project manifest
@@ -138,98 +145,128 @@
         """
     # ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
 
-        self.socket_debug = False
+        self.defval_tree = None
+        self.manifest_tree = None
 
-        # Save verbose setting as other methods use it too.
-        self.verbose = verbose
+        # Set up defaults for ancillary files.
+        manifest_name = manifest_name.strip()
+        if (manifest_name.endswith(ManifestServ.XML_SUFFIX)):
 
-        # Strip the suffix from the manifest file name, to get the base
-        # name used for temporary file and maybe for other files used
-        # in procsessing the manifest.
-        full_manifest_basename = manifest.replace(ManifestServ.XML_SUFFIX, "")
+            # Strip the suffix from the manifest file name, to get the base
+            # name used for temporary file and maybe for other files used
+            # in procsessing the manifest.
+            full_manifest_basename = \
+                manifest_name.replace(ManifestServ.XML_SUFFIX, "")
+        else:
+            full_manifest_basename = manifest_name
+            manifest_name += ManifestServ.XML_SUFFIX
+
+        # Initialize the project manifest data tree.
+        try:
+            self.manifest_tree = TreeAcc(manifest_name)
+        except TreeAccError:
+            print >> sys.stderr, "Error instantiating manifest tree:"
+            raise
 
         # Initialize default for valfile_base, if necessary.
         if (valfile_base is None):
             valfile_base = full_manifest_basename
+        self.valfile_base = valfile_base
 
         # Schema and defval_manifest root names are taken from
         # valfile_base.
-        schema = valfile_base + ManifestServ.SCHEMA_SUFFIX
-        defval_manifest = (valfile_base + ManifestServ.DEFVAL_XML_SUFFIX)
-
-        # Get process ID in string form, to use in file- and
-        # socket-names.
-        strpid = str(os.getpid())
+        self.schema_name = valfile_base + ManifestServ.SCHEMA_SUFFIX
+        self.defval_manifest_name = valfile_base + \
+            ManifestServ.DEFVAL_XML_SUFFIX
 
         # Create a new string without any prepended directories
         # from before the basename.  This will be used in creation
         # of the temporary filename.
         manifest_basename = full_manifest_basename.rsplit("/")[-1]
 
+        # Get process ID in string form, to use in file- and socket-names.
+        self.strpid = str(os.getpid())
+
         # This is name of temporary file, that includes defaults,
         # before reformatting.
-        temp_manifest = ("/tmp/" + manifest_basename + "_temp_" + strpid +
-                         ManifestServ.XML_SUFFIX)
+        self.temp_manifest_name = ("/tmp/" + manifest_basename + "_temp_" +
+                                   self.strpid + ManifestServ.XML_SUFFIX)
+
+        self.out_manifest_name = out_manifest_name
+        self.verbose = verbose
+        self.keep_temp_files = keep_temp_files
 
         # Do this here in case cleanup() is called without
         # start_socket_server() having been called first.
-        self.listen_sock_name = ("/tmp/ManifestServ." + strpid)
+        self.listen_sock_name = ("/tmp/ManifestServ." + self.strpid)
         self.listen_sock = None    # Filled in by start_server()
         self.server_run = False
+        self.socket_debug = False
 
-        # Initalize and validate the defaults/content-validation tree.
-        try:
-            defval_tree = init_defval_tree(defval_manifest)
-        except ManifestProcError, err:
-            print >> sys.stderr, ("Error initializing " +
-                                  "defaults/content-validation tree:")
-            print >> sys.stderr, str(err)
-            raise
+        # Preprocess if full_init is specified.
+        if (full_init):
+            self.set_defaults(self.defval_manifest_name,
+                              self.temp_manifest_name, self.verbose,
+                              self.keep_temp_files)
+            self.schema_validate(self.schema_name, self.temp_manifest_name,
+                                 self.out_manifest_name, self.verbose,
+                                 self.keep_temp_files)
+            self.semantic_validate(self.defval_manifest_name,
+                                   self.temp_manifest_name, self.verbose,
+                                   self.keep_temp_files)
 
-        # Initialize the project manifest data tree.
-        try:
-            self.manifest_tree = TreeAcc(manifest)
-        except TreeAccError, err:
-            print >> sys.stderr, "Error instantiating manifest tree:"
-            print >> sys.stderr, str(err)
-            raise
+
+    # ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+    def schema_validate(self, schema_name=None, temp_manifest_name=None,
+                        out_manifest_name=None, verbose=None,
+                        keep_temp_files=None):
+    # ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+        """ Validate manifest against the given schema
 
-        # Add defaults to the project manifest data tree.
-        try:
-            add_defaults(self.manifest_tree, defval_tree, verbose)
-        except (KeyError, ManifestProcError), err:
-            print >> sys.stderr, "Error adding defaults to manifest tree"
-            print >> sys.stderr, str(err)
+        Args:
+          schema_name: (optional): Filename of the schema to validate
+            against.  If not supplied here, the default name of
+            <manifest_name>.rng is used.
 
-            # Create temp manifest for debugging
-            if (keep_temp_files):
-                self.__save_tree(temp_manifest)
-            raise
+          temp_manifest_name: (optional): Name of the (temporary) manifest
+            file to validate.  If not supplied here, the default name of
+            /tmp/<manifest_name>_temp<PID>.xml is used.
 
-        # Do semantic / content validation on the project manifest
-        # data tree.
-        try:
-            validate_content(self.manifest_tree, defval_tree, verbose)
-        except (KeyError, ManifestProcError), err:
-            print >> sys.stderr, "Error validating manifest tree content:"
-            print >> sys.stderr, str(err)
+          out_manifest_name: (optional): Filename to write out a
+            nicely-formatted manifest.  If not supplied here, the name
+            specified in the constructor is used.
+
+          verbose: boolean: True = extra messages.  If not supplied here,
+            the value specified in the constructor is used.
 
-            # Create temp manifest for debugging
-            if (keep_temp_files):
-                self.__save_tree(temp_manifest)
-            raise
+          keep_temp_files: boolean: True = Do not delete the temp_manifest.
+            If the value is not supplied here, the value specified in the
+            constructor is used.
 
+        """
+    # ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
         # Validate the project manifest data tree against its schema.
         # Save a nicely-formatted copy if out_manifest is specified.
-        self.__save_tree(temp_manifest)
 
+        if (schema_name is None):
+            schema_name = self.schema_name
+        if (temp_manifest_name is None):
+            temp_manifest_name = self.temp_manifest_name
+        if (out_manifest_name is None):
+            out_manifest_name = self.out_manifest_name
+        if (verbose is None):
+            verbose = self.verbose
+        if (keep_temp_files is None):
+            keep_temp_files = self.keep_temp_files
+	
         # Pylint bug: See http://www.logilab.org/ticket/8764
         # pylint: disable-msg=C0321
         try:
-            schema_validate(schema, temp_manifest, out_manifest)
+            self.__save_tree(temp_manifest_name)
+            schema_validate(schema_name, temp_manifest_name, out_manifest_name)
         except ManifestProcError, err:
             print >> sys.stderr, ("Error validating " +
-                                  "manifest against schema " + schema)
+                                  "manifest against schema " + schema_name)
             print >> sys.stderr, str(err)
             raise
 
@@ -238,8 +275,161 @@
         finally:
             if (not keep_temp_files):
                 if (verbose):
-                    print ("Removing temporary file: " + temp_manifest)
-                os.unlink(temp_manifest)
+                    print ("Removing temporary file: " + temp_manifest_name)
+                os.unlink(temp_manifest_name)
+
+
+    # ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+    def __load_defval_tree__(self, defval_manifest_name):
+    # ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+        """ Initialize and validate the defaults/content validation tree
+        if not already done.
+
+        Args:
+          defval_manifest_name: Name of the defaults/content-validation
+            (defval) manifest file.
+
+        Returns: None
+
+        Raises:
+          ManifestProcError: Error initializing defaults/content-validation
+            tree.
+
+        """
+    # ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+        if (self.defval_tree is None):
+
+            # Initialize and validate the defaults/content-validation tree.
+            try:
+                self.defval_tree = init_defval_tree(defval_manifest_name)
+            except ManifestProcError, err:
+                print >> sys.stderr, ("Error initializing defaults/" +
+                                     "content-validation tree")
+                print >> sys.stderr, str(err)
+                raise
+
+
+    # ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+    def set_defaults(self, defval_manifest_name=None,
+                     temp_manifest_name=None, verbose=None,
+                     keep_temp_files=None):
+    # ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+        """ Set defaults into the (project) data tree.  Defaults defined by
+        the defval_manifest.
+
+        Args:
+          defval_manifest_name: Filename of the defaults/content-validation
+            (defval) manifest.  Used only if defval_manifest is not already
+            opened.  If not supplied here, the default name of
+            <manifest_name>.defval.xml is used.  No verification of consistency
+            between this defval_manifest_name and a defval_manifest file which
+            is already opened.
+
+          temp_manifest_name: Name of the (temporary) manifest file.  Can
+            be used to verify setting of defaults.  If not supplied here, the
+            default name of /tmp/<manifest_name>_temp<PID>.xml is used.
+
+          keep_temp_files: boolean: True = Do not delete the temp_manifest.
+            If the value is not supplied here, the value specified in the
+            constructor is used.
+
+          verbose: boolean: True = extra messages.  If the value is not
+            supplied here, the value specified in the constructor is used.
+
+        Returns: None
+
+        Raises:
+          KeyError: As raised from add_defaults()
+          ManifestProcError: As raised from add_defaults()
+
+        """
+    # ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+        if (defval_manifest_name is None):
+            defval_manifest_name = self.defval_manifest_name
+        if (temp_manifest_name is None):
+            temp_manifest_name = self.temp_manifest_name
+        if (verbose is None):
+            verbose = self.verbose
+        if (keep_temp_files is None):
+            keep_temp_files = self.keep_temp_files
+
+        # Stores self.defval_tree on success
+        self.__load_defval_tree__(defval_manifest_name)
+
+        # Add defaults to the project manifest data tree.
+        try:
+            add_defaults(self.manifest_tree, self.defval_tree, verbose)
+        except (KeyError, ManifestProcError), err:
+            print >> sys.stderr, "Error adding defaults to manifest tree"
+            print >> sys.stderr, str(err)
+
+            # Create temp manifest for debugging
+            if (keep_temp_files):
+                self.__save_tree(temp_manifest_name)
+            raise
+
+
+    # ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+    def semantic_validate(self, defval_manifest_name=None,
+                          temp_manifest_name=None, verbose=None,
+                          keep_temp_files=None):
+    # ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+        """ Perform semantic validation of the (project) data tree.
+        Validation tasks defined by the defval_manifest.
+
+        Args:
+          defval_manifest_name: Filename of the defaults/content-validation
+            (defval) manifest.  Used only if defval_manifest is not already
+            opened.  If not supplied here, the default name of
+            <manifest_name>.defval.xml is used.  No verification of consistency
+            between this defval_manifest_name and a defval_manifest file which
+            is already opened.
+
+          temp_manifest_name: Name of the (temporary) manifest file.  Can
+            be used to double-check validation.  If not supplied here, the
+            default name of /tmp/<manifest_name>_temp<PID>.xml is used.
+
+          keep_temp_files: boolean: True = Do not delete the temp_manifest.
+           If the value is not supplied here, the value specified in the
+            constructor is used.
+
+          verbose: boolean: True = extra messages.  If not supplied here,
+            the value specified in the constructor is used.
+
+        Returns: None
+
+        Raises:
+          KeyError: As raised from add_defaults()
+          ManifestProcError: As raised from add_defaults()
+
+        """
+    # ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+        if (defval_manifest_name is None):
+            defval_manifest_name = self.defval_manifest_name
+        if (temp_manifest_name is None):
+            temp_manifest_name = self.temp_manifest_name
+        if (verbose is None):
+            verbose = self.verbose
+        if (keep_temp_files is None):
+            keep_temp_files = self.keep_temp_files
+
+        # Stores self.defval_tree on success
+        self.__load_defval_tree__(defval_manifest_name)
+
+        # Do semantic / content validation on the project manifest
+        # data tree.
+        try:
+            validate_content(self.manifest_tree, self.defval_tree, verbose)
+        except (KeyError, ManifestProcError), err:
+            print >> sys.stderr, "Error validating manifest tree content:"
+            print >> sys.stderr, str(err)
+
+            # Create temp manifest for debugging
+            if (keep_temp_files):
+                self.__save_tree(temp_manifest_name)
+            raise
 
 
     # ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
@@ -252,7 +442,9 @@
 
         Returns: none
 
-        Raises: none
+        Raises:
+          FileOpenError: Could not open save_manifest file
+          FileSaveError: Could not save data to save_manifest file
 
         """
     # ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
@@ -262,6 +454,7 @@
             print >> sys.stderr, ("Error saving temporary manifest %s:" %
                                   save_manifest)
             print >> sys.stderr, str(err)
+            raise
 
 
     # ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
@@ -341,7 +534,7 @@
         
 
     # ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
-    def get_values(self, request, is_key=False):
+    def get_values(self, request, is_key=False, verbose=False):
     # ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
         """ Method for the project's main process (which invoked this
             module) to retrieve XML data directly (no sockets).
@@ -356,6 +549,8 @@
             request and submitted.  If false, the request is
             submitted for searching as provided.
 
+           verbose: boolean: if True, print messages
+
         Returns:
            list of string values from nodes which match the nodepath
 
@@ -378,7 +573,7 @@
             else:
                 strlist.extend(space_parse(value))
 
-        if (self.verbose):
+        if (verbose):
             print "get_values: request = \"" + request + "\""
             print (("   %d results found: " % len(strlist)) +
                    str(strlist))
--- a/usr/src/pkgdefs/SUNWslim-utils/prototype_com	Fri May 07 11:46:54 2010 -0700
+++ b/usr/src/pkgdefs/SUNWslim-utils/prototype_com	Wed May 12 22:18:21 2010 -0700
@@ -19,8 +19,7 @@
 # CDDL HEADER END
 #
 #
-# Copyright 2010 Sun Microsystems, Inc.  All rights reserved.
-# Use is subject to license terms.
+# Copyright (c) 2008, 2010, Oracle and/or its affiliates. All rights reserved.
 #
 #
 # This required package information file contains a list of package contents.
@@ -51,6 +50,7 @@
 d none jack/.config 755 jack staff
 d none jack/.config/autostart 755 jack staff
 f none jack/.config/autostart/iotrace.desktop 644 jack staff
+f none jack/.config/autostart/ddu_silent.desktop 644 jack staff
 d none jack/Desktop 755 jack staff
 f none jack/Desktop/ddu.desktop 644 jack staff
 f none jack/Desktop/gparted.desktop 644 jack staff
@@ -67,6 +67,7 @@
 f none lib/svc/method/live-fs-root-minimal 555 root bin
 f none lib/svc/method/live-io-tracing 555 root bin
 f none lib/svc/method/live-sysidtool-system 555 root bin
+f none lib/svc/method/live-var-pkg-move 555 root bin
 f none lib/svc/method/media-fs-root 555 root bin
 f none lib/svc/method/net-fs-root 555 root bin
 d none mnt 755 root sys
@@ -80,6 +81,7 @@
 f none var/svc/manifest/system/live-a11y.xml 444 root sys
 f none var/svc/manifest/system/live-io-tracing.xml 444 root sys
 f none var/svc/manifest/system/live-sysidtool.xml 444 root sys
+f none var/svc/manifest/system/live-var-pkg-move.xml 444 root sys
 f none var/svc/manifest/system/filesystem/live-root-fs.xml 444 root sys
 f none var/svc/manifest/system/filesystem/live-root-fs-minimal.xml 444 root sys
 f none var/svc/manifest/system/filesystem/live-usr-fs.xml 444 root sys