3356 ManifestServ need to be stopped and it's socket file cleaned up
authorJack Schwartz <Jack.A.Schwartz@Sun.COM>
Mon, 13 Oct 2008 11:47:37 -0700
changeset 265 3686c98a3917
parent 264 2b62330f7e65
child 266 ef9d9f2ade3f
3356 ManifestServ need to be stopped and it's socket file cleaned up 3559 list index out of range ERROR seen when using distro_const with bad xml file 3600 Implement a provision in the DC manifest for new grub entries 3730 example manifests lack certain devfsadm link modules 3800 ManifestServ doesn't deal well with manifest entries containing spaces 3851 XML Manifest utility error handling makes its point too many times 3853 More robust handling of ManifestServ socket transactions 3854 ManifestServ test program should tolerate bad interactive queries 3855 New defaults handling of XML nodes with empty-string values 3857 Port accessibility grub menu.lst changes for slim CD to use manifest. 3863 Get rid of range(len(list)) in python Manifest processing files
usr/src/cmd/distro_const/DC-manifest.defval.xml
usr/src/cmd/distro_const/DC-manifest.rng
usr/src/cmd/distro_const/DC_checkpoint.py
usr/src/cmd/distro_const/DC_defs.py
usr/src/cmd/distro_const/DefaultsModule.py
usr/src/cmd/distro_const/ValidatorModule.py
usr/src/cmd/distro_const/auto_install/ai_x86_image.xml
usr/src/cmd/distro_const/distro_const.py
usr/src/cmd/distro_const/slim_cd/Makefile
usr/src/cmd/distro_const/slim_cd/slim_cd.xml
usr/src/cmd/distro_const/slim_cd/slimcd_grub_a11y
usr/src/cmd/distro_const/utils/Makefile
usr/src/cmd/distro_const/utils/grub_setup.py
usr/src/cmd/distro_const/utils/post_bootroot_pkg_image_mod
usr/src/cmd/install-tools/ManifestRead.py
usr/src/cmd/install-tools/ManifestServ.py
usr/src/lib/install_utils/DefValProc.py
usr/src/lib/install_utils/ManifestRead.py
usr/src/lib/install_utils/ManifestServ.py
usr/src/lib/install_utils/SocketServProtocol.py
usr/src/lib/install_utils/TreeAcc.py
usr/src/lib/install_utils/defval-manifest.rng
usr/src/lib/install_utils/install_utils.py
usr/src/pkgdefs/SUNWdistro-const/prototype_com
--- a/usr/src/cmd/distro_const/DC-manifest.defval.xml	Mon Oct 13 18:10:47 2008 +0200
+++ b/usr/src/cmd/distro_const/DC-manifest.defval.xml	Mon Oct 13 11:47:37 2008 -0700
@@ -307,10 +307,9 @@
 
 	<!-- Run val_abspath() on all nodes matching the nodepaths below. -->
 	<validate group="val_abspath">
-		img_params/output_image/bootroot/adjustment/add,
-		img_params/output_image/bootroot/adjustment/delete,
-		img_params/grub_menu.lst,
-		img_params/netservices_SMF_profile,
+		"img_params/output_image/bootroot/adjustment/add"
+		"img_params/output_image/bootroot/adjustment/delete"
+		"img_params/netservices_SMF_profile"
 	</validate>
 
 </DC_manifest_defval>
--- a/usr/src/cmd/distro_const/DC-manifest.rng	Mon Oct 13 18:10:47 2008 +0200
+++ b/usr/src/cmd/distro_const/DC-manifest.rng	Mon Oct 13 11:47:37 2008 -0700
@@ -224,11 +224,10 @@
 				<ref name="nm_user"/>
 			</zeroOrMore>
 
-			<!-- Replacement menu.lst file for grub. -->
-			<optional> <!-- If not spec, use regular menu.lst -->
-				<element name="grub_menu.lst">
-					<text/>	<!-- filepath -->
-				</element>
+			<!-- grub menu modifications. -->
+			<optional>	<!-- If not specified,
+					     use regular menu.lst -->
+				<ref name="nm_grub_menu_mods"/>
 			</optional>
 
 			<!-- SMF profile defining which net services get
@@ -273,6 +272,49 @@
 
 	<!--
 	=======================================================================
+	Grub menu modifications
+	=======================================================================
+	-->
+	<define name="nm_grub_menu_mods">
+		<element name="grub_menu_modifications">
+			<interleave>
+				<optional>
+					<element name="default_entry">
+						<data type="unsignedInt"/>
+					</element>
+				</optional>
+				<optional>
+					<element name="timeout">
+						<data type="unsignedInt"/>
+					</element>
+				</optional>
+				<zeroOrMore>
+					<ref name="nm_grub_entry"/>
+				</zeroOrMore>
+			</interleave>
+		</element>
+	</define>
+
+	<define name="nm_grub_entry">
+		<element name="entry">
+			<optional>
+				<attribute name="position">
+					<data type="unsignedInt"/>
+				</attribute>
+			</optional>
+			<element name="title_suffix">
+				<text/>
+			</element>
+			<oneOrMore>
+				<element name="line">
+					<text/>
+				</element>
+			</oneOrMore>
+		</element>
+	</define>
+
+	<!--
+	=======================================================================
 	Bootroot content specification.  Start with a base list of files
 	to put into the bootroot.  Then adjust this list by adding and deleting
 	individual files as adjustments.
@@ -449,9 +491,7 @@
 			<!-- Args to pass to the script -->
 			<optional>	<!-- Maybe none are needed -->
 				<element name="argslist">
-					<optional>
-						<ref name="nm_list_type"/>
-					</optional>
+					<text/>
 				</element>
 			</optional>
 		</element>
@@ -504,13 +544,13 @@
 			<optional>
 				<attribute name="attrs">
 					<!-- Addl checking needed -->
-					<ref name="nm_list_type"/>
+					<text/>
 				</attribute>
 			</optional>
 			<optional>
 				<attribute name="tags">
 					<!-- Addl checking needed -->
-					<ref name="nm_list_type"/>
+					<text/>
 				</attribute>
 			</optional>
 		</element>
@@ -527,7 +567,7 @@
 			<!-- Rather than a long list of elements, one per
 			     locale, manifest will be easier to
 			     read w/single element w/text list. -->
-			<ref name="nm_list_type"/>
+			<text/>
 		</element>
 		<optional>
 			<element name="locale_defaults">
@@ -688,17 +728,4 @@
 			</element>
 		</optional>
 	</define>
-
-	<!--
-	=======================================================================
-	Generic list.  Used by many locations.
-	=======================================================================
-	-->
-	<define name="nm_list_type">
-		<list>
-			<oneOrMore>
-				<data type="string"/>
-			</oneOrMore>
-		</list>
-	</define>
 </grammar>
--- a/usr/src/cmd/distro_const/DC_checkpoint.py	Mon Oct 13 18:10:47 2008 +0200
+++ b/usr/src/cmd/distro_const/DC_checkpoint.py	Mon Oct 13 11:47:37 2008 -0700
@@ -451,12 +451,8 @@
 	# Args list gets returned as a single string which can contain multiple
 	# args, if the args list exists. Split into individual args, accounting
 	# for the possibility of an empty list.
-	script_args_str = get_manifest_value(manifest_server_obj,
+	script_args = get_manifest_list(manifest_server_obj,
 	    FINALIZER_SCRIPT_NAME_TO_ARGSLIST % script)
-	if (script_args_str != None):
-		script_args = script_args_str.split()
-	else:
-		script_args = []
 
 	finalizer_obj.register(script, script_args) 
 	cp.incr_current_step()
--- a/usr/src/cmd/distro_const/DC_defs.py	Mon Oct 13 18:10:47 2008 +0200
+++ b/usr/src/cmd/distro_const/DC_defs.py	Mon Oct 13 11:47:37 2008 -0700
@@ -92,6 +92,15 @@
 POST_INSTALL_ADD_URL_TO_MIRROR_URL = POST_INSTALL_ADD_AUTH_MAIN + "[url=\"%s\"]/../mirror/url"
 FINALIZER_SCRIPT_NAME_TO_ARGSLIST = FINALIZER_SCRIPT + "[name=\"%s\"]/argslist"
 
+# Grub menu stuff
+GRUB_DATA = IMG_PARAMS + "/grub_menu_modifications"
+GRUB_DEFAULT_ENTRY_NUM = GRUB_DATA + "/default_entry"
+GRUB_DEFAULT_TIMEOUT = GRUB_DATA + "/timeout"
+GRUB_ENTRY = GRUB_DATA + "/entry"
+GRUB_ENTRY_TITLE_SUFFIX = GRUB_ENTRY + "/title_suffix"
+GRUB_ENTRY_POSITION = GRUB_ENTRY + "[title_suffix=\"%s\"]/position"
+GRUB_ENTRY_LINES = GRUB_ENTRY + "[title_suffix=\"%s\"]/line"
+
 FUTURE_URL = "http://pkg.opensolaris.org:80"
 FUTURE_AUTH = "opensolaris.org"
 
--- a/usr/src/cmd/distro_const/DefaultsModule.py	Mon Oct 13 18:10:47 2008 +0200
+++ b/usr/src/cmd/distro_const/DefaultsModule.py	Mon Oct 13 11:47:37 2008 -0700
@@ -115,10 +115,10 @@
 			found = False
 			# If find a match for current uid value, bump current
 			# value and start over.
-			for i in range(len(uid_nodes)):
-				if (uid_nodes[i].get_value() == str(uid)):
+			for uid_node in uid_nodes:
+				if (uid_node.get_value() == str(uid)):
 					uid += 1
-					del uid_nodes[i]
+					del uid_node
 					found = True
 					break
 		return uid
--- a/usr/src/cmd/distro_const/ValidatorModule.py	Mon Oct 13 18:10:47 2008 +0200
+++ b/usr/src/cmd/distro_const/ValidatorModule.py	Mon Oct 13 11:47:37 2008 -0700
@@ -28,7 +28,7 @@
 # =============================================================================
 
 from osol_install.TreeAcc import TreeAcc, TreeAccError
-from osol_install.install_utils import comma_ws_split
+from osol_install.install_utils import space_parse
 from osol_install.distro_const.DC_defs import LOCALE_LIST
 import string
 
@@ -62,8 +62,8 @@
 	# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
 		disallowed_chars = "*%?@[]{}|><()#$\"\'\\"
 		value = node.get_value()
-		for i in range(len(disallowed_chars)):
-			if (value.find(disallowed_chars[i:i+1]) != -1):
+		for dchar in disallowed_chars:
+			if (value.find(dchar) != -1):
 				return True
 		return False
 
@@ -87,9 +87,9 @@
 		checkthis = node.get_value().strip()
 		tree = node.get_tree()
 		locale_node = tree.find_node(LOCALE_LIST)[0]
-		locales = comma_ws_split(locale_node.get_value())
-		for i in range(len(locales)):
-			if (checkthis == locales[i].strip()):
+		locales = space_parse(locale_node.get_value())
+		for locale in locales:
+			if (checkthis == locale.strip()):
 				return True
 		return False
 
@@ -131,7 +131,7 @@
 		tree = node.get_tree()
 		path = node.get_path()
 		nodepath_matches = tree.find_node(path)
-		for i in range(len(nodepath_matches)):
-			if (nodepath_matches[i].get_value() == value):
+		for match in nodepath_matches:
+			if (match.get_value() == value):
 				count += 1
 		return (count == 1)
--- a/usr/src/cmd/distro_const/auto_install/ai_x86_image.xml	Mon Oct 13 18:10:47 2008 +0200
+++ b/usr/src/cmd/distro_const/auto_install/ai_x86_image.xml	Mon Oct 13 11:47:37 2008 -0700
@@ -478,8 +478,8 @@
 				<script name="/usr/share/distro_const/bootroot_configure">
 					<checkpoint name="br-config" message="Boot root configuration"/>
 					<argslist>
-						/usr/share/distro_const/auto_install/ai_generic_live.xml
-						.autoinstall
+						"/usr/share/distro_const/auto_install/ai_generic_live.xml"
+						".autoinstall"
 					</argslist>
 				</script>
 				<script name="/usr/share/distro_const/auto_install/ai_bootroot_configure">
@@ -488,10 +488,13 @@
 				<script name="/usr/share/distro_const/bootroot_archive.py">
 					<checkpoint name="br-arch" message="Boot root archiving"/>
 				</script>
+				<script name="/usr/share/distro_const/grub_setup.py">
+					<checkpoint name="grub-setup" message="Grub menu setup"/>
+				</script>
 				<script name="/usr/share/distro_const/post_bootroot_pkg_image_mod">
 					<checkpoint name="post-mod" message="Post bootroot image area modification"/>
                                         <argslist>
-                                                usr_zlib_compression=gzip
+                                                "usr_zlib_compression=gzip"
                                         </argslist>
 				</script>
 				<script name="/usr/share/distro_const/create_iso">
--- a/usr/src/cmd/distro_const/distro_const.py	Mon Oct 13 18:10:47 2008 +0200
+++ b/usr/src/cmd/distro_const/distro_const.py	Mon Oct 13 11:47:37 2008 -0700
@@ -25,6 +25,7 @@
 import getopt
 import sys
 import os
+import atexit
 import logging
 from osol_install.finalizer import DCFinalizer
 from osol_install.ManifestServ import ManifestServ
@@ -263,7 +264,7 @@
 		print "Invalid or missing subcommand"
 	        usage()
         return 0
-     
+
 #
 # Main distribution constructor function.
 #
@@ -271,7 +272,6 @@
 def main_func():
 #~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
 
-
         cp = checkpoints()
 
 	try:
@@ -280,6 +280,9 @@
 
 		# Start the socket server
 		DC_start_manifest_server(manifest_server_obj)
+
+		# Set up to shut down socket server cleanly on exit
+		atexit.register(manifest_server_obj.stop_socket_server)
 	except:
 		return 1
 
--- a/usr/src/cmd/distro_const/slim_cd/Makefile	Mon Oct 13 18:10:47 2008 +0200
+++ b/usr/src/cmd/distro_const/slim_cd/Makefile	Mon Oct 13 11:47:37 2008 -0700
@@ -36,8 +36,7 @@
 		slimcd_bootroot_configure \
 		slim_cd.xml \
 		slimcd_pre_bootroot_pkg_image_mod \
-		slimcd_post_bootroot_pkg_image_mod \
-		slimcd_grub_a11y
+		slimcd_post_bootroot_pkg_image_mod
 		
 ROOTSLIMCD_FILES=	$(SLIMCD_FILES:%=$(ROOTDC_SLIM)/%)
 
--- a/usr/src/cmd/distro_const/slim_cd/slim_cd.xml	Mon Oct 13 18:10:47 2008 +0200
+++ b/usr/src/cmd/distro_const/slim_cd/slim_cd.xml	Mon Oct 13 11:47:37 2008 -0700
@@ -86,21 +86,6 @@
 			<base_include type="file">usr/lib/fs/ufs/mount</base_include>
 			<base_include type="file">usr/lib/libfstyp.so.1</base_include>
 			<base_include type="file">usr/lib/platexec</base_include>
-			<base_include type="file">usr/lib/devfsadm/linkmod/SUNW_audio_link.so</base_include>
-			<base_include type="file">usr/lib/devfsadm/linkmod/SUNW_cfg_link.so</base_include>
-			<base_include type="file">usr/lib/devfsadm/linkmod/SUNW_disk_link.so</base_include>
-			<base_include type="file">usr/lib/devfsadm/linkmod/SUNW_fssnap_link.so</base_include>
-			<base_include type="file">usr/lib/devfsadm/linkmod/SUNW_ieee1394_link.so</base_include>
-			<base_include type="file">usr/lib/devfsadm/linkmod/SUNW_lofi_link.so</base_include>
-			<base_include type="file">usr/lib/devfsadm/linkmod/SUNW_md_link.so</base_include>
-			<base_include type="file">usr/lib/devfsadm/linkmod/SUNW_misc_link.so</base_include>
-			<base_include type="file">usr/lib/devfsadm/linkmod/SUNW_misc_link_i386.so</base_include>
-			<base_include type="file">usr/lib/devfsadm/linkmod/SUNW_port_link.so</base_include>
-			<base_include type="file">usr/lib/devfsadm/linkmod/SUNW_ramdisk_link.so</base_include>
-			<base_include type="file">usr/lib/devfsadm/linkmod/SUNW_sgen_link.so</base_include>
-			<base_include type="file">usr/lib/devfsadm/linkmod/SUNW_tape_link.so</base_include>
-			<base_include type="file">usr/lib/devfsadm/linkmod/SUNW_usb_link.so</base_include>
-			<base_include type="file">usr/lib/devfsadm/linkmod/SUNW_zfs_link.so</base_include>
 			<base_include type="file">usr/lib/devfsadm/devfsadmd</base_include>
 			<base_include type="file">usr/lib/libm.so.2</base_include>
 			<base_include type="file">usr/lib/libm.so</base_include>
@@ -140,6 +125,7 @@
 			<base_include type="dir">sbin</base_include>
 			<base_include type="dir">dev</base_include>
 			<base_include type="dir">devices</base_include>
+			<base_include type="dir">usr/lib/devfsadm/linkmod</base_include>
 			<base_include type="dir">opt</base_include>
 			<base_include type="dir">root</base_include>
 			<base_include type="dir">jack</base_include>
@@ -153,6 +139,18 @@
 		</bootroot_contents>
 		<live_img_compression type="lzma"/>
 		<build_area>rpool/dc</build_area>
+		<grub_menu_modifications>
+			<entry>
+				<title_suffix>with magnifier</title_suffix>
+				<line>kernel$ /platform/i86pc/kernel/$ISADIR/unix -B assistive_tech=magnifier</line>
+				<line>module /boot/x86.microroot</line>
+			</entry>
+			<entry>
+				<title_suffix>with screen reader</title_suffix>
+				<line>kernel$ /platform/i86pc/kernel/$ISADIR/unix -B assistive_tech=reader</line>
+				<line>module /boot/x86.microroot</line>
+			</entry>
+		</grub_menu_modifications>
 		<output_image>
 			<finalizer>
 				<script name="/usr/share/distro_const/pre_bootroot_pkg_image_mod">
@@ -170,8 +168,8 @@
 				<script name="/usr/share/distro_const/bootroot_configure">
 					<checkpoint name="br-config" message="Boot root configuration"/>
 					<argslist>
-						/usr/share/distro_const/slim_cd/slimcd_generic_live.xml
-						.livecd
+						"/usr/share/distro_const/slim_cd/slimcd_generic_live.xml"
+						".livecd"
 					</argslist>
 				</script>
 				<script name="/usr/share/distro_const/bootroot_archive.py">
@@ -180,15 +178,15 @@
 				<script name="/usr/share/distro_const/slim_cd/slimcd_post_bootroot_pkg_image_mod">
 					<checkpoint name="slim-post-mod" message="Slim CD post bootroot image area modification"/>
 				</script>
+				<script name="/usr/share/distro_const/grub_setup.py">
+					<checkpoint name="grub-setup" message="Grub menu setup"/>
+				</script>
 				<script name="/usr/share/distro_const/post_bootroot_pkg_image_mod">
 					<checkpoint name="post-mod" message="Post bootroot image area modification"/>
 					<argslist>
-						usr_zlib_compression=gzip
+						"usr_zlib_compression=gzip"
 					</argslist>
 				</script>
-				<script name="/usr/share/distro_const/slim_cd/slimcd_grub_a11y">
-					<checkpoint name="grub-a11y" message="Add accessibility items to GRUB menu"/>
-				</script>
 				<script name="/usr/share/distro_const/create_iso">
 					<checkpoint name="iso" message="ISO image creation"/>
 				</script>
--- a/usr/src/cmd/distro_const/slim_cd/slimcd_grub_a11y	Mon Oct 13 18:10:47 2008 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,87 +0,0 @@
-#!/bin/ksh
-#
-# CDDL HEADER START
-#
-# The contents of this file are subject to the terms of the
-# Common Development and Distribution License (the "License").
-# You may not use this file except in compliance with the License.
-#
-# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
-# or http://www.opensolaris.org/os/licensing.
-# See the License for the specific language governing permissions
-# and limitations under the License.
-#
-# When distributing Covered Code, include this CDDL HEADER in each
-# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
-# If applicable, add the following below this CDDL HEADER, with the
-# fields enclosed by brackets "[]" replaced with your own identifying
-# information: Portions Copyright [yyyy] [name of copyright owner]
-#
-# CDDL HEADER END
-#
-# Copyright 2008 Sun Microsystems, Inc.  All rights reserved.
-# Use is subject to license terms.
-#
-
-# =============================================================================
-# =============================================================================
-# grub_a11y
-#
-# Adds screen reader and magnifier options to the GRUB menu.
-# =============================================================================
-# =============================================================================
-
-# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
-# Main
-# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
-# Adds screen reader and magnifier options to the GRUB menu.
-# 
-# Args:
-#   MFEST_SOCKET: Socket needed to get manifest data via ManifestRead object
-#	(not used)
-#
-#   PKG_IMG_PATH: Package image area
-#
-#   TMP_DIR: Temporary directory to contain the bootroot file (not used)
-#
-#   BR_BUILD: Area where bootroot is put together (not used)
-#
-#   MEDIA_DIR: Area where the media is put (not used)
-#
-# Of these automatically-passed variables, only the PKG_IMG_PATH is actually
-# used.
-#
-# Note: This assumes bootroot is built and pkg_image area is intact.
-#
-# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
-
-if [ "$#" != "5" ] ; then
-	print -u2 "Usage: $0: Requires 5 args:"
-	print -u2 "    Reader socket, pkg_image area, tmp dir,"
-	print -u2 "    bootroot build area, media area"
-	exit 1
-fi
-
-PKG_IMG_PATH=$2
-if [ ! -d ${PKG_IMG_PATH} ] ; then
-	print -u2 "$0: Image package area $PKG_IMG_PATH is not valid"
-	exit 1
-fi
-
-SED=/usr/bin/sed
-GREP=/usr/bin/grep
-HEAD=/usr/bin/head
-
-default_title=`$GREP title ${PKG_IMG_PATH}/boot/grub/menu.lst | $HEAD -1`
-$SED "s/TITLE/${default_title}/" <<ENDMENU >>${PKG_IMG_PATH}/boot/grub/menu.lst
-
-TITLE with magnifier
-	kernel$ /platform/i86pc/kernel/\$ISADIR/unix -B assistive_tech=magnifier
-	module /boot/x86.microroot
-
-TITLE with screen reader
-	kernel$ /platform/i86pc/kernel/\$ISADIR/unix -B assistive_tech=reader
-	module /boot/x86.microroot
-ENDMENU
-
-exit 0
--- a/usr/src/cmd/distro_const/utils/Makefile	Mon Oct 13 18:10:47 2008 +0200
+++ b/usr/src/cmd/distro_const/utils/Makefile	Mon Oct 13 11:47:37 2008 -0700
@@ -32,7 +32,8 @@
 install:=	TARGET=	install
 
 PYMODULES=	bootroot_initialize.py \
-		bootroot_archive.py
+		bootroot_archive.py \
+		grub_setup.py
 
 PYCMODULES=	$(PYMODULES:%.py=%.pyc)
 
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/usr/src/cmd/distro_const/utils/grub_setup.py	Mon Oct 13 11:47:37 2008 -0700
@@ -0,0 +1,192 @@
+#!/usr/bin/python
+#
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License (the "License").
+# You may not use this file except in compliance with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+# Copyright 2008 Sun Microsystems, Inc.  All rights reserved.
+# Use is subject to license terms.
+#
+
+# =============================================================================
+# =============================================================================
+# grub_setup
+#
+# Customizations to the grub menu.
+#
+# To be done before post_bootroot_pkg_image_mod gets called.
+# =============================================================================
+# =============================================================================
+
+import os
+import sys
+from osol_install.ManifestRead import ManifestRead
+from osol_install.distro_const.dc_utils import get_manifest_value
+from osol_install.distro_const.dc_utils import get_manifest_list
+from osol_install.distro_const.DC_defs import *
+
+DEFAULT_DEFAULT_ENTRY = "0"
+DEFAULT_TIMEOUT = "30" # Seconds
+
+RELEASE_FILE = "/etc/release"
+
+FIND_EXTRACT_ERR_MSG = ("Error finding or extracting " +
+    "non-empty release string from " + RELEASE_FILE)
+
+# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+# Main
+# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+""" Customizations to the grub menu.
+
+Args:
+  MFEST_SOCKET: Socket needed to get manifest data via ManifestRead object
+
+  PKG_IMG_PATH: Package image area mountpoint
+
+  TMP_DIR: Temporary directory (not used)
+
+  BR_BUILD: Area where bootroot is put together.  (not used)
+
+  MEDIA_DIR: Area where the media is put. (not used)
+
+Note: This assumes a populated pkg_image area exists at the location
+	${PKG_IMG_PATH}
+"""
+# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+if (len(sys.argv) != 6): # Don't forget sys.argv[0] is the script itself.
+	raise Exception, (sys.argv[0] + ": Requires 5 args:\n" +
+	    "    Reader socket, pkg_image area, temp dir,\n" +
+	    "    bootroot build area, media area.")
+
+# collect input arguments from what this script sees as a commandline.
+MFEST_SOCKET = sys.argv[1]	# Manifest reader socket
+PKG_IMG_PATH = sys.argv[2]	# package image area mountpoint
+
+# get the manifest reader object from the socket
+manifest_reader_obj = ManifestRead(MFEST_SOCKET)
+
+# Get the release from first line of /etc/release in PKG_IMG_PATH
+release_fd = None
+release = None
+try:
+	try:
+		release_fd = open(PKG_IMG_PATH + RELEASE_FILE, "r")
+		release = release_fd.readline().strip()
+	except Exception, err:
+		print >>sys.stderr, sys.argv[0] + ": " + FIND_EXTRACT_ERR_MSG
+		raise err
+finally:
+	if (release_fd != None):
+		release_fd.close()
+
+if ((release == None) or (len(release.strip()) == 0)):
+	print >>sys.stderr, sys.argv[0] + ": Empty or blank first line in file"
+	raise Exception, sys.argv[0] + ": " + FIND_EXTRACT_ERR_MSG
+
+# Open menu.lst file.
+try:
+	menu_lst_file = open(PKG_IMG_PATH + "/boot/grub/menu.lst", "w")
+except IOError, err:
+	print >>sys.stderr, "Error opening grub menu.lst for writing"
+	raise
+
+# Get default entry from manifest, if it exists.
+DEFAULT_ENTRY = get_manifest_value(manifest_reader_obj, GRUB_DEFAULT_ENTRY_NUM)
+if (DEFAULT_ENTRY == None):
+	DEFAULT_ENTRY = DEFAULT_DEFAULT_ENTRY
+menu_lst_file.write("default=" + DEFAULT_ENTRY + "\n")
+
+# Get default timeout from manifest. if it exists.
+TIMEOUT = get_manifest_value(manifest_reader_obj, GRUB_DEFAULT_TIMEOUT)
+if (TIMEOUT == None):
+	TIMEOUT = DEFAULT_TIMEOUT
+menu_lst_file.write("timeout=" + TIMEOUT + "\n")
+
+menu_lst_file.write("splashimage=/boot/grub/splash.xpm.gz\n")
+menu_lst_file.write("foreground=ffffff\n")
+menu_lst_file.write("background=215ECA\n")
+
+# "entries" is an ordered list of grub entries.  Defaults will be:
+#	<release>
+#	<release> text console
+#	Boot from hard disk
+#
+# Add new entries from the manifest, in the position requested.  Position will
+# be relative to the existing entry list.  For example, if two entries are
+# listed for position 1, the first one will be put at position 1, but then the
+# second one will be put at position 1, bumping the first to position 2.
+
+entries = []
+
+# The following three entries are the standard "hardwired" entries.
+
+entry = []
+entry.append("title " + release)
+entry.append("\tkernel$ /platform/i86pc/kernel/$ISADIR/unix")
+entry.append("\tmodule /boot/x86.microroot")
+entries.append(entry)
+
+entry = []
+entry.append("title " + release + " text console")
+entry.append("\tkernel$ /platform/i86pc/kernel/$ISADIR/unix -B livemode=text")
+entry.append("\tmodule /boot/x86.microroot")
+entries.append(entry)
+
+entry = []
+entry.append("title Boot from Hard Disk")
+entry.append("\trootnoverify (hd0)")
+entry.append("\tchainloader +1")
+entries.append(entry)
+
+# This all assumes that data is returned from the manifest in the order it is
+# provided.  Otherwise, lines within an entry could be out of order.
+
+entry_names = get_manifest_list(manifest_reader_obj, GRUB_ENTRY_TITLE_SUFFIX)
+for name in entry_names:
+	entry = []
+	entry.append("title " + release + " " + name)
+	lines = get_manifest_list(manifest_reader_obj, GRUB_ENTRY_LINES % name)
+	for line in lines:
+		entry.append("\t" + line)
+
+	position_str = get_manifest_value(manifest_reader_obj,
+	    GRUB_ENTRY_POSITION % name)
+
+	# Put at the end of the list if no position stated.
+	if (position_str == None):
+		entries.append(entry)
+	else:
+		try:
+			position = int(position_str)
+			entries.insert(position, entry)
+		except ValueError:
+			print >>sys.stderr, ("Position specified for the \"" +
+			    release + " " + name + "\" entry")
+			print >>sys.stderr, ("    is not a positive number.  " +
+			    "Found: " + position_str)
+			print >>sys.stderr, "    Placing at the end of the list"
+			entries.append(entry)
+
+for entry in entries:
+	menu_lst_file.write("\n")
+	for entry_line in entry:
+		menu_lst_file.write(entry_line + "\n")
+
+menu_lst_file.close()
+sys.exit(0)
--- a/usr/src/cmd/distro_const/utils/post_bootroot_pkg_image_mod	Mon Oct 13 18:10:47 2008 +0200
+++ b/usr/src/cmd/distro_const/utils/post_bootroot_pkg_image_mod	Mon Oct 13 11:47:37 2008 -0700
@@ -35,6 +35,7 @@
 CPIO=/usr/bin/cpio
 CUT=/usr/bin/cut
 DU=/usr/bin/du
+ECHO=/usr/bin/echo
 FIND=/usr/bin/find
 GREP=/usr/bin/grep
 HEAD=/usr/bin/head
@@ -112,7 +113,7 @@
         fi
 
 	# get the boot archive raw device and create a filesystem on it
-        rlofidev=`echo $lofidev | $SED s/lofi/rlofi/`
+        rlofidev=`$ECHO $lofidev | $SED s/lofi/rlofi/`
         $NEWFS -m 0 $rlofidev < /dev/null 
         if [ $? -ne 0 ] ; then
                 print -u2 "$0: create_ufs_fs: Unable to newfs $rlofidev"
@@ -183,13 +184,13 @@
 	exit 1
 fi
 
-USER_ZLIB_KEY=`echo $6|cut -d'=' -f1`
+USER_ZLIB_KEY=`$ECHO $6 | $CUT -d'=' -f1`
 if [ "XX${USER_ZLIB_KEY}" != "XXusr_zlib_compression" ] ; then
 	print -u2 "Key to specify usr zlib compression algorithm is not valid."
 	exit 1
 fi
 
-USER_ZLIB_ALG=`echo $6|cut -d'=' -f2`
+USER_ZLIB_ALG=`$ECHO $6 |$CUT -d'=' -f2`
 #
 # Just make sure the algorithm is not an empty string.  We will not
 # check to make sure whether it is a valid algorithm or not here.
@@ -233,27 +234,6 @@
 	$FIND . -type f -a ! -name unix | $XARGS $RM -f
 fi
 
-# Generate GRUB menu for CD using release name from /etc/release
-release=`$HEAD -1 ${PKG_IMG_PATH}/etc/release | $SED 's/^ *//'`
-$SED "s/OS_VER/${release}/" <<ENDMENU >${PKG_IMG_PATH}/boot/grub/menu.lst
-default=0
-timeout=30
-splashimage=/boot/grub/splash.xpm.gz
-foreground=ffffff
-background=215ECA
-title OS_VER
-	kernel$ /platform/i86pc/kernel/\$ISADIR/unix
-	module /boot/x86.microroot
-
-title OS_VER text console
-	kernel$ /platform/i86pc/kernel/\$ISADIR/unix -B livemode=text
-	module /boot/x86.microroot
-
-title Boot from Hard Disk
-	rootnoverify (hd0)
-	chainloader +1
-ENDMENU
-
 print "Generating usr filesystem image"
 if [ ! -d $PKG_IMG_PATH ] ; then
 	print -u2 "$0: Image package area $PKG_IMG_PATH is not valid"
@@ -338,9 +318,9 @@
 #pkg.zlib as a special case so it has the correct uid/gid/permission
 #
 
-/usr/bin/echo "var" | /usr/bin/cpio -pdum ${pkgarchive_mntpt}
+$ECHO "var" | $CPIO -pdum ${pkgarchive_mntpt}
 
-/bin/find var/pkg | /usr/bin/cpio -pdum ${pkgarchive_mntpt}
+$FIND var/pkg | $CPIO -pdum ${pkgarchive_mntpt}
 
 $UMOUNT $pkgarchive_mntpt
 $LOFIADM -d $pkgarchive
--- a/usr/src/cmd/install-tools/ManifestRead.py	Mon Oct 13 18:10:47 2008 +0200
+++ b/usr/src/cmd/install-tools/ManifestRead.py	Mon Oct 13 11:47:37 2008 -0700
@@ -107,7 +107,6 @@
 		print "Interrupted"
 	except Exception, err:
 		print "Exception caught:"
-		print str(err)
 	if (err):
 		ret = err.args[0]
 	sys.exit(ret)
--- a/usr/src/cmd/install-tools/ManifestServ.py	Mon Oct 13 18:10:47 2008 +0200
+++ b/usr/src/cmd/install-tools/ManifestServ.py	Mon Oct 13 11:47:37 2008 -0700
@@ -32,6 +32,7 @@
 
 import sys
 import errno
+import atexit
 import getopt
 from osol_install.ManifestServ import ManifestServ
 
@@ -83,12 +84,20 @@
 			continue
 
 		matches = []
-		results = mfest_obj.get_values(path, key_mode)
-		for i in range(len(results)):
-			if (results[i].strip() == ""):
+		try:
+			results = mfest_obj.get_values(path, key_mode)
+		except Exception, err:
+			print >>sys.stderr, (
+			    "Exception caught when retrieving values")
+			print >>sys.stderr, "    request: " + path
+			print >>sys.stderr, "    " + str(err)
+			continue
+			
+		for result in results:
+			if (result.strip() == ""):
 				print "(empty string / no value)"
 			else:
-				print results[i]
+				print result
 
 
 # ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
@@ -127,6 +136,22 @@
 
 
 # ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+def __ManifestServ_exit_handler(mfest_obj):
+# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+# Called at exit time to stop the socket server.
+#
+# Args:
+#   mfest_obj: ManifestServ object to stop the socket server on.
+#
+# Returns: None
+#
+# Raises: None
+#
+# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+	mfest_obj.stop_socket_server()
+
+
+# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
 # Main
 # ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
 if __name__ == "__main__":
@@ -187,6 +212,9 @@
 			print ("Connect to socket with name " +
 			    mfest_obj.get_sockname())
 
+			# Set up to shut down the socket server at exit.
+			atexit.register(__ManifestServ_exit_handler, mfest_obj)
+
 		# Enable querying from this process as well.  This method will
 		# block to hold the socket server open for remote queries as
 		# well (if enabled).
@@ -196,10 +224,6 @@
         except Exception, err:
 		print >>sys.stderr, "Error running Manifest Server"
 
-	# Time to shut down socket server.
-	if ((mfest_obj != None) and (s_flag)):
-		mfest_obj.stop_socket_server()
-
 	if (err != None):
 		ret = err.args[0]
 	sys.exit (ret)
--- a/usr/src/lib/install_utils/DefValProc.py	Mon Oct 13 18:10:47 2008 +0200
+++ b/usr/src/lib/install_utils/DefValProc.py	Mon Oct 13 11:47:37 2008 -0700
@@ -34,7 +34,7 @@
 import osol_install.TreeAcc
 from osol_install.TreeAcc import TreeAcc, TreeAccError, NodeNotFoundError, TreeAccNode
 from osol_install.install_utils import Trace
-from osol_install.install_utils import canaccess, comma_ws_split
+from osol_install.install_utils import canaccess, space_parse
 
 # =============================================================================
 # Constants
@@ -151,9 +151,9 @@
 
 		# Each helper element contains attributes for module, method
 		# and ref.
-		for i in range(len(helpers)):
+		for helper in helpers:
 			# Get list of attributes for current helper element.
-			helper_attrs = helpers[i].get_attr_dict()
+			helper_attrs = helper.get_attr_dict()
 
 			# This is the ref string used to index into the
 			# dictionaries for a given method in a given module.
@@ -349,6 +349,13 @@
 	# Note current_node starts out and remains a one-item list.
 	ancestor_node = current_node = tree.find_node(nodepath_pieces[0])
 
+	# Err out if the nodepath (from the defval manifest) doesn't jibe
+	# with the project manifest.  At a minimum, the tops of the trees
+	# should be the same.
+	if (len(ancestor_node) == 0):
+		raise ManifestProcError, ("generate_ancestor_nodes: " +
+		    "manifest and defval manifest are incompatible")
+
 	# Iterate from the top of the nodepath, filling in nodes which are
 	# missing.  New nodes will have no value.
 	for i in range(1, len(nodepath_pieces)):
@@ -506,6 +513,19 @@
 		- "skip": Do nothing.
 		- "error": Err out.
 		- default if not specified = "error"
+	- empty_str: (optional) What to do when an empty string is encountered.
+		An empty string is defined here as a zero length string, or ""
+		or '' to handled quote-enveloped empty strings.  When specified,
+		must be one of the following:
+		- "set_default": Determine the default as if no node was
+			present, then change the value of any matching
+			empty-string nodes to the default value.
+		- "valid": Accept the empty string as a valid value.  Do
+			nothing.
+		- "error": Flag the empty string as an error.
+		Note that all matching nodes with empty strings will have their
+		default value calculated and plugged.
+
 	- skip_if_no_exist: (optional) The nodepath of an ancestor node.  If the
 		ancestor node is missing, skip processing.
 
@@ -548,22 +568,21 @@
 	if (len(defaults) == 0):
 		return
 
-	for i in range(len(defaults)):
-		attributes = defaults[i].get_attr_dict()
+	for curr_def in defaults:
+		attributes = curr_def.get_attr_dict()
 
 		manifest_nodepath = attributes["nodepath"]
 		if (debug):
-			Trace.log(2, Trace.DEFVAL_MASK,
-			    "Checking skip_if_no_exist for nodepath " +
-			    manifest_nodepath)
+			Trace.log(2, Trace.DEFVAL_MASK, "Checking defaults " +
+			    "for " + manifest_nodepath)
+
 		if __do_skip_if_no_exist(attributes, manifest_tree, debug):
 			if (debug):
 				Trace.log(2, Trace.DEFVAL_MASK,
-				    manifest_nodepath +
-				    " doesn't exist.  Skipping...")
+				    "Ancestor doesn't exist.  Skipping...")
 			continue
 
-		value_from_xml = defaults[i].get_value()
+		value_from_xml = curr_def.get_value()
 		type_str = attributes["type"]
 		via = attributes["from"]
 
@@ -646,9 +665,32 @@
 				errors = True
 				continue
 
+		# Check how to handle an empty string.  Sometimes an empty
+		# string is a valid value.  Other times it may be as valid as a
+		# missing value, and a default should be set in its place.  The
+		# latter case may prove useful for documentation purposes: as a
+		# placeholder in the XML file for something which can be filled
+		# in later by defaults processing.  Additionally, an empty
+		# string can be an error.
+		empty_str = "set_default"
+
+		try:
+			empty_str = attributes["empty_str"]
+		except KeyError:
+			pass
+
+		if ((empty_str != "set_default") and (empty_str != "valid") and
+		    (empty_str != "error")):
+
+			# Shouldn't get here if defaults / validation manifest
+			# passed schema validation.
+			print >>sys.stderr, ("add_defaults: " +
+			    "Invalid \"empty_str\" attribute = " + empty_str)
+			errors = True
+			continue
+
 		# Check each parent node for children.
-		for j in range(len(parent_nodes)):
-			parent_node = parent_nodes[j]
+		for parent_node in parent_nodes:
 			if (debug):
 				Trace.log(3, Trace.DEFVAL_MASK,
 				    "New parent node:" + str(parent_node))
@@ -658,45 +700,64 @@
 			# the default.  If a parent has no such child, give it
 			# one with the default value.
 
-			# Make sure the value is not already present.
-			node = manifest_tree.find_node(child_nodepath,
+			# Handle any values present as empty strings.
+			nodes = manifest_tree.find_node(child_nodepath,
 			    parent_node)
-			if (len(node) != 0):
-				if (debug):
-					Trace.log(2, Trace.DEFVAL_MASK,
-					    "Node " + manifest_nodepath +
-					    " already exists for this " +
-					    "parent.  Skip.")
+			if (len(nodes) != 0):
+				for node in nodes:
+
+					# Zero length strings and "" and ''
+					# are considered empty here.
+					node_value = node.get_value()
+					if ((len(node_value) > 0) and not
+					    ((len(node_value) == 2) and
+					    ((node_value == "\"\"") or
+					    (node_value == "''")))):
+						continue
+
+					elif (empty_str == "valid"):
+						Trace.log(2, Trace.DEFVAL_MASK,
+						    "Valid empty string found")
+						continue
+
+					elif (empty_str != "set_default"):
+						Trace.log(2, Trace.DEFVAL_MASK,
+						    "Unpermitted empty " +
+						    "string found for " +
+						    "nodepath " +
+						    manifest_nodepath)
+						errors = True
+						continue
+
+					# Install default.
+					try:
+						default_value = __get_default(
+						    via, value_from_xml,
+						    deflt_setters, parent_node,
+						    debug)
+					except Exception:
+						errors = True
+						continue
+
+					if (debug):
+						Trace.log(2, Trace.DEFVAL_MASK,
+						    ("Replacing %s value at " +
+						    "%s with %s...") % (
+						    type_str, manifest_nodepath,
+						    default_value))
+					manifest_tree.replace_value(
+					    child_nodepath, default_value,
+					    parent_node)
+							
 				continue
 
-			# At this point, a node with a default value is needed.
-
-			# Call helper method to determine the value.
-			if (via == "helper"):
-				try:
-					default_value = __get_value_from_helper(
-					    value_from_xml, deflt_setters,
-					    parent_node, debug)
-
-				# Skip and muddle along as best we can on error
-				except Exception, err:
-					print >>sys.stderr, str(err)
-					print >>sys.stderr, ("add_defaults: " +
-					    "Error getting default value " +
-					    "from helper method for " +
-					    manifest_nodepath)
-					errors = True
-					continue
-
-			# Get the value from the defaults / validation manifest.
-			elif (via == "value"):
-				default_value = value_from_xml
-
-			# Shouldn't get here if defaults / validation manifest
-			# passed schema validation.
-			else:
-				print >>sys.stderr, ("add_defaults: " +
-				    "Invalid \"from\" attribute = " + via)
+			# No value is present.
+			# A new node w/a default value is needed.
+			try:
+				default_value = __get_default(via,
+				    value_from_xml, deflt_setters, parent_node,
+				    debug)
+			except Exception:
 				errors = True
 				continue
 
@@ -713,6 +774,56 @@
 
 
 # ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+def __get_default(via, value_from_xml, deflt_setters, parent_node, debug):
+# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+	""" Get or calculate a default value.
+
+	Args:
+	  via: "from" field of the relevant default entry in the defval XML doc:
+		set to either "helper" or "value"
+
+	  value_from_xml: "value" of a default entry in the default XML doc
+
+	  deflt_setters: dictionary of default setter helper methods.
+
+	  parent_node: Parent node of the nodes receiving defaults
+
+	  debug: Turn on debug / tracing messages when True
+
+	Returns: String value of the sought default
+
+	Raises:
+	  Exceptions from __get_value_from_helper
+	  Excepion: Invalid "from" attribute
+	"""
+# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+	# Call helper method to determine the value.
+	if (via == "helper"):
+		try:
+			default_value = __get_value_from_helper(
+			    value_from_xml, deflt_setters, parent_node, debug)
+
+		# Skip and muddle along as best we can on error
+		except Exception, err:
+			print >>sys.stderr, ("add_defaults: Error getting " +
+			    "default value from helper method for " +
+			    manifest_nodepath)
+			raise err
+
+	# Get the value from the defaults / validation manifest.
+	elif (via == "value"):
+		default_value = value_from_xml
+
+	# Shouldn't get here if defaults / validation manifest
+	# passed schema validation.
+	else:
+		raise Exception, ("add_defaults: " +
+		    "Invalid \"from\" attribute = " + via)
+
+	return default_value
+
+
+# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
 def __validate_node(validator_ref, validator_dicts, node, debug):
 # ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
 	"""Perform semantic validation on given a given node.
@@ -857,8 +968,8 @@
 		return
 
 	# Create separate lists of the different kinds of "validate" nodes.
-	for i in range(len(to_validate)):
-		attributes = to_validate[i].get_attr_dict()
+	for validateme in to_validate:
+		attributes = validateme.get_attr_dict()
 
 		# Do specific nodes specified with "nodepath" attribute, for
 		# this pass.
@@ -878,21 +989,21 @@
 					    "Node doesn't exist.  Skipping...")
 				continue
 
-			singles_validate.append(to_validate[i])
+			singles_validate.append(validateme)
 			continue
 		except KeyError:
 			pass
 
 		try:
 			dummy = attributes["group"]
-			group_validate.append(to_validate[i])
+			group_validate.append(validateme)
 			continue
 		except KeyError:
 			pass
 
 		try:
 			dummy = attributes["exclude"]
-			exclude_validate.append(to_validate[i])
+			exclude_validate.append(validateme)
 			continue
 		except KeyError:
 			# Schema should protect from ever getting here...
@@ -945,8 +1056,8 @@
 	"""
 # ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
 	errors = False
-	for i in range(len(to_validate)):
-		attributes = to_validate[i].get_attr_dict()
+	for validateme in to_validate:
+		attributes = validateme.get_attr_dict()
 		manifest_nodepath = attributes["nodepath"]
 
 		if (debug):
@@ -954,7 +1065,7 @@
 			    "Validating node(s) at nodepath " +
 			    manifest_nodepath)
 
-		validator_list = comma_ws_split(to_validate[i].get_value())
+		validator_list = space_parse(validateme.get_value())
 
 		# Nodepaths which are direct children of the root are
 		# special cases
@@ -1000,8 +1111,7 @@
 			continue
 
 		# Check each parent node for children.
-		for j in range(len(parent_nodes)):
-			parent_node = parent_nodes[j]
+		for parent_node in parent_nodes:
 			if (debug):
 				Trace.log(2, Trace.DEFVAL_MASK,
 				    "  Processing new parent node")
@@ -1024,13 +1134,11 @@
 			# for each parent node.
 
 			# Loop through each child.
-			for k in range(len(nodes)):
-				node = nodes[k]
+			for node in nodes:
 
 				# Call helper methods to do the validation.
-				for l in range(len(validator_list)):
-					validator_ref = (
-					    validator_list[l].strip())
+				for validator in validator_list:
+					validator_ref = validator.strip()
 					if (not __validate_node(validator_ref,
 					    validator_dicts, node, debug)):
 						errors = True
@@ -1061,8 +1169,8 @@
 	"""
 # ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
 	errors = False
-	for i in range(len(to_validate)):
-		attributes = to_validate[i].get_attr_dict()
+	for validateme in to_validate:
+		attributes = validateme.get_attr_dict()
 		validator_ref = attributes["group"].strip()
 
 		if (debug):
@@ -1072,10 +1180,10 @@
 
 		# Get the list of nodepaths of nodes to validate as a string,
 		# then break into individual strings.
-		nodepaths = comma_ws_split(to_validate[i].get_value())
+		nodepaths = space_parse(validateme.get_value())
 
-		for j in range(len(nodepaths)):
-			nodepath = nodepaths[j].strip()
+		for raw_nodepath in nodepaths:
+			nodepath = raw_nodepath.strip()
 			if (debug):
 				Trace.log(2, Trace.DEFVAL_MASK,
 				    "  Validating nodes matching nodepath " +
@@ -1089,8 +1197,7 @@
 				continue
 
 			# Check each node.
-			for k in range(len(nodes)):
-				node = nodes[k]
+			for node in nodes:
 				value = node.get_value()
 				if (debug):
 					Trace.log(2, Trace.DEFVAL_MASK,
@@ -1128,15 +1235,15 @@
 	"""
 # ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
 	errors = False
-	for i in range(len(to_exclude)):
-		attributes = to_exclude[i].get_attr_dict()
+	for excludeme in to_exclude:
+		attributes = excludeme.get_attr_dict()
 		validator_ref = attributes["exclude"].strip()
 
 		if (debug):
 			Trace.log(2, Trace.DEFVAL_MASK,
 			"  Processing unexcluded nodes validated by " +
 			    validator_ref + "()")
-		nodepaths = comma_ws_split(to_exclude[i].get_value())
+		nodepaths = space_parse(excludeme.get_value())
 
 		# For every node in the tree do
 		walker = manifest_tree.get_tree_walker() 
@@ -1147,25 +1254,24 @@
 		while (curr_list != None):
 
 			# Cycle through all returned nodes.
-			for j in range(len(curr_list)):
+			for node in curr_list:
 
 				if (debug):
 					Trace.log(2, Trace.DEFVAL_MASK,
 					    "Checking current node: " +
-					    curr_list[j].get_path())
+					    node.get_path())
 
 				# Check through the list of nodepaths to be
 				# inhibited.  Note if the path of the current
 				# node is in the list.
 				inhibit = False
-				for k in range(len(nodepaths)):
-					nodepath = nodepaths[k].strip()
+				for raw_nodepath in nodepaths:
+					nodepath = raw_nodepath.strip()
 					if (debug):
 						Trace.log(2, Trace.DEFVAL_MASK,
 						    "%s vs %s" % (nodepath,
-						    curr_list[j].get_path()))
-					if (nodepath ==
-					    curr_list[j].get_path()):
+						    node.get_path()))
+					if (nodepath == node.get_path()):
 						inhibit = True
 						break
 
@@ -1175,8 +1281,7 @@
 						    "Not inbibited.  " +
 						    "Checking node")
 					if (not __validate_node(validator_ref,
-					    validator_dicts, curr_list[j]),
-					   debug):
+					    validator_dicts, node), debug):
 						errors = True
 
 			curr_list = manifest_tree.walk_tree(walker)
--- a/usr/src/lib/install_utils/ManifestRead.py	Mon Oct 13 18:10:47 2008 +0200
+++ b/usr/src/lib/install_utils/ManifestRead.py	Mon Oct 13 11:47:37 2008 -0700
@@ -34,7 +34,6 @@
 import socket
 import errno
 import osol_install.SocketServProtocol as SocketServProtocol
-from osol_install.install_utils import comma_ws_split
 
 # =============================================================================
 class ManifestRead(object):
@@ -139,9 +138,37 @@
 	# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
 		results_list = []
 
-		# Convert keys to proper requests.
+		# Specify key and request size in pre_request string.
+		# Pre_request string must be SocketServProtocol.PRE_REQ_SIZE
+		# bytes total.
 		if (is_key):
-			request = SocketServProtocol.KEY_PATH % (request)
+			pre_request = "1"
+		else:
+			pre_request = "0"
+		pre_request += " "
+		pre_request += "%6.6d" % len(request)
+
+		# Sending the pre-request
+		if (self.debug):
+			print "Sending pre-request: " + pre_request
+		try:
+			self.client_sock.send(pre_request)
+		except Exception, err:
+			print >>sys.stderr, (
+			    "Error sending pre-request to server")
+			raise
+
+		# Wait for server to return the pre-request acknowledge
+		try:
+			pre_req_ack = self.client_sock.recv(1)
+		except Exception, err:
+			print >>sys.stderr, ("Protocol error: Did not " +
+			    "receive pre_request acknowledge.")
+			raise
+
+		if (pre_req_ack[0] != SocketServProtocol.PRE_REQ_ACK):
+			raise Exception, ("Protocol error: " +
+			    "pre_request acknowledge is incorrect")
 
 		# Send the request
 		if (self.debug):
@@ -150,7 +177,6 @@
 			self.client_sock.send(request)
 		except Exception, err:
 			print >>sys.stderr, "Error sending request to server"
-			print >>sys.stderr, str(err)
 			raise
 
 		# Wait for server to return the result count and size first.
@@ -161,7 +187,6 @@
 		except Exception, err:
 			print >>sys.stderr, ("Protocol error: Did not " +
 			    "receive request count and size.")
-			print >>sys.stderr, str(err)
 			raise
 
 		# Acknowledge to server the receipt of count and size.
@@ -171,7 +196,6 @@
 		except Exception, err:
 			print >>sys.stderr, ("Error sending params-rcvd " +
 			    "message to server")
-			print >>sys.stderr, str(err)
 			raise
 
 		if (self.debug):
@@ -187,7 +211,6 @@
 		except Exception, err:
 			print >>sys.stderr, ("Error receiving results from " +
 			    "server")
-			print >>sys.stderr, str(err)
 			raise
 
 		# Note that the final list element is REQ_COMPLETE and is
@@ -197,7 +220,7 @@
 		got_empty_string = False
 		for i in range(count):
 			if (results[i][0] != SocketServProtocol.EMPTY_STR):
-				results_list.extend(comma_ws_split(results[i]))
+				results_list.append(results[i])
 			elif (not got_empty_string):
 				results_list.append("(empty string)")
 				got_empty_string = True
@@ -246,22 +269,17 @@
 		"""
 	# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
 		print_nodepath = ((len(request_list) > 1) or (force_req_print))
-		for i in range(len(request_list)):
-			if (are_keys):
-				request = SocketServProtocol.KEY_PATH % (
-				    request_list[i])
-			else:
-				request = request_list[i]
+		for request in request_list:
 			try:
-				result_list = self.get_values(request, False)
-			except err:
+				result_list = self.get_values(request, are_keys)
+			except Exception, err:
 				raise
 			if (print_nodepath):
-				nodepath = request_list[i] + " "
+				nodepath = request + " "
 			else:
 				nodepath = ""
-			for j in range(len(result_list)):
-				print "%s%s" % (nodepath, result_list[j])
+			for result in result_list:
+				print "%s%s" % (nodepath, result)
 
 	# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
 	def set_debug(self, on_off):
--- a/usr/src/lib/install_utils/ManifestServ.py	Mon Oct 13 18:10:47 2008 +0200
+++ b/usr/src/lib/install_utils/ManifestServ.py	Mon Oct 13 11:47:37 2008 -0700
@@ -35,6 +35,7 @@
 import socket
 import thread
 import osol_install.SocketServProtocol as SocketServProtocol
+from osol_install.install_utils import space_parse
 from osol_install.TreeAcc import TreeAcc, TreeAccError
 from osol_install.DefValProc import init_defval_tree, add_defaults
 from osol_install.DefValProc import validate_content, schema_validate
@@ -121,6 +122,9 @@
 		"""
 	# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
 
+		# Save verbose setting as other methods use it too.
+		self.verbose = verbose
+
 		# 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.
@@ -141,6 +145,18 @@
 		# socket-names.
 		strpid = str(os.getpid())
 
+		# 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[
+		    (full_manifest_basename.rfind("/") + 1):]
+
+		# This is name of temporary file, that includes defaults,
+		# before reformatting.
+		temp_manifest = (
+		    "/tmp/" + manifest_basename + "_temp_" + strpid +
+		    ManifestServ.XML_SUFFIX)
+
 		# Do this here in case cleanup() is called without
 		# start_socket_server() having been called first.
 		self.listen_sock_name = ("/tmp/ManifestServ." + strpid)
@@ -153,7 +169,6 @@
 		except Exception, err:
 			print >>sys.stderr, ("Error initializing " +
 			    "defaults/content-validation tree")
-			print >>sys.stderr, str(err)
 			raise
 
 		# Initialize the project manifest data tree.
@@ -161,7 +176,6 @@
 			self.manifest_tree = TreeAcc(manifest)
 		except Exception, err:
 			print >>sys.stderr, "Error instantiating manifest tree "
-			print >>sys.stderr, str(err)
 			raise
 
 		# Add defaults to the project manifest data tree.
@@ -170,8 +184,15 @@
 		except Exception, err:
 			print >>sys.stderr, (
 			    "Error adding defaults to manifest tree")
-			print >>sys.stderr, str(err)
-			raise
+
+			# Create temp manifest for debugging
+			if (keep_temp_files):
+				try:
+					self.manifest_tree.save_tree(
+					    temp_manifest)
+				except Exception, save_exc:
+					print >>sys.stderr, str(save_exc)
+			raise err
 
 		# Do semantic / content validation on the project manifest
 		# data tree.
@@ -181,23 +202,19 @@
 		except Exception, err:
 			print >>sys.stderr, (
 			    "Error validating manifest tree content")
-			print >>sys.stderr, str(err)
+
+			# Create temp manifest for debugging
+			if (keep_temp_files):
+				try:
+					self.manifest_tree.save_tree(
+					    temp_manifest)
+				except Exception, save_exc:
+					print >>sys.stderr, str(save_exc)
 			raise
 
 		# Validate the project manifest data tree against its schema.
 		# Save a nicely-formatted copy if out_manifest is specified.
 
-		# Strip any prepended directories from before the basename.
-		# This will be used in creation of the temporary filename.
-		manifest_basename = full_manifest_basename[
-		    (full_manifest_basename.rfind("/") + 1):]
-
-		# This is name of temporary file, that includes defaults,
-		# before reformatting.
-		temp_manifest = (
-		    "/tmp/" + manifest_basename + "_temp_" + strpid +
-		    ManifestServ.XML_SUFFIX)
-
 		try:
 			try:
 				self.manifest_tree.save_tree(temp_manifest)
@@ -206,7 +223,6 @@
 			except Exception, err:
 				print >>sys.stderr, ("Error validating " +
 				    "manifest against schema " + schema)
-				print >>sys.stderr, str(err)
 				raise
 
 		# Check to delete the temporary file whether or not an
@@ -243,7 +259,6 @@
 			thread.start_new_thread(self.__socket_server_main, ())
 		except Exception, err:
 			print >>sys.stderr, "Error starting socket server"
-			print >>sys.stderr, str(err)
 			raise
 
 
@@ -314,8 +329,17 @@
 			request = SocketServProtocol.KEY_PATH % (request)
 
 		nodelist = self.manifest_tree.find_node(request.strip())
-		for i in range(len(nodelist)):
-			strlist.append(nodelist[i].get_value())
+		for node in nodelist:
+			value = node.get_value()
+			if (value == ""):
+				strlist.append("")
+			else:
+				strlist.extend(space_parse(value))
+
+		if (self.verbose):
+			print "get_values: request = \"" + request + "\""
+			print (("   %d results found: " % len(strlist)) +
+			    str(strlist))
 		return strlist
 
 
@@ -368,32 +392,52 @@
 		while (True):
 
 			# Receive a new request.
-			request = srvsock.recv(1024)
+			pre_request = srvsock.recv(SocketServProtocol.PRE_REQ_SIZE)
 
 			# Client terminated (unexpectedly or per protocol)
-			if ((len(request) == 0) or 
-			    (request[0] == SocketServProtocol.TERM_LINK)):
+			if ((len(pre_request) == 0) or 
+			    (pre_request[0] == SocketServProtocol.TERM_LINK)):
 				if (self.socket_debug):
 					print "termination requested"
 				break
 
+			if (pre_request[0] == '0'):
+				is_key = False
+			elif (pre_request[0] == '1'):
+				is_key = True
+			else:
+				raise Exception, "Prerequest Protocol Error:key"
+
+			try:
+				request_size = int(pre_request[
+				    2:SocketServProtocol.PRE_REQ_SIZE])
+			except ValueError:
+				raise Exception, (
+				    "Prerequest Protocol Error:size")
+
+			if (self.socket_debug):
+				print ("Prerequest received: key is " +
+				    str(is_key) + " and size = " +
+				    str(request_size))
+
+			# Send the pre_request ack and wait for the request
+			srvsock.send(SocketServProtocol.PRE_REQ_ACK)
+			request = srvsock.recv(request_size)
+
 			# Query the request
 			if (self.socket_debug):
 				print "Received Request: " + request.strip()
-			nodelist = self.manifest_tree.find_node(request.strip())
+
+			values = self.get_values(request.strip(), is_key)
 
 			# Send the count and size.  In the case of found
 			# results, calculate the results string first to get
 			# the size.
-			if (len(nodelist) == 0):	# No results found
+			if (len(values) == 0):	# No results found
 				srvsock.send("0,0")
 			else:
 				results = ""
-				for i in range(len(nodelist)):
-					value = nodelist[i].get_value()
-
-					if (self.socket_debug):
-						print "Result: " + value
+				for value in values:
 
 					# Handle "empty string" results.
 					if (value == ""):
@@ -408,7 +452,7 @@
 				results += SocketServProtocol.REQ_COMPLETE
 
 				# Send results count and size.
-				srvsock.send(str(len(nodelist)) + "," +
+				srvsock.send(str(len(values)) + "," +
 				    str(len(results)))
 
 			# Wait for the count/size acknowledge from the client.
@@ -418,7 +462,7 @@
 				raise Exception, "Protocol Error"
 
 			# Done with this request if there are no results.
-			if (len(nodelist) == 0):
+			if (len(values) == 0):
 				continue
 
 			# Send the results.
@@ -491,21 +535,18 @@
 			    socket.SOCK_STREAM)
 		except Exception, e:
 			print >>sys.stderr, "Error creating listener socket"
-			print >>sys.stderr, str(e)
 			raise
 
 		try:
 			self.listen_sock.bind(self.listen_sock_name)
 		except Exception, e:
 			print >>sys.stderr, "Error binding receptor socket"
-			print >>sys.stderr, str(e)
 			raise
 
 		try:
 			self.listen_sock.listen(5)
 		except Exception, e:
 			print >>sys.stderr, "Error listening on receptor socket"
-			print >>sys.stderr, str(e)
 			raise
 
 		# Now wait for clients.  Start a new thread for each client.
--- a/usr/src/lib/install_utils/SocketServProtocol.py	Mon Oct 13 18:10:47 2008 +0200
+++ b/usr/src/lib/install_utils/SocketServProtocol.py	Mon Oct 13 11:47:37 2008 -0700
@@ -27,11 +27,22 @@
 # =============================================================================
 # =============================================================================
 
+# To initiate contact with the server, client sends prerequest with two fields:
+#	byte 0: "0"= not key, "1" = key
+#	byte 1 blank
+#	bytes 2:PRE_REQ_SIZE: size of the request string.
+
+PRE_REQ_SIZE = 8
+
+# Next, server sends PRE_REQ_ACK.  Then client can send the request.
+
+PRE_REQ_ACK = '\001'
+
 # REQ_COMPLETE is sent by server as a final "string" of a
 # request to note that the request (which can consist of many
 # strings) has been completed
 
-REQ_COMPLETE = '\001'
+REQ_COMPLETE = '\002'
 	
 # EMPTY_STR is sent instead of an empty string, when an empty string
 # would be returned as part of a request.  This allows clients to use
@@ -40,7 +51,7 @@
 # string which could be returned by split() if the split character is
 # at the end of the string being split.
 
-EMPTY_STR = '\002'
+EMPTY_STR = '\003'
 #
 # RECV_PARAMS_RECVD is sent by the client after it gets the count (of
 # strings being returned to fulfill a request) and size of total
@@ -51,18 +62,20 @@
 # zero, the server waits for the next request, and the client expects
 # nothing more from the current request.
 #
-RECV_PARAMS_RECVD = '\003'
+RECV_PARAMS_RECVD = '\004'
 #
 # TERM_LINK is sent by the client to terminate the socket link, after
 # all requests are done.
 # 
-TERM_LINK = '\004'
+TERM_LINK = '\005'
 #
 # String separator, placed between results in the string.
 #
 STRING_SEP ='\0'
 #
 # Protocol is as follows:
+# Client->server: Prerequest containing is_key and request size is sent.
+# Server->client: Sends PRE_REQ_ACK back to the client.
 # Client->server: Request in the form of a nodepath is sent.
 # Server->client: Send count of matching nodes, and the size of the
 #	entire results string (which includes all results).
--- a/usr/src/lib/install_utils/TreeAcc.py	Mon Oct 13 18:10:47 2008 +0200
+++ b/usr/src/lib/install_utils/TreeAcc.py	Mon Oct 13 11:47:37 2008 -0700
@@ -427,7 +427,31 @@
 		for child in element_node.childNodes:
 			if (child.nodeType == Node.TEXT_NODE):
 				value = value + child.nodeValue
-		return value.strip()
+
+		value = value.strip()
+
+		# Strip off any enveloping double or single quotes.  Such
+		# quotes may surround element values in the manifest in order
+		# to treat space characters as normal characters.  It is
+		# undesirable for the enveloping quotes to be treated as part
+		# of the string, for example, during comparisons.
+		#
+		# Note: this quote stripping isn't needed for strings stored as
+		# attribute node values as such values already have their
+		# enveloping single- or double quotes stripped off.
+		#
+		if ((len(value) > 2) and (value[0] == value[-1]) and
+		    ((value[0] == "\"") or (value[0] == "'"))):
+	
+			# Remove middle escaped quote chars for comparison.
+			non_esc = value.replace(("\\" + value[0]),"")
+
+			# if have more than the two quotes on the ends, keep all
+			# as this is a list and quotes are needed..
+		        if (non_esc.count(non_esc[0]) == 2): 
+				return value[1:-1]
+
+		return value
 
 
 	# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
@@ -446,8 +470,8 @@
 	#
 	# Raises: None
 	# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
-		for i in range(len(path_tokens)):
-			if (path_tokens[i].name == ".."):
+		for ptoken in path_tokens:
+			if (ptoken.name == ".."):
 				return True
 		return False
 
--- a/usr/src/lib/install_utils/defval-manifest.rng	Mon Oct 13 18:10:47 2008 +0200
+++ b/usr/src/lib/install_utils/defval-manifest.rng	Mon Oct 13 11:47:37 2008 -0700
@@ -145,6 +145,16 @@
 				     order for the default setting to occur. -->
 				<attribute name="skip_if_no_exist"/>
 			</optional>
+			<optional>	<!-- Default is to take default -->
+				<attribute name="empty_str">
+					<choice>
+						<value>set_default</value>
+						<value>valid</value>
+						<value>error</value>
+					</choice>
+				</attribute>
+
+			</optional>
 			<text/>
 		</element>
 	</define>
--- a/usr/src/lib/install_utils/install_utils.py	Mon Oct 13 18:10:47 2008 +0200
+++ b/usr/src/lib/install_utils/install_utils.py	Mon Oct 13 11:47:37 2008 -0700
@@ -22,6 +22,7 @@
 # Use is subject to license terms.
 #
 
+import sys
 import os
 import errno
 import select
@@ -123,6 +124,187 @@
 			print formatstr % varargs
 
 
+#==============================================================================
+#==============================================================================
+# Code used to parse an input string.  If no unescaped quotes or double-quotes
+# are seen in the string, the whole string is returned in one token.  Quotes or 
+# double-quotes enveloping tokens break the input into the enveloped tokens.
+# Quotes and double-quotes can be escaped with a \.  Unescaped quotes can be
+# used inside double-quotes, and vice versa.
+#
+# Examples:
+#   abc		-> [ abc ]		"abc" "def"	-> [ abc, def ]
+#   "abc"	-> [ abc ]		"a'bc" "d\"ef"	-> [ a'bc, d"ef ]
+#   abc def	-> [ abc, def ]		'a"bc' 'd\'ef'	-> [ a"bc, d'ef ]
+#   "abc def"	-> [ abc def ]		"abc def" "ghi"	-> [ abc def, ghi ]
+#   "a'b'c" 'd"e"f ghi'	-> [ a'b'c, d"e"f ghi ]
+#==============================================================================
+#==============================================================================
+
+# States
+(
+__qst_start,		# Start
+__qst_char,		# Most chars and whitespace
+__qst_esc,		# escape (first backslash character)
+__qst_sqt,		# Unescaped single quote
+__qst_dqt		# Unescaped double quote
+) = range(5)
+
+# State table indices.
+(
+__qst_nonesc,		# Index used by start, char, sqt and dqt states
+__qst_bsesc		# Index used by esc state
+) = range(2)
+
+# Next state indices.  These correspond to the current char which determines
+# the next state.
+(
+__qst_char_char,	# Index in the tables corresp to char or whtsp recd
+__qst_char_bs,		# Index in the tables corresp to a backslash recd
+__qst_char_sqt,		# Index in the tables corresp to a single quote recd
+__qst_char_dqt		# Index in the tables corresp to a double quote recd
+) = range(4)
+
+# The state table
+
+__qst_nonesc_tbl = []
+__qst_nonesc_tbl.insert(__qst_char_char, __qst_char)
+__qst_nonesc_tbl.insert(__qst_char_bs, __qst_esc)
+__qst_nonesc_tbl.insert(__qst_char_sqt, __qst_sqt)
+__qst_nonesc_tbl.insert(__qst_char_dqt, __qst_dqt)
+
+__qst_esc_tbl = []
+__qst_esc_tbl.insert(__qst_char_char, __qst_char)
+__qst_esc_tbl.insert(__qst_char_bs, __qst_char)
+__qst_esc_tbl.insert(__qst_char_sqt, __qst_char)
+__qst_esc_tbl.insert(__qst_char_dqt, __qst_char)
+
+__space_state_table = []
+__space_state_table.insert(__qst_nonesc, __qst_nonesc_tbl)
+__space_state_table.insert(__qst_bsesc, __qst_esc_tbl)
+
+
+# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+def __space_next_state(curr_state, curr_char):
+# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+# Find next state from space state table.  (Private function)
+# 
+# Args:
+#   curr_state: Current state.  One of the state table states listed above
+#
+#   curr_char: Current character.
+#
+# Returns: Next state.  One of the state table states listed above.
+#
+# Raises: N/A
+#
+# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+	if (curr_state == __qst_esc):
+		idx = __qst_bsesc
+	else:
+		idx = __qst_nonesc
+	if (curr_char == "\""):
+		next_state = __space_state_table[idx][__qst_char_dqt]
+	elif (curr_char == "\'"):
+		next_state = __space_state_table[idx][__qst_char_sqt]
+	elif (curr_char == "\\"):
+		next_state = __space_state_table[idx][__qst_char_bs]
+	else:
+		next_state = __space_state_table[idx][__qst_char_char]
+	return next_state
+
+
+# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+def space_parse(input):
+# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+	""" Parse an input string into words, accounting for whitespace
+	inside quoted (or double-quoted) strings.
+
+	Quotes and double-quotes can be escaped with a \.  Unescaped quotes
+	can be used inside double-quotes, and vice versa.
+
+	Unescaped quotes and double-quotes are removed from the output.
+
+	Escaped quotes and double-quotes are added to the output but their
+	escaping \ are removed.
+
+	Args:
+	  input: string to parse.
+
+	Returns: list of parsed tokens.
+
+	Raises: N/A
+	"""
+# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+	# Initialize.
+	state = __qst_start
+	word = ""
+	outlist = []
+	squoteon = False
+	dquoteon = False
+	input = input.strip()
+
+	# Examine each character individually.
+	for char in input:
+		next_state = __space_next_state(state, char)
+
+		# If previous state was escape and current char isn't a quote
+		# being escaped, treat the previous backslash as a normal
+		# character and append to the current word.
+		if ((state == __qst_esc) and
+		    ((char != "\"") and (char != "'"))):
+			word += "\\"
+
+		# Regular character
+		if (next_state == __qst_char):
+			word += char
+
+		# Escaped single quote
+		elif (next_state == __qst_sqt):
+			if (dquoteon):
+				word += char
+			else:
+				squoteon = not squoteon
+				if (not squoteon):
+					if (len(word) != 0):
+						outlist.append(word.strip())
+						word = ""
+		
+		# Escaped double quote
+		elif (next_state == __qst_dqt):
+			if (squoteon):
+				word += char
+			else:
+				dquoteon = not dquoteon
+				if (not dquoteon):
+					if (len(word) != 0):
+						outlist.append(word.strip())
+						word = ""
+
+		elif (next_state != __qst_esc):
+			print >>sys.stderr, (
+			    "space_parse: Shouldn't get here!!!! " +
+			    "Char:%s, state:%d, next:%d" % (char, state,
+			    next_state))
+
+		state = next_state
+
+	# Done looping.  Handle the case of residual mismatched quotes or
+	# double-quotes.
+	if ((squoteon) or (dquoteon)):
+		raise Exception, "Unexpected unescaped quote found: " + input
+
+	# Handle the case where the \ is the last character of the input.
+	if (state == __qst_esc):
+		word += "\\"
+
+	# Flush any remaining word in progress.
+	if (len(word) != 0):
+		outlist.append(word.strip())
+
+	return (outlist)
+
+
 # =============================================================================
 # =============================================================================
 # Other utility functions
@@ -206,35 +388,6 @@
 
 
 # ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
-def comma_ws_split(input):
-# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
-	"""Split the input string, accounting for whitepspace and commas.
-	User-friendly for lists in an XML doc.
-
-	Args:
-	  input: String to split.
-
-	Returns:
-	  list of items split out from the string, stripped of whitespace
-
-	Raises: None
-	"""
-# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
-	intermediate_list = input.strip().split()
-	out_list = []
-
-	for s in intermediate_list:
-		if (s.find(",") == -1):		# No comma
-			out_list.append(s)	# Ready to go
-	        else:
-			# If last char is comma, next split will give a ""
-			if (s[len(s) - 1] == ","):
-				s = s[:len(s) - 1]
-			if (len(s) > 0):
-				out_list.extend(s.split(","))
-	return out_list
-
-# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
 def exec_cmd_outputs_to_log(cmd, log,
     stdout_log_level=None, stderr_log_level=None):
 # ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
@@ -255,6 +408,7 @@
 
 	Raises: None
 	"""
+# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
 
 	if (stdout_log_level == None):
 		stdout_log_level = DEBUG
--- a/usr/src/pkgdefs/SUNWdistro-const/prototype_com	Mon Oct 13 18:10:47 2008 +0200
+++ b/usr/src/pkgdefs/SUNWdistro-const/prototype_com	Mon Oct 13 11:47:37 2008 -0700
@@ -94,6 +94,7 @@
 f none usr/share/distro_const/bootroot_archive.py 0555 root bin
 f none usr/share/distro_const/pre_bootroot_pkg_image_mod 0555 root bin
 f none usr/share/distro_const/post_bootroot_pkg_image_mod 0555 root bin
+f none usr/share/distro_const/grub_setup.py 0555 root bin
 f none usr/share/distro_const/create_iso 0555 root bin
 f none usr/share/distro_const/create_usb 0555 root bin
 f none usr/share/distro_const/DC-manifest.defval.xml 0444 root sys
@@ -104,7 +105,6 @@
 f none usr/share/distro_const/slim_cd/slimcd_iso.sort 0444 root sys
 f none usr/share/distro_const/slim_cd/slim_cd.xml 0444 root sys
 f none usr/share/distro_const/slim_cd/slimcd_bootroot_configure 0555 root bin
-f none usr/share/distro_const/slim_cd/slimcd_grub_a11y 0555 root bin
 f none usr/share/distro_const/slim_cd/slimcd_pre_bootroot_pkg_image_mod 0555 root bin
 f none usr/share/distro_const/slim_cd/slimcd_post_bootroot_pkg_image_mod 0555 root bin
 f none usr/share/distro_const/auto_install/ai_generic_live.xml 0444 root sys