Saturday, July 08, 2006

How small can you make Open Solaris - Part 2

In the first part of the article I decided to use the failsafe miniroot as a base for a minimal Solaris. I also described how to mount and unmount a copy of the miniroot image which I termed microroot. You should be very comfortable with doing this before tackling this article. I will first start with a quick analysis of the miniroot before we start jump in and start removing files. It is really important to find out what is required by the operating system before you weild the 'rm' axe.

From playing around in the last article you would have realised that failsafe stops at the single user milestone. So it would be really nice to see what file SMF executes to get to the shell prompt. After you get the shell prompt we should look at what processes are running, and what dynamic libraries are in use. This will give us an idea of what needs to be kept.

Lets start - In normal Solaris as the root user create a directory called 'work' in your root partition. This directory will later be used to transfer files between the microroot and normal solaris. In that directory create a script "report.sh" from the following listing. Other than listing services, and processes. The horrible munge of shell scripting goes through the process table listing share libraries and objects used be each process. It then sorts and does a 'unique', to give you a nice listing of libraries and objects that will be required to keep.

mkdir /work
vi /work/report.sh

#!/sbin/sh

echo "Shared libraries in use"
echo "-----------------------"
(
/usr/bin/ps -eo pid | /usr/bin/awk '
$1 != "PID" {
printf "/usr/bin/pmap -x %s\n",$1
}' | /sbin/sh 2>/dev/null
) | /usr/bin/awk '{ print $7 }' | /usr/bin/grep '[.]so' | /usr/bin/sort -u

echo
echo "Services Status"
echo "---------------"
/usr/bin/svcs

echo
echo "Process Table"
echo "-------------"
/usr/bin/ps -eo user,pid,comm


Now we are ready to reboot and run the report. At the grub prompt you can either select the failsafe or the microroot option from part 1 as they should be identical at this point. Instead of pressing ENTER to boot press 'e' for edit. This should bring up a screen with the entries from your menu.lst file in the grub directory. Using the arrow keys move down to the line starting with 'kernel' and press 'e' again. At the end of the line add the '-m debug' option. This option will send 'smf' into debug mode filling your screen with information when you boot. Don't worry any changes made here as not save back to the menu.1st file. Press ENTER and then 'b' to boot.

During the boot smf should be printing out a huge amount of information. Don't panic if you can not speed read, the information you need is at the end. You will see that the last thing smf does is related to install-discovery.xml file. Later if you look at this file you will find it runs the script /sbin/install-discovery. This is a file of great interest and we will definitely attack it.

At this point you are being asked to mount your normal Solaris partition on /a. Answer 'y' for yes. Now run the report and put the results back into your work directory.

cd /
/sbin/sh /a/work/report.sh > /a/work/report.txt

This should not take long to run. When it has finished reboot in to normal Solaris and review the report.

In the report you should see around 36 libraries/objects listed. The process table contains a small number of processes running. They seem perfectly reasonable, so we will leave them alone. Now mount the microroot onto /mnt and run a the du command on it. Note: I have truncated the output.

du -ks * .??* | sort -nr
89502 usr
24961 kernel
10857 lib
2230 platform
1317 etc
1215 sbin
1156 boot
227 .tmp_proto

As you may expect, from the ouput of the du command you will see that /usr, /kernel, /lib, /platform are the biggest disk space users. Since /kernel and /platform are the Solaris Kernel we will leave these alone. At the end if you want a smaller image, there is plenty of scope for removing unused driver modules. As most people would expect, it will be /usr and /lib, that will first come under the knife. You might find it strange that /tmp is not empty, and there is a .tmp_proto directory. This is because the Solaris image you are building unlike Solaris on your system is not writeable. The microroot will make use of tmpfs filesystem for files which need to be written to. You can see this by doing a 'ls -l /mnt/etc/vfstab'. If you want to know more, just look through the install-discovery script.

Now the first time I cutting sections out, I just went in to a directory, had a look and if I did not think the file or directory was relevent I attacked it with the 'rm' command. Then I rebooted to test the changes. After doing this a few times, I thought it would be better to copy the commands into an editor so I could easily repeat the whole process from scratch if needed.

The next step is to copy /work/report.txt to /work/libs. Edit this file and remove everything except for the libraries and share objects. You will notice that some libraries end with .so and others end with '.so.1' etc. This is the version number of the shared library. With the editor remove the version from the end of the lines. Now all the lines should now end with '.so'. We will use the file later with fgrep to identify the libraries and links we want to keep.

It is now time to get to the fun part. Choose your weapon, 'as were going Hunting', er sorry - 'deleting'. I have listed the command so you can place them into a script at your leisure.

Lets start of with a no brainer and remove some obvious directories. No real space is gained yet but it may make you feel better...

cd /mnt
rm -rf /mnt/boot /mnt/cdrom /mnt/opt
Remove all locale's except for 'C' from /usr/lib/locale.
cd /mnt/usr/lib/locale && rm -rf [a-z]* POSIX
Now take the axe to anything that should not be required. Though make sure you leave anything related to devfsadm, sysevent, and booting the system alone. 'ls' does not need networking so while your at it take out anything network related. This is a script right! You can fix it later. ( The next part is not intended to look like uuencoding or base64. It just came out that way!)

cd /mnt/usr/lib && rm -rf zones zfs vplot term tabset t[0-9]* sunw,rcp spell
cd /mnt/usr/lib && rm -rf rcm print patch nss_nisplus.so.1 install ldap krb5
cd /mnt/usr/lib && rm -rf iconv inet crypto cron diff3prog diffh dns expreserve
cd /mnt/usr/lib && rm -rf netsvc newsyslog nfs nis nscd_nischeck nss_compat.so.1
cd /mnt/usr/lib && rm -rf nss_ldap.so.1 nss_nis.so.1 passwdutil.so.1 calprog
cd /mnt/usr/lib && rm -rf flash fp getoptcvt gmsgfmt help intrd lddstub libc
cd /mnt/usr/lib && rm -rf lp* lvm localedef lwp makekey more.help mps pt_chmod
cd /mnt/usr/lib && rm -rf fs/pcfs fs/fd fs/cachefs fs/nfs security/pam_krb5* drv
cd /mnt/usr/lib && rm -rf pam_dial* pam_ldap* pam_sample* platexec embedded_su
cd /mnt/usr/lib && rm -rf mdb smartcard sasl rsh kssladm ll* gss exrecover
cd /mnt/usr/lib && rm -rf nss_dns.so.1 abi adb class link_audit saf utmp* lib.b

Ok we need to copy the libraries we want to keep, and move them back once we have been brutal with 'rm'. It is now time to use the /work/libs file for input to fgrep. The logic of this script could be re-done, but it does the job for now.

cd /mnt/usr/lib
mkdir .bak
for i in `ls *[.]so*` ; do echo $i |fgrep -f /work/libs | cpio -pdm .bak 2>/dev/null ; done
rm -f *[.]so*
mv .bak/* .
rm -rf .bak

Move up one level and take the broad axe to /usr. I will leave /usr/bin and /usr/sbin to your discretion.

cd /mnt/usr && rm -rf X X11 adm ccs dict dt java kernel kvm mail net
cd /mnt/usr && rm -rf platform preserve pub sadm sfw share snadm spool
cd /mnt/usr && rm -rf news old openwin perl5 src xpg4 proc

For the second time use the /works/libs file, but this time on /lib.

cd /mnt/lib
mkdir .bak
for i in `ls *[.]so*` ; do echo $i |fgrep -f /work/libs | cpio -pdm .bak 2>/dev/null ; done
rm -f *[.]so*
mv .bak/* .
rm -rf .bak

The final directory we will attack before we build the image and reboot will be /sbin

cd /mnt/sbin && rm -rf rc* install* ifconfig ifparse getpart getmemory zpool
cd /mnt/sbin && rm -rf rc* biosdev sysid* meta* p* d* e* route* suninstall jsh
cd /mnt/sbin && rm -rf bootadm bpgetfile cleanup_hosts getInstallLangs swap*
cd /mnt/sbin && rm -rf getbootargs grepInstalledLocales hostconfig in.mpathd
cd /mnt/sbin && rm -rf mkmenu mountall netstrategy setup* selection siwrapper
cd /mnt/sbin && rm -rf soconfig umountall zfs zonename getconsole get_netmask

Now create 2 files. We need bootadm to just exit. The install-discovery file is just the original stripped down (remove the installation code) and compressed to fit in the blog.
vi /mnt/sbin/bootadm

#!/sbin/sh
exit 0

vi /mnt/sbin/install-discover

#!/sbin/sh
# Copyright 2005 Sun Microsystems, Inc. All rights reserved
# Use is subject to license terms.
SHELL=/sbin/sh;export SHELL
PATH=/sbin:/usr/bin:${PATH};export PATH
PLATFORM=`/sbin/uname -p`;export PLATFORM
_INIT_RECONFIG=set; export _INIT_RECONFIG #Dont know what this does
exec /dev/console 2>&1
/sbin/mount -F tmpfs swap /tmp
if [ $? -ne 0 ]; then
echo "tmpfs mount failed."
/sbin/sh
fi
( cd /.tmp_proto; find . -print -depth | cpio -pdm /tmp 2>/tmp/cpio.out )
echo "Memory free after tmpfs initialization: `/sbin/mem`"
echo "swap - /tmp tmpfs - no -" >> /etc/vfstab
echo "/proc - /proc proc - no -" >> /etc/vfstab
find dev -depth -print | cpio -pdum /tmp >/dev/null 2>&1
ln -sf /devices /tmp/devices
/sbin/mount -F lofs -O /tmp/dev /dev
mkdir -p /tmp/etc
mkdir -p /tmp/etc/sysevent&&/usr/lib/sysevent/syseventd -r /tmp
/usr/lib/devfsadm/devfsadmd -r /tmp -p /tmp/root/etc/path_to_inst
eval `/sbin/get_root -t Roottype -b Rootfs /`
echo "${Rootfs} - / ${Roottype} - no ro" >> /etc/vfstab
echo
echo "Welcome to Super Small Solaris"
echo
echo "Dont expect your normal list of command."
echo "Just try 'ls /sbin' and you will find all you want."
echo
exec /sbin/sh


After just have made the scripts executable you should unmount /mnt and commit the changes to the image on /grub. The instructions below will backup the filesystem, and restore it onto a smaller UFS image. If you do not do this you will most likely find that your image has actually grown, as the UFS partition is the same size.

chmod 755 /mnt/sbin/bootadm /mnt/sbin/install-discovery
cd /
du -ks /mnt
# Take note of the size returned and add 15%
sync
ufsdump 0f /work/x86.microroot.dmp /mnt
umount /mnt
lofiadm -d /tmp/x86.microroot
mkfile k /tmp/x86.microroot
DEV="`lofiadm -a /tmp/x86.microroot`"
newfs -m 0 $DEV
mount -F ufs $DEV /mnt
cd /mnt
ufsrestore -rf /work/x86.microroot.dmp
rm restoresymtable
cd /
umount /mnt
lofiadm -d /tmp/x86.microroot
gzip -c /tmp/x86.microroot > /boot/x86.microroot

The last step we should do is to create an iso image to try out on a CD

cd /tmp
mkdir iso
cp -pr /boot /tmp/iso
rm /tmp/iso/boot/x86.miniroot-safe
rm /tmp/iso/boot/boot_archive

cat > /tmp/iso/boot/grub/menu.lst << EOM
default 0
timeout 10
splashimage /boot/grub/splash.xpm.gz
title Solaris Microroot
kernel /boot/multiboot kernel/unix -s
module /boot/x86.microroot
EOM

mkisofs -R -b boot/grub/stage2_eltorito -no-emul-boot boot-load-size 4 -boot-info-table -o /work/microroot.iso iso

At the end of all of this you should now have a 25 megabyte iso image in /work. For the moment I will stop here. Before I have gone further and been able to reduce the iso to 18 megabytes which still includes the full 32bit kernel from the latest Open Solaris builds. The next part of the article we will see if we can do the same thing using scripts, getting the files straight from an Open Solaris build. This involves just a little more work as we have to tackle smf and also build our own /devices and /dev directories.

Good Luck. Let me know how you go!

5 comments:

Doug Scott said...

Sorry, if some of the code formatting did not come out properly. While web based editors have come along way, they are still extremely rough compared to something like Open Office. It would be nice if blogs could be submitted in ODF format :-)

Thats my bitch for the day....

Anonymous said...

Here's some trickery simplifying the
investigation script.

# show all shared libraries in use
pmap -x /proc/* |nawk '/\.so/ { print $7 }' |sort -u

Never use awk and grep in a single pipeline!
:-)

Anonymous said...

Doug,

Jump into the blogger setup and turn on RSS/Atom feed - Please

All the best,
Verta :-)

Doug Scott said...

Verta,
I have been through every setup menu I can find in blogger and I have not been able to find that option. Most likely they would like me to pay money... Grrrr

I did find this ATOM URL though - http://solaristhings.blogspot.com/atom.xml

BK said...

pldd is does less work than "pmap -x" so it's faster and allows you to use just grep instead of awk.

# pldd /proc/* | grep '\.so\.' | sort -u