Friday, July 21, 2006

How small can you make Open Solaris - Part 5

In the latest post I have just done a quick update of the "Quick and Dirty Solaris Installer". Version 0.3 of the installer is now able to exclude Solaris clusters and packages from the installation. With this we are now able to start reducing the size and try to get close to what was achieved in the first couple of posts.

If you read through the code you can see that I have started with the SUNWCmreq metacluster, as it is the smallest defined on the Solaris installation media. I actually had to add the package SUNWmdr, as devfsadmd started complaining during the first reboot. In the code you will see a big list of packages defined in the "exclude array". Since this list includes device drivers, you may have to add and remove driver packages for your system. To do this you will need to read through the '.clustertoc' file in the Product directory of your installation media.

Using the configuration in this script, I was able to get a bootable multi-user system which only needed /etc/nodename and /etc/hosts configured. The disk usage is down to around 149MB (still a long way to go). Solaris core packages actually have a lot of files and command which I would not call 'core'. Later on I will look at creating new packages, and pulling files directly from a Open Solaris bfu archive. This may also require some files being pulled from "running" Solaris, as the bfu archives are not complete. If we can successfully use a bfu archive, then we could later see if changing some of the compiler flags in an Open Solaris build will shrink the binary size.

In the next post, I will start reducing installation further, by adding commands to the local_install.bash script which can be run just before the filesystem is un-mounted.


#!/bin/bash
#
# Quick and Dirty Solaris installer
# Version 0.3
#
PROD=/cdrom/sol_11_x86/Solaris_11/Product
#PROD=/a/Solaris_11/Product
TOK=${PROD}/.clustertoc
ORDER=${PROD}/.order

# Current metaclusters are
# SUNWCXall - Entire Distribution plus OEM support
# SUNWCall - Entire Distribution
# SUNWCprog - Developer System Support
# SUNWCuser - End User System Suppor
# SUNWCreq - Core System Support
# SUNWCrnet - Reduced Networking Core System Support
# SUNWCmreq - Minimal Core System Support
METACLUSTER=SUNWCmreq
export METACLUSTER

SWAP=/dev/dsk/c0d0s1
LOG=/tmp/install.log
SVCPROFILE=generic_limited_net.xml

#
# Change these to relect your system.
#
FS=zfs
[ "${FS}" != "zfs" -a "${FS}" != "ufs" ] && {
printf "Unknown filesystem ${FS}\n"
exit 1
}

[ "${FS}" = "zfs" ] && {
ROOTDEV=intdisk/snv43_zfs
RAWROOTDEV=-
GRUBFS=/dev/dsk/c0d0s0
ZFSBOOTARCHIVE=/grub/boot/boot_zfs
ZFSCOMPRESS=off
# ZFSCOMPRESS=on
MOB=yes
}

[ "${FS}" = "ufs" ] && {
ROOTDEV=/dev/dsk/c0d0s3
RAWROOTDEV=${ROOTDEV/dsk/rdsk}
MOB=no
}

typeset -a pkgs
# extrapkgs is an array of additional packages to be installed
typeset -a extrapkgs=( SUNWmdr )
# exclude is an array of packages or clusters which should not be installed
typeset -a exclude=( SUNWChbaapi SUNWCfcadb SUNWCfca SUNWCfcadb SUNWCfct
SUNWCfutil SUNWCiscsi SUNWCib SUNWCmpapi SUNWCtavor
CADP160 HPFC SK98sol SUNWaac SUNWadp SUNWadpu320 SUNWamr SUNWcadp SUNWced
SUNWchxge SUNWcqhpc SUNWlsimega SUNWmv88sx SUNWnge SUNWrge SUNWrmodr
SUNWrmodu SUNWrtls SUNWses SUNWsi3124 SUNWuksp SUNWuedg SUNWukspfw
SUNWuprl SUNWxge SYMhisl SKfp SUNWintgige SUNWwbsup
SUNWCpkgcmds SUNWjss SUNWidnl SUNWbzip
SUNWperl584core SUNWperl584usr )

#
# check_install tests whether $1 is in the list of packages to be excluded
#
function check_install() {
local i
[ "$#" != "1" ] && return 0

for i in ${exclude[@]} ; do
[ ${i} = "$1" ] && return 1
done
return 0
}

#
# add_extra_pkgs will add in the extrapkgs array to the pkgs array. It will
# void adding a package twice
#
function add_extra_pkgs() {
pkgcnt=${#pkgs[@]}
for i in ${extrapkgs[@]} ; do
found=0
for j in ${pkgs[@]} ; do
[ ${i} = ${j} ] && {
found=1
break;
}
done
# Add package if not already found
[ "${found}" = "0" ] && {
printf "Adding Package - %s [%d]\n" "${i}" ${pkgcnt}
pkgs[$(( pkgcnt++ ))]="$i"
}
done
}

#
# Solaris packages need to be installed in the correct order.
# The .order file contains all the packages in the correct
# installation order
#
function reorder_pkgs() {
typeset -a pkglist=( ${pkgs[@]} )
pkgcnt=0

while read order_pkg ; do
for i in ${pkglist[@]} ; do
[ "$order_pkg" = "${i%.i}" ] && {
pkgs[$(( pkgcnt++ ))]="${i}"
printf "."
}
done
done < ${ORDER}
}

#
# This function builds a list of packages in a cluster
# If there is a cluster within a cluster, it will call itself to
# resolve all the packages.
#
# Before calling make sure you initialize pkgcnt to 0
# Arg: $1 contains the cluster name
# Affected vars: pkgs, pkgcnt
#
function get_pkg_list() {
local IFS="="
local print_on=0
local cluster=$1

while read arg1 arg2
do
[ "${arg1}" = "END" -a "${print_on}" = "1" ] && break;
[ -z "${arg2}" ] && continue;

[ "${arg2}" = "${cluster}" ] && {
print_on=1
continue
}

[ "${print_on}" = "1" -a "${arg1}" = "SUNW_CSRMEMBER" ] && {
# Test to see if package/cluster is on the exclude list
check_install "${arg2}" || continue

ifcluster=`expr "${arg2}" : '\(SUNWC\)'`
if [ "${ifcluster}" = "SUNWC" ]; then
get_pkg_list ${arg2}
else
[ -d ${PROD}/${arg2} ] && {
pkgs[$(( pkgcnt++ ))]="${arg2}"
printf "."
continue;
}

arg2="${arg2}.i"
[ -d ${PROD}/${arg2} ] && pkgs[$(( pkgcnt++ ))]="${arg2}"
printf "."
fi
}
done < ${TOK}
}

#
# Check for the installation image before proceeding
#
[ ! -d ${PROD} ] && {
echo "Cannot find Solaris Installation"
exit 1
}

#
# Create a pkg admin file - see man admin(4)
#
sed 's/ask/nocheck/' /var/sadm/install/admin/default > /tmp/.admin.doit

#
# Build an ordered list of packages from the Solaris installation image
#
printf "Building a list of packages "
pkgcnt=0
get_pkg_list ${METACLUSTER}
echo
# Add extra packages before sorting
add_extra_pkgs
echo
printf "Sorting packages into the correct order for installation "
reorder_pkgs
echo
#
# Try to create the filesystem you define at the beginning
#
case ${FS} in
"zfs") zfs create ${ROOTDEV} || {
echo "Cannot create zfs filesystem!"
exit 1
}
zfs set mountpoint=legacy ${ROOTDEV}
zfs set compression=${ZFSCOMPRESS} ${ROOTDEV}
;;
"ufs") newfs ${ROOTDEV} || {
echo "Cannot create ufs filesystem!"
exit 1
} ;;
*) echo "Cannot create ufs filesystem!"
exit 1
;;
esac

mount -F ${FS} ${ROOTDEV} /mnt || {
echo "Cannot mount filesystem!"
exit 1
}

#
# Install packages from Solaris installation image
#
echo "Starting installation of packages"
echo
(
for i in ${pkgs[@]} ; do
pkgadd -n -a /tmp/.admin.doit -d ${PROD} -R /mnt $i
done
) > ${LOG}

#
# Update /etc/vfstab with swap and root partitions
#
(
printf "${SWAP}\t-\t-\tswap\t-\tno\t-\n"
printf "${ROOTDEV}\t${RAWROOTDEV}\t/\t${FS}\t1\t${MOB}\t-\n"
[ "$FS" = "zfs" ] && {
mkdir -m 0755 /mnt/grub
printf "${GRUBFS}\t${GRUBFS/dsk/rdsk}\t/grub\tufs\t3\tyes\t-\n"
}
) >> /mnt/etc/vfstab

#
# Copy links for disk partitions in /dev/dsk and /dev/rdsk
# This is needed so the system can find the root partion on boot
#
( cd /dev && find dsk rdsk -depth | cpio -pdm /mnt/dev 2>/dev/null )

#
# Configure system to initialize identity on first boot
# If there is a sysidcfg file in the current directory. This will
# be copied across.
#
PROFILEDIR=/mnt/var/svc/profile
[ -f ${PROFILEDIR}/${SVCPROFILE} ] && {
if [ -f ./sysidcfg ]; then
cp ./sysidcfg /mnt/etc
else
touch /mnt/etc/.UNCONFIGURED
fi
cp -p ${PROFILEDIR}/${SVCPROFILE} ${PROFILEDIR}/generic.xml
}

#
# set bootpath to root filesystem.
# Also set the console to text
#
(
[ "${FS}" = "ufs" ] && {
BOOTPATH=$( ls -l ${ROOTDEV} | nawk '{print $11}' |
sed -e 's#[./]*/devices/#/#' )

printf "setprop bootpath ${BOOTPATH}\n"
}
printf "setprop console 'text'\n"
) >> /mnt/boot/solaris/bootenv.rc

#
# If found execute local script before /mnt is unmounted
#
[ -x ./local_install.bash ] && ./local_install.bash

#
# Finish off installation
#
[ -f /etc/zfs/zpool.cache ] && {
cp -p /etc/zfs/zpool.cache /mnt/etc/zfs
echo "etc/zfs/zpool.cache" >> /mnt/boot/solaris/filelist.ramdisk
}

#
# Configure for a ZFS boot. At the moment you need a small UFS partition
# somewhere for grub.
#
[ "${FS}" = "zfs" ] && {
(
printf "rootfs:zfs\n"
printf "zfsroot:${ROOTDEV}\n"
) >> /mnt/etc/system
}

devfsadm -r /mnt
rm -f /mnt/reconfigure
bootadm update-archive -R /mnt

[ "${FS}" = "zfs" ] && {
cp /mnt/platform/i86pc/boot_archive ${ZFSBOOTARCHIVE}
cp -p /mnt/sbin/bootadm /mnt/sbin/bootadm.real
cat >/mnt/sbin/bootadm << EOM
#!/usr/bin/sh

/sbin/bootadm.real "\$@"
if [ "\$1" = "update-archive" -a -d /grub/boot/grub ]; then
/usr/bin/cp /platform/i86pc/boot_archive ${ZFSBOOTARCHIVE}
fi
exit 0
EOM
}

echo
echo "If you have not already, you will need to configure menu.lst to"
echo "boot this partition."

umount /mnt
# eject cdrom

No comments: