Supermin appliance.
authorRichard Jones <rjones@redhat.com>
Thu, 22 Oct 2009 11:31:05 +0000 (12:31 +0100)
committerRichard Jones <rjones@redhat.com>
Thu, 22 Oct 2009 11:31:05 +0000 (12:31 +0100)
This ports the libguestfs supermin appliance work into febootstrap
to make it more generally available to anyone who wants to use it.

.gitignore
Makefile.am
febootstrap-supermin-helper.pod [new file with mode: 0644]
febootstrap-supermin-helper.sh [new file with mode: 0755]
febootstrap-to-supermin.pod [new file with mode: 0644]
febootstrap-to-supermin.sh [new file with mode: 0755]
febootstrap.pod

index 24fc34d..a430f1f 100644 (file)
@@ -16,6 +16,8 @@ febootstrap-run
 febootstrap-install
 febootstrap-minimize
 febootstrap-to-initramfs
+febootstrap-to-supermin
+febootstrap-supermin-helper
 install-sh
 missing
 stamp-h1
index b239bbf..861b5e0 100644 (file)
@@ -24,7 +24,9 @@ bin_SCRIPTS = \
        febootstrap-run \
        febootstrap-install \
        febootstrap-minimize \
-       febootstrap-to-initramfs
+       febootstrap-to-initramfs \
+       febootstrap-to-supermin \
+       febootstrap-supermin-helper
 DISTCLEANFILES = $(bin_SCRIPTS)
 
 febootstrap: febootstrap.sh
@@ -57,12 +59,26 @@ febootstrap-to-initramfs: febootstrap-to-initramfs.sh
        chmod 0555 $@-t
        mv $@-t $@
 
+febootstrap-to-supermin: febootstrap-to-supermin.sh
+       rm -f $@
+       cp $< $@-t
+       chmod 0555 $@-t
+       mv $@-t $@
+
+febootstrap-supermin-helper: febootstrap-supermin-helper.sh
+       rm -f $@
+       cp $< $@-t
+       chmod 0555 $@-t
+       mv $@-t $@
+
 man_MANS = \
        febootstrap.8 \
        febootstrap-run.8 \
        febootstrap-install.8 \
        febootstrap-minimize.8 \
-       febootstrap-to-initramfs.8
+       febootstrap-to-initramfs.8 \
+       febootstrap-to-supermin.8 \
+       febootstrap-supermin-helper.8
 
 if HAVE_PERLDOC
 
@@ -116,6 +132,26 @@ febootstrap-to-initramfs.8: febootstrap-to-initramfs.pod
 febootstrap-to-initramfs.txt: febootstrap-to-initramfs.pod
        pod2text $< > $@
 
+febootstrap-to-supermin.8: febootstrap-to-supermin.pod
+       pod2man \
+         --section 8 \
+         -c "Virtualization Support" \
+         --release "$(PACKAGE_NAME)-$(PACKAGE_VERSION)" \
+         $< > $@
+
+febootstrap-to-supermin.txt: febootstrap-to-supermin.pod
+       pod2text $< > $@
+
+febootstrap-supermin-helper.8: febootstrap-supermin-helper.pod
+       pod2man \
+         --section 8 \
+         -c "Virtualization Support" \
+         --release "$(PACKAGE_NAME)-$(PACKAGE_VERSION)" \
+         $< > $@
+
+febootstrap-supermin-helper.txt: febootstrap-supermin-helper.pod
+       pod2text $< > $@
+
 endif
 
 EXTRA_DIST = \
@@ -132,4 +168,10 @@ EXTRA_DIST = \
          febootstrap-minimize.sh \
        febootstrap-to-initramfs.8 febootstrap-to-initramfs.txt \
          febootstrap-to-initramfs.pod \
-         febootstrap-to-initramfs.sh
+         febootstrap-to-initramfs.sh \
+       febootstrap-to-supermin.8 febootstrap-to-supermin.txt \
+         febootstrap-to-supermin.pod \
+         febootstrap-to-supermin.sh \
+       febootstrap-supermin-helper.8 febootstrap-supermin-helper.txt \
+         febootstrap-supermin-helper.pod \
+         febootstrap-supermin-helper.sh
diff --git a/febootstrap-supermin-helper.pod b/febootstrap-supermin-helper.pod
new file mode 100644 (file)
index 0000000..d39d8bd
--- /dev/null
@@ -0,0 +1,105 @@
+=head1 NAME
+
+febootstrap-supermin-helper - Reconstruct initramfs from supermin appliance.
+
+=head1 SYNOPSIS
+
+ febootstrap-supermin-helper supermin.img hostfiles.txt kernel initrd
+
+=head1 DESCRIPTION
+
+I<febootstrap-supermin-helper> reconstructs a bootable kernel and
+initramfs from a supermin appliance.  First you should be familiar
+with L<febootstrap(8)>, L<febootstrap-to-initramfs(8)> and
+L<febootstrap-to-supermin(8)>.
+
+=head1 PARAMETERS
+
+Of the four parameters, the first two are I<input> files, and the last
+two are I<output> files.
+
+C<supermin.img> and C<hostfiles.txt> are the input files which
+describe the supermin appliance.
+
+C<kernel> and C<initrd> are the temporary output files that this
+script produces.  These output files are meant to be used just for
+booting the appliance, and should be deleted straight afterwards.
+
+=head1 OPTIONS
+
+=over 4
+
+=item B<--kmods file>
+
+If this option is specified, then C<file> should be a list of
+wildcards matching kernel module names, eg:
+
+ virtio*.ko
+ scsi*.ko
+ piix.ko
+
+In this case, only kernel modules matching those wildcards will be
+included in the output appliance.  Note: You must resolve any
+dependencies yourself as this does not pull in dependent modules
+automatically.
+
+If this option is not specified, then every kernel module from the
+host will be included.  This is safer, but can produce rather large
+appliances which need a lot more memory to boot.
+
+=back
+
+=head1 SPEED
+
+In libguestfs, on a mid-range Intel-based PC, we reconstruct the
+initramfs using this script in around 1/5th of a second (assuming a
+"hot cache" - it's rather slower when run the first time on a cold
+cache).
+
+Some tips to improve performance:
+
+=over 4
+
+=item *
+
+Use a kernel module whitelist (the C<--kmods> option), and only
+list the kernel modules you really need.
+
+=item *
+
+Minimize the appliance, removing as much extraneous junk as possible.
+
+As well as using L<febootstrap-minimize(8)> it is worth checking for
+anything that is not necessary for your particular application and
+removing it by hand.
+
+=back
+
+=head1 SEE ALSO
+
+L<febootstrap(8)>,
+L<febootstrap-to-initramfs(8)>,
+L<febootstrap-to-supermin(8)>.
+
+=head1 AUTHORS
+
+Richard W.M. Jones <rjones @ redhat . com>
+
+=head1 COPYRIGHT
+
+(C) Copyright 2009 Red Hat Inc.,
+L<http://et.redhat.com/~rjones/febootstrap>.
+
+This program is free software; you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation; either version 2 of the License, or
+(at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program; if not, write to the Free Software
+Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
diff --git a/febootstrap-supermin-helper.sh b/febootstrap-supermin-helper.sh
new file mode 100755 (executable)
index 0000000..c2088bf
--- /dev/null
@@ -0,0 +1,132 @@
+#!/bin/bash -
+# febootstrap-supermin-helper
+# (C) Copyright 2009 Red Hat Inc.
+#
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation; either version 2 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, write to the Free Software
+# Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+#
+# Written by Richard W.M. Jones <rjones@redhat.com>
+
+unset CDPATH
+
+TEMP=`getopt \
+        -o '' \
+        --long help,kmods: \
+        -n febootstrap-supermin-helper -- "$@"`
+if [ $? != 0 ]; then
+    echo "febootstrap-supermin-helper: problem parsing the command line arguments"
+    exit 1
+fi
+eval set -- "$TEMP"
+
+usage ()
+{
+    echo "Usage: febootstrap-supermin-helper supermin.img hostfiles.txt kernel initrd"
+    echo "Please read febootstrap-supermin-helper(8) man page for more information."
+}
+
+kmods=""
+
+while true; do
+    case "$1" in
+       --help)
+           usage
+           exit 0;;
+       --kmods)
+           kmods=$2
+           shift 2;;
+       --)
+           shift
+           break;;
+       *)
+           echo "Internal error!"
+           exit 1;;
+    esac
+done
+
+if [ $# -ne 4 ]; then
+    usage
+    exit 1
+fi
+
+set -e
+
+# Input files.
+supermin="$1"
+hostfiles="$2"
+
+# Output files.
+kernel="$3"
+initrd="$4"
+
+rm -f "$kernel" "$initrd"
+
+# Kernel:
+# Look for the most recent kernel named vmlinuz-*.<arch>* which has a
+# corresponding directory in /lib/modules/. If the architecture is x86, look
+# for any x86 kernel.
+#
+# RHEL 5 didn't append the arch to the kernel name, so look for kernels
+# without arch second.
+
+arch=$(echo "@host_cpu@" | sed 's/^i.86$/i?86/')
+kernels=$(ls -1vr /boot/vmlinuz-*.$arch* 2>/dev/null | grep -v xen; ls -1vr /boot/vmlinuz-* 2>/dev/null | grep -v xen)
+for f in $kernels; do
+    b=$(basename "$f")
+    b=$(echo "$b" | sed 's,vmlinuz-,,')
+    modpath="/lib/modules/$b"
+    if [ -d "$modpath" ]; then
+        ln -sf "$f" "$kernel"
+        break
+    fi
+    modpath=
+done
+
+if [ -z "$modpath" ]; then
+    echo "$0: failed to find a suitable kernel" >&2
+    exit 1
+fi
+
+# The initrd consists of these components:
+# (1) The base skeleton appliance that we constructed at build time.
+#     format = plain cpio (could be compressed cpio)
+# (2) The modules from modpath which are on the module whitelist.
+#     format = plain cpio
+# (3) The host files which match wildcards in hostfiles.
+#     format = plain cpio
+
+cp "$supermin" "$initrd" ;# (1)
+
+# Kernel modules (2).
+
+if [ -n "$kmods" ]; then
+    exec 5<"$kmods"
+    whitelist=
+    while read kmod 0<&5; do
+       whitelist="$whitelist -o -name $kmod"
+    done
+    exec 5<&-
+else
+    whitelist="-o -name *.ko"
+fi
+
+find "$modpath" \( -not -name '*.ko' $whitelist \) -a -print0 |
+  cpio --quiet -o -0 -H newc >> "$initrd"
+
+# Host files (3).
+
+hostfiles=$(readlink -f "$hostfiles")
+(cd / &&
+    ls -1df $(cat "$hostfiles") 2>/dev/null |
+    cpio --quiet -o -H newc ) >> "$initrd"
diff --git a/febootstrap-to-supermin.pod b/febootstrap-to-supermin.pod
new file mode 100644 (file)
index 0000000..6263f51
--- /dev/null
@@ -0,0 +1,193 @@
+=head1 NAME
+
+febootstrap-to-supermin - Convert febootstrap root to supermin appliance.
+
+=head1 SYNOPSIS
+
+ febootstrap-to-supermin DIR supermin.img hostfiles.txt
+
+=head1 DESCRIPTION
+
+I<febootstrap-to-supermin> converts the filesystem created by
+L<febootstrap(8)> into a supermin appliance.  The term "supermin
+appliance" is described in the documentation below.  First you should
+be familiar with L<febootstrap(8)> and L<febootstrap-to-initramfs(8)>.
+
+=head1 PARAMETERS
+
+C<DIR> is the directory created by febootstrap (ie. the output of
+febootstrap and the input to this program).
+
+C<supermin.img> is the name of the supermin appliance that this
+program creates, and C<hostfiles.txt> is the name of the list of
+hostfiles that this program creates.  (ie. the outputs of this
+program).
+
+=head1 SUPERMIN APPLIANCE
+
+A supermin appliance is a very specialized, highly minimized
+appliance which can be reconstructed on-the-fly at runtime into
+an ordinary (initramfs) appliance.
+
+The normal appliance is a self-contained Linux operating system, based
+on the Fedora/RHEL/CentOS Linux distro.  So it contains a complete
+copy of all the libraries and programs needed, like kernel, libc,
+bash, coreutils etc etc.
+
+The supermin appliance removes the kernel and all the executable
+libraries and programs from the appliance.  That just leaves a
+skeleton of directories, config files and some data files, which is
+obviously massively smaller than the normal appliance.  At runtime we
+rebuild the appliance on-the-fly from the libraries and programs on
+the host (eg. pulling in the real /lib/libc.so, the real /bin/bash
+etc.)
+
+Although this process of rebuilding the appliance each time sounds
+slow, it turns out to be faster than using a prebuilt appliance.
+(Most of the saving comes from not compressing the appliance - it
+transpires that decompressing the appliance is the slowest part of the
+whole boot sequence).  On my machine, a new appliance can be built in
+under a fifth of a second, and the boot time is several seconds
+shorter.
+
+The big advantage of the supermin appliance for distributions like
+Fedora is that it gets security fixes automatically from the host, so
+there is no need to rebuild the whole appliance for a security update
+in some underlying library.
+
+There are several I<disadvantages>:
+
+It won't work at all except in very narrow, controlled cases like the
+Fedora packaging case.  We control the dependencies of the appliance
+RPM tightly to ensure that the required binaries are actually present
+on the host.
+
+Furthermore there are certain unlikely changes in the packages on the
+host which could break a supermin appliance, eg. an updated library
+which depends on an additional data file.
+
+Also supermin appliances are subjected to changes in the host kernel
+which might break compatibility with qemu -- these are, of course,
+real bugs in any case.
+
+Lastly, supermin appliances really can't be moved between branches of
+distributions (eg. built on Fedora 12 and moved to Fedora 10) because
+they are not self-contained and they rely on certain libraries being
+around.  You shouldn't do this anyway.
+
+Use supermin appliances with caution.
+
+=head2 ANATOMY OF A SUPERMIN APPLIANCE
+
+A supermin appliance consists of two files:
+
+=over 4
+
+=item supermin.img
+
+The image file (conventionally called C<supermin.img>, but you can
+call it anything you want) is the skeleton initramfs.  This is like an
+initramfs built by L<febootstrap-to-initramfs(8)>, but all libraries
+and binaries are removed.
+
+=item hostfiles.txt
+
+This plain text file contains a list of files that we need to add back
+from the host at runtime.  ie. It's the list of libraries and binaries
+that we removed when we constructed C<supermin.img>.
+
+This file usually contains wildcards.  This is because we don't
+want the file to break on minor updates to libraries, so for example
+instead of listing
+
+ lib64/libreadline.so.6.1.2
+
+the file contains
+
+ lib64/libreadline.so.6.*
+
+=back
+
+=head2 RECONSTRUCTING AN INITRAMFS FROM A SUPERMIN APPLIANCE
+
+The program L<febootstrap-supermin-helper(8)> can be used to
+reconstruct a full initramfs from C<supermin.img> and C<hostfiles.txt>
+(plus, naturally, the required programs and libraries in the host
+filesystem).
+
+See that man page for details.
+
+=head2 RESTRICTION: UNREADABLE BINARIES ON THE HOST
+
+Some binaries on the host are not publically readable.  For example:
+
+ $ ll /usr/libexec/pt_chown 
+ -rws--x--x 1 root root 28418 2009-09-28 13:42 /usr/libexec/pt_chown
+ $ ll /usr/bin/chsh 
+ -rws--x--x 1 root root 18072 2009-10-05 16:28 /usr/bin/chsh
+
+These binaries cause a problem when reconstructing the supermin
+appliance, because we'd like to copy them into the final appliance,
+and usually that process is done as non-root.  Currently the only
+solution is that you should remove these problematic binaries from the
+appliance.
+
+=head1 EXAMPLE
+
+Create a basic Fedora directory and turn it into a supermin image.
+
+I<NB> You must only build "Rawhide on Rawhide".  If using another
+Fedora branch, you must change C<rawhide> below as appropriate, eg to
+C<fedora-12>.
+
+ $ febootstrap rawhide /tmp/fedora
+ $ febootstrap-to-supermin /tmp/fedora supermin.img hostfiles.txt
+
+Examine the resulting files:
+
+ $ cpio -itv < supermin.img | less
+ $ less hostfiles.txt
+
+Reconstruct the final kernel and initramfs.
+
+I<NB> The first time you run this, it will be slow because the
+required host files are not in cache.  With a "hot cache" it should be
+lightning fast.  Run it several times to get representative timings.
+
+ $ febootstrap-supermin-helper supermin.img hostfiles.txt \
+     /tmp/kernel /tmp/initrd
+
+You would boot the final image like this, although in this example it
+probably won't work unless you add a C</init> file to the appliance
+(see the discussion in L<febootstrap-to-initramfs(8)>).
+
+ $ qemu -m 1024 -kernel /tmp/kernel -initrd /tmp/initrd [etc...]
+
+=head1 SEE ALSO
+
+L<febootstrap(8)>,
+L<febootstrap-to-initramfs(8)>,
+L<febootstrap-supermin-helper(8)>.
+
+=head1 AUTHORS
+
+Richard W.M. Jones <rjones @ redhat . com>
+
+=head1 COPYRIGHT
+
+(C) Copyright 2009 Red Hat Inc.,
+L<http://et.redhat.com/~rjones/febootstrap>.
+
+This program is free software; you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation; either version 2 of the License, or
+(at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program; if not, write to the Free Software
+Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
diff --git a/febootstrap-to-supermin.sh b/febootstrap-to-supermin.sh
new file mode 100755 (executable)
index 0000000..32fd593
--- /dev/null
@@ -0,0 +1,133 @@
+#!/bin/bash -
+# febootstrap-to-supermin
+# (C) Copyright 2009 Red Hat Inc.
+#
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation; either version 2 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, write to the Free Software
+# Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+#
+# Written by Richard W.M. Jones <rjones@redhat.com>
+
+unset CDPATH
+
+TEMP=`getopt \
+        -o '' \
+        --long help \
+        -n febootstrap-to-supermin -- "$@"`
+if [ $? != 0 ]; then
+    echo "febootstrap-to-supermin: problem parsing the command line arguments"
+    exit 1
+fi
+eval set -- "$TEMP"
+
+usage ()
+{
+    echo "Usage: febootstrap-to-supermin DIR supermin.img hostfiles.txt"
+    echo "Please read febootstrap-to-supermin(8) man page for more information."
+}
+
+while true; do
+    case "$1" in
+       --help)
+           usage
+           exit 0;;
+       --)
+           shift
+           break;;
+       *)
+           echo "Internal error!"
+           exit 1;;
+    esac
+done
+
+if [ $# -ne 3 ]; then
+    usage
+    exit 1
+fi
+
+set -e
+
+# Create a temporary directory, make sure it gets cleaned up at the end.
+tmpdir=$(mktemp -d)
+remove_tmpdir ()
+{
+  status=$?
+  rm -rf "$tmpdir" && exit $status
+}
+trap remove_tmpdir EXIT
+
+# Get the complete list of files and directories in the appliance.
+(cd "$1" > /dev/null && find) > "$tmpdir/files"
+
+exec 5>"$tmpdir/keep"          # Files/dirs we will keep in supermin.img
+exec 6>$3                      # hostfiles.txt (output)
+exec 7<"$tmpdir/files"
+
+while read path <&7; do
+    dir=$(dirname "$path")
+    file=$(basename "$path")
+
+    # Ignore fakeroot.log.
+    if [ "$path" = "./fakeroot.log" ]; then
+       :
+
+    # Kernel modules are always copied in from the host, including all
+    # the dependency information files.
+    elif [[ "$path" =~ '^\./lib/modules/' ]]; then
+       :
+
+    # All we're going to keep are the special files /init, the daemon,
+    # configuration files (/etc), devices and modifiable stuff (/var).
+    elif [ "$path" = "./init" ]; then
+        echo "$path" >&5
+
+    elif [[ "$path" =~ '^\./etc' || "$path" =~ '^./dev' || "$path" =~ '^\./var' ]]; then
+        echo "$path" >&5
+
+    # Always write directory names to both output files.
+    elif [ -d "$path" ]; then
+        echo "$path" >&5
+        echo "$path" >&6
+
+    # Some libraries need fixed version numbers replaced by wildcards.
+    elif [[ "$file" =~ '^ld-[.0-9]+\.so$' ]]; then
+        echo "$dir/ld-*.so" >&6
+
+    # libfoo-1.2.3.so
+    elif [[ "$file" =~ '^lib(.*)-[-.0-9]+\.so$' ]]; then
+        echo "$dir/lib${BASH_REMATCH[1]}-*.so" >&6
+
+    # libfoo-1.2.3.so.1.2.3 (but NOT '*.so.N')
+    elif [[ "$file" =~ '^lib(.*)-[-.0-9]+\.so\.([0-9]+)\.' ]]; then
+        echo "$dir/lib${BASH_REMATCH[1]}-*.so.${BASH_REMATCH[2]}.*" >&6
+
+    # libfoo.so.1.2.3 (but NOT '*.so.N')
+    elif [[ "$file" =~ '^lib(.*)\.so\.([0-9]+)\.' ]]; then
+        echo "$dir/lib${BASH_REMATCH[1]}.so.${BASH_REMATCH[2]}.*" >&6
+
+    else
+        # Anything else comes from the host directly.
+        echo "$path" >&6
+    fi
+done
+
+# Close output files.
+exec 5>&-
+exec 6>&-
+
+# Now run febootstrap-to-initramfs to construct the supermin
+# appliance.
+if ! febootstrap-to-initramfs --nocompress --files="$tmpdir/keep" "$1" > "$2"
+then
+    rm -f "$2"
+fi
index e208603..d8ec24e 100644 (file)
@@ -169,6 +169,11 @@ permissions using the tool C<febootstrap-to-initramfs>.
 
 =item *
 
+Generate a supermin appliance using the tool
+C<febootstrap-to-supermin>.
+
+=item *
+
 Apply the permissions to the target directory using the forthcoming
 tool C<febootstrap-fix-root> (requires root).
 
@@ -227,6 +232,7 @@ L<febootstrap-to-initramfs(8)>,
 L<febootstrap-minimize(8)>,
 L<febootstrap-run(8)>,
 L<febootstrap-install(8)>,
+L<febootstrap-to-supermin(8)>,
 L<fakeroot(1)>,
 L<fakechroot(1)>,
 L<yum(8)>,