From 76b7711268a6b0c0929952ee261821825589a165 Mon Sep 17 00:00:00 2001 From: Richard Jones Date: Thu, 22 Oct 2009 12:31:05 +0100 Subject: [PATCH 1/1] Supermin appliance. This ports the libguestfs supermin appliance work into febootstrap to make it more generally available to anyone who wants to use it. --- .gitignore | 2 + Makefile.am | 48 +++++++++- febootstrap-supermin-helper.pod | 105 ++++++++++++++++++++++ febootstrap-supermin-helper.sh | 132 +++++++++++++++++++++++++++ febootstrap-to-supermin.pod | 193 ++++++++++++++++++++++++++++++++++++++++ febootstrap-to-supermin.sh | 133 +++++++++++++++++++++++++++ febootstrap.pod | 6 ++ 7 files changed, 616 insertions(+), 3 deletions(-) create mode 100644 febootstrap-supermin-helper.pod create mode 100755 febootstrap-supermin-helper.sh create mode 100644 febootstrap-to-supermin.pod create mode 100755 febootstrap-to-supermin.sh diff --git a/.gitignore b/.gitignore index 24fc34d..a430f1f 100644 --- a/.gitignore +++ b/.gitignore @@ -16,6 +16,8 @@ febootstrap-run febootstrap-install febootstrap-minimize febootstrap-to-initramfs +febootstrap-to-supermin +febootstrap-supermin-helper install-sh missing stamp-h1 diff --git a/Makefile.am b/Makefile.am index b239bbf..861b5e0 100644 --- a/Makefile.am +++ b/Makefile.am @@ -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 index 0000000..d39d8bd --- /dev/null +++ b/febootstrap-supermin-helper.pod @@ -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 reconstructs a bootable kernel and +initramfs from a supermin appliance. First you should be familiar +with L, L and +L. + +=head1 PARAMETERS + +Of the four parameters, the first two are I files, and the last +two are I files. + +C and C are the input files which +describe the supermin appliance. + +C and C 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 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 it is worth checking for +anything that is not necessary for your particular application and +removing it by hand. + +=back + +=head1 SEE ALSO + +L, +L, +L. + +=head1 AUTHORS + +Richard W.M. Jones + +=head1 COPYRIGHT + +(C) Copyright 2009 Red Hat Inc., +L. + +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 index 0000000..c2088bf --- /dev/null +++ b/febootstrap-supermin-helper.sh @@ -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 + +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-*.* 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 index 0000000..6263f51 --- /dev/null +++ b/febootstrap-to-supermin.pod @@ -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 converts the filesystem created by +L into a supermin appliance. The term "supermin +appliance" is described in the documentation below. First you should +be familiar with L and L. + +=head1 PARAMETERS + +C is the directory created by febootstrap (ie. the output of +febootstrap and the input to this program). + +C is the name of the supermin appliance that this +program creates, and C 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: + +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, but you can +call it anything you want) is the skeleton initramfs. This is like an +initramfs built by L, 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. + +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 can be used to +reconstruct a full initramfs from C and C +(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 You must only build "Rawhide on Rawhide". If using another +Fedora branch, you must change C below as appropriate, eg to +C. + + $ 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 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 file to the appliance +(see the discussion in L). + + $ qemu -m 1024 -kernel /tmp/kernel -initrd /tmp/initrd [etc...] + +=head1 SEE ALSO + +L, +L, +L. + +=head1 AUTHORS + +Richard W.M. Jones + +=head1 COPYRIGHT + +(C) Copyright 2009 Red Hat Inc., +L. + +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 index 0000000..32fd593 --- /dev/null +++ b/febootstrap-to-supermin.sh @@ -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 + +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 diff --git a/febootstrap.pod b/febootstrap.pod index e208603..d8ec24e 100644 --- a/febootstrap.pod +++ b/febootstrap.pod @@ -169,6 +169,11 @@ permissions using the tool C. =item * +Generate a supermin appliance using the tool +C. + +=item * + Apply the permissions to the target directory using the forthcoming tool C (requires root). @@ -227,6 +232,7 @@ L, L, L, L, +L, L, L, L, -- 1.8.3.1