Rewrite febootstrap as a general supermin appliance building tool. 3.0
authorRichard W.M. Jones <rjones@redhat.com>
Fri, 3 Dec 2010 13:17:00 +0000 (13:17 +0000)
committerRichard W.M. Jones <rjones@redhat.com>
Sun, 5 Dec 2010 09:33:25 +0000 (09:33 +0000)
This complete rewrite of the building tools turns febootstrap
into a general purpose, cross-distro, supermin appliance only
build tool.

There is now only one program 'febootstrap' which is used to
build a supermin appliance from a list of packages.

Normal appliances are not supported.

The tools are incompatible with febootstrap 2.x (use the
febootstrap-2.x branch from git to get the old package).

38 files changed:
.depend [new file with mode: 0644]
.gitignore
Makefile.am
README
TODO [deleted file]
config.ml.in [new file with mode: 0644]
configure.ac
examples/Makefile.am [deleted file]
examples/README [deleted file]
examples/guestfs-test.sh [deleted file]
examples/minimal-filesystem.sh [deleted file]
f10.log.gz [deleted file]
fakechroot-2.8-relchroot.patch [deleted file]
fakechroot-2.9-cmdsubst.patch [deleted file]
fakechroot-svn-no-dup-envs.patch [deleted file]
febootstrap-install.pod [deleted file]
febootstrap-install.sh [deleted file]
febootstrap-minimize.pod [deleted file]
febootstrap-minimize.sh [deleted file]
febootstrap-run.pod [deleted file]
febootstrap-run.sh [deleted file]
febootstrap-to-initramfs.pod [deleted file]
febootstrap-to-initramfs.sh [deleted file]
febootstrap-to-supermin.pod [deleted file]
febootstrap-to-supermin.sh [deleted file]
febootstrap.ml [new file with mode: 0644]
febootstrap.pod
febootstrap.sh [deleted file]
febootstrap_cmdline.ml [new file with mode: 0644]
febootstrap_cmdline.mli [new file with mode: 0644]
febootstrap_debian.ml [new file with mode: 0644]
febootstrap_package_handlers.ml [new file with mode: 0644]
febootstrap_package_handlers.mli [new file with mode: 0644]
febootstrap_utils.ml [new file with mode: 0644]
febootstrap_utils.mli [new file with mode: 0644]
febootstrap_yum_rpm.ml [new file with mode: 0644]
helper/Makefile.am
m4/ocaml.m4 [new file with mode: 0644]

diff --git a/.depend b/.depend
new file mode 100644 (file)
index 0000000..9f90bab
--- /dev/null
+++ b/.depend
@@ -0,0 +1,25 @@
+config.cmo: 
+config.cmx: 
+febootstrap_utils.cmi: 
+febootstrap_utils.cmo: febootstrap_utils.cmi 
+febootstrap_utils.cmx: febootstrap_utils.cmi 
+febootstrap_cmdline.cmi: 
+febootstrap_cmdline.cmo: config.cmo febootstrap_cmdline.cmi 
+febootstrap_cmdline.cmx: config.cmx febootstrap_cmdline.cmi 
+febootstrap_package_handlers.cmi: 
+febootstrap_package_handlers.cmo: febootstrap_utils.cmi \
+    febootstrap_cmdline.cmi febootstrap_package_handlers.cmi 
+febootstrap_package_handlers.cmx: febootstrap_utils.cmx \
+    febootstrap_cmdline.cmx febootstrap_package_handlers.cmi 
+febootstrap_yum_rpm.cmo: febootstrap_utils.cmi \
+    febootstrap_package_handlers.cmi febootstrap_cmdline.cmi config.cmo 
+febootstrap_yum_rpm.cmx: febootstrap_utils.cmx \
+    febootstrap_package_handlers.cmx febootstrap_cmdline.cmx config.cmx 
+febootstrap_debian.cmo: febootstrap_utils.cmi \
+    febootstrap_package_handlers.cmi febootstrap_cmdline.cmi config.cmo 
+febootstrap_debian.cmx: febootstrap_utils.cmx \
+    febootstrap_package_handlers.cmx febootstrap_cmdline.cmx config.cmx 
+febootstrap.cmo: febootstrap_utils.cmi febootstrap_package_handlers.cmi \
+    febootstrap_cmdline.cmi config.cmo 
+febootstrap.cmx: febootstrap_utils.cmx febootstrap_package_handlers.cmx \
+    febootstrap_cmdline.cmx config.cmx 
index 22e1175..b8a4aea 100644 (file)
@@ -1,4 +1,7 @@
 *~
+*.cmi
+*.cmo
+*.cmx
 *.o
 *.a
 .deps
@@ -13,21 +16,17 @@ config.guess
 config.h.in
 config.h
 config.log
+config.ml
 config.status
 config.sub
 configure
 cscope.out
 depcomp
 febootstrap
-febootstrap-run
-febootstrap-install
-febootstrap-minimize
-febootstrap-to-initramfs
-febootstrap-to-supermin
-febootstrap-supermin-helper
 febootstrap*.8
 febootstrap*.txt
 febootstrap-*.tar.gz
+helper/febootstrap-supermin-helper
 helper/init
 lib/alloca.h
 lib/arg-nonnull.h
index 02bc3f3..3dcbe1f 100644 (file)
 
 ACLOCAL_AMFLAGS = -I m4
 
-SUBDIRS = lib helper examples
-
-bin_SCRIPTS = \
-       febootstrap \
-       febootstrap-run \
-       febootstrap-install \
-       febootstrap-minimize \
-       febootstrap-to-initramfs \
-       febootstrap-to-supermin
-DISTCLEANFILES = $(bin_SCRIPTS)
-
-febootstrap: febootstrap.sh
-       rm -f $@
-       cp $< $@-t
-       chmod 0555 $@-t
-       mv $@-t $@
-
-febootstrap-run: febootstrap-run.sh
-       rm -f $@
-       cp $< $@-t
-       chmod 0555 $@-t
-       mv $@-t $@
-
-febootstrap-install: febootstrap-install.sh
-       rm -f $@
-       cp $< $@-t
-       chmod 0555 $@-t
-       mv $@-t $@
-
-febootstrap-minimize: febootstrap-minimize.sh
-       rm -f $@
-       cp $< $@-t
-       chmod 0555 $@-t
-       mv $@-t $@
-
-febootstrap-to-initramfs: febootstrap-to-initramfs.sh
-       rm -f $@
-       cp $< $@-t
-       chmod 0555 $@-t
-       mv $@-t $@
+SUBDIRS = lib helper
+
+# Note these must be in build dependency order.
+SOURCES = \
+       config.ml \
+       febootstrap_utils.mli \
+       febootstrap_utils.ml \
+       febootstrap_cmdline.mli \
+       febootstrap_cmdline.ml \
+       febootstrap_package_handlers.mli \
+       febootstrap_package_handlers.ml \
+       febootstrap_yum_rpm.ml \
+       febootstrap_debian.ml \
+       febootstrap.ml
+
+CLEANFILES = *~ *.cmi *.cmo *.cmx *.o febootstrap
 
-febootstrap-to-supermin: febootstrap-to-supermin.sh
-       rm -f $@
-       cp $< $@-t
-       chmod 0555 $@-t
-       mv $@-t $@
+EXTRA_DIST = \
+       .gitignore \
+       .gitmodules \
+       autogen.sh \
+       febootstrap.8 \
+       febootstrap.pod \
+       m4/gnulib-cache.m4 \
+       $(SOURCES)
 
 man_MANS = \
-       febootstrap.8 \
-       febootstrap-run.8 \
-       febootstrap-install.8 \
-       febootstrap-minimize.8 \
-       febootstrap-to-initramfs.8 \
-       febootstrap-to-supermin.8
+       febootstrap.8
 
-if HAVE_PERLDOC
+bin_SCRIPTS = febootstrap
 
-febootstrap.8: febootstrap.pod
-       pod2man \
-         --section 8 \
-         -c "Virtualization Support" \
-         --release "$(PACKAGE_NAME)-$(PACKAGE_VERSION)" \
-         $< > $@
+SOURCES_ML = $(filter %.ml,$(SOURCES))
+BOBJECTS = $(SOURCES_ML:.ml=.cmo)
+XOBJECTS = $(SOURCES_ML:.ml=.cmx)
 
-febootstrap.txt: febootstrap.pod
-       pod2text $< > $@
+if !HAVE_OCAMLOPT
+OBJECTS = $(BOBJECTS)
+else
+OBJECTS = $(XOBJECTS)
+endif
 
-febootstrap-run.8: febootstrap-run.pod
-       pod2man \
-         --section 8 \
-         -c "Virtualization Support" \
-         --release "$(PACKAGE_NAME)-$(PACKAGE_VERSION)" \
-         $< > $@
+OCAMLPACKAGES = -package unix,str
+OCAMLFLAGS = -warn-error CDEFLMPSUVXYZ
 
-febootstrap-run.txt: febootstrap-run.pod
-       pod2text $< > $@
+febootstrap: $(OBJECTS)
+       $(OCAMLFIND) $(OCAMLBEST) $(OCAMLFLAGS) $(OCAMLPACKAGES) -linkpkg \
+         $^ -o $@
 
-febootstrap-install.8: febootstrap-install.pod
-       pod2man \
-         --section 8 \
-         -c "Virtualization Support" \
-         --release "$(PACKAGE_NAME)-$(PACKAGE_VERSION)" \
-         $< > $@
+.mli.cmi:
+       $(OCAMLFIND) ocamlc $(OCAMLFLAGS) $(OCAMLPACKAGES) -c $< -o $@
+.ml.cmo:
+       $(OCAMLFIND) ocamlc $(OCAMLFLAGS) $(OCAMLPACKAGES) -c $< -o $@
+.ml.cmx:
+       $(OCAMLFIND) ocamlopt $(OCAMLFLAGS) $(OCAMLPACKAGES) -c $< -o $@
 
-febootstrap-install.txt: febootstrap-install.pod
-       pod2text $< > $@
+depend: .depend
 
-febootstrap-minimize.8: febootstrap-minimize.pod
-       pod2man \
-         --section 8 \
-         -c "Virtualization Support" \
-         --release "$(PACKAGE_NAME)-$(PACKAGE_VERSION)" \
-         $< > $@
+.depend: $(SOURCES)
+       rm -f $@ $@-t
+       $(OCAMLFIND) ocamldep $^ > $@-t
+       mv $@-t $@
 
-febootstrap-minimize.txt: febootstrap-minimize.pod
-       pod2text $< > $@
+include .depend
 
-febootstrap-to-initramfs.8: febootstrap-to-initramfs.pod
-       pod2man \
-         --section 8 \
-         -c "Virtualization Support" \
-         --release "$(PACKAGE_NAME)-$(PACKAGE_VERSION)" \
-         $< > $@
+SUFFIXES = .cmo .cmi .cmx .ml .mli .mll .mly
 
-febootstrap-to-initramfs.txt: febootstrap-to-initramfs.pod
-       pod2text $< > $@
+if HAVE_PERLDOC
 
-febootstrap-to-supermin.8: febootstrap-to-supermin.pod
+febootstrap.8: febootstrap.pod
        pod2man \
          --section 8 \
          -c "Virtualization Support" \
          --release "$(PACKAGE_NAME)-$(PACKAGE_VERSION)" \
          $< > $@
-
-febootstrap-to-supermin.txt: febootstrap-to-supermin.pod
-       pod2text $< > $@
-
 endif
-
-EXTRA_DIST = \
-       fakechroot-2.8-relchroot.patch \
-       fakechroot-svn-no-dup-envs.patch \
-       febootstrap.8 febootstrap.txt febootstrap.pod \
-         febootstrap.sh \
-       febootstrap-run.8 febootstrap-run.txt febootstrap-run.pod \
-         febootstrap-run.sh \
-       febootstrap-install.8 febootstrap-install.txt febootstrap-install.pod \
-         febootstrap-install.sh \
-       febootstrap-minimize.8 febootstrap-minimize.txt \
-         febootstrap-minimize.pod \
-         febootstrap-minimize.sh \
-       febootstrap-to-initramfs.8 febootstrap-to-initramfs.txt \
-         febootstrap-to-initramfs.pod \
-         febootstrap-to-initramfs.sh \
-       febootstrap-to-supermin.8 febootstrap-to-supermin.txt \
-         febootstrap-to-supermin.pod \
-         febootstrap-to-supermin.sh \
-       m4/gnulib-cache.m4
diff --git a/README b/README
index 5383dcb..ee28067 100644 (file)
--- a/README
+++ b/README
@@ -1,32 +1,29 @@
-febootstrap - Bootstrap a basic Fedora system
+febootstrap - Bootstrapping tool for creating supermin appliances
 by Richard W.M. Jones (rjones@redhat.com)
 http://people.redhat.com/~rjones/febootstrap
 ----------------------------------------------------------------------
 
-febootstrap is a Fedora equivalent to Debian's debootstrap.  You can
-use it to create a basic Fedora filesystem.
+febootstrap is a tool for building supermin appliances.  These are
+tiny appliances [similar to virtual machines], usually around 100KB in
+size, which get fully instantiated on-the-fly in a fraction of a
+second when you need to boot one of them.
 
-There are currently some restrictions compared to debootstrap.
+A complete description is in the febootstrap(8) man page.
 
-For instructions, restrictions, examples etc. please read the manpage
-febootstrap(8).
+IMPORTANT NOTE FOR USERS OF FEBOOTSTRAP 2.x:
+  febootstrap 3.x is a complete rewrite.  febootstrap 2.x could only
+  build Fedora distributions.  This version can build many varieties
+  of Linux distros.  3.x only builds supermin appliances, it does not
+  build chroots.  3.x does not build cross-distro, cross-release or
+  cross-architecture systems.  If you want febootstrap 2.x, please use
+  the 'febootstrap-2.x' branch from the git repository.
 
 Requirements
 ------------
 
-  fakeroot
-    - Tested with version 1.11.
+  ocaml
 
-  fakechroot >= 2.9 or 2.8 + patch
-    - **** NB. KNOWN NOT TO WORK WITH 2.8 *****  You MUST apply
-      the patch 'fakechroot-2.8-relchroot.patch' to the fakechroot
-      sources and recompile.  Hopefully this patch will be included
-      in future versions of fakechroot.
-
-  yum
-    - Tested with version 3.2 (works best with 3.2.21 or above)
-    - Needs network access to a Fedora repository, or a local
-      Fedora mirror.
+  ocaml findlib ("ocamlfind" program)
 
   perldoc
     - This is just used to generate the manpage.
@@ -36,12 +33,21 @@ Requirements
   gcc
 
   qemu
-    - If you want to test-run your systems.
+    - Only if you need to test-boot appliances.
 
   libext2fs
   /sbin/mke2fs
     - These are part of e2fsprogs.
 
+Optional
+--------
+(but you will need a package manager for your Linux distro)
+
+  yum
+  rpm
+  apt-get
+  dpkg
+
 Building and installing
 -----------------------
 
@@ -49,42 +55,9 @@ Building and installing
   make
   sudo make install
 
-Note, if you want to run the scripts locally without installing,
-you have to set the $PATH to the current directory, ie:
-
-  PATH=$(pwd):$PATH
-  febootstrap [...]
-
-Debugging
----------
-
-If you get segfaults while running febootstrap or errors in the %post
-scripts, these are often caused by some incompatibility in the
-emulation provided by fakeroot/fakechroot.  You can track them down by
-running the yum command explicitly.  Try:
-
-  fakeroot fakechroot -s \
-    yum -y -c /tmp/repo \
-      --disablerepo=\* --enablerepo=febootstrap \
-      --noplugins --nogpgcheck \
-      --installroot=/tmp/root \
-      install "@Core"
-
-where /tmp/repo would be a file like this (change repo and arch
-parameters as appropriate):
-
-  [febootstrap]
-  name=febootstrap
-  failovermethod=priority
-  enabled=1
-  gpgcheck=0
-  mirrorlist=http://mirrors.fedoraproject.org/mirrorlist?repo=rawhide&arch=x86_64
-
-You can insert gdb in the appropriate place in the fakeroot /
-fakechroot / yum command.
-
 Feedback and bugs
 -----------------
 
-Send feedback to rjones@redhat.com.  You can file bugs in
-http://bugzilla.redhat.com/
+Send feedback to libguestfs@redhat.com.  You can file bugs in
+https://bugzilla.redhat.com/ (under "Fedora", "febootstrap")
+
diff --git a/TODO b/TODO
deleted file mode 100644 (file)
index 901e607..0000000
--- a/TODO
+++ /dev/null
@@ -1,15 +0,0 @@
-- More files in the root, eg: fstab, mtab, resolv.conf
-
-- Configurable stuff in the febootstrap repo, eg. proxies, extra repos
-
-- Update to latest fakechroot release
-
-- 32-on-64 support in fakechroot/fakeroot
-
-- febootstrap on Debian etc (to bootstrap Fedora on Debian of course)
-
-
-
-For more to-do ideas, see:
-
-http://rwmj.wordpress.com/2009/03/19/febootstrap-fedora-equivalent-of-debootstrap/#comments
diff --git a/config.ml.in b/config.ml.in
new file mode 100644 (file)
index 0000000..ebda655
--- /dev/null
@@ -0,0 +1,27 @@
+(* febootstrap 3
+ * Copyright (C) 2009-2010 Red Hat Inc.
+ * @configure_input@
+ *
+ * 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+ *)
+
+let package_name = "@PACKAGE_NAME@"
+let package_version = "@PACKAGE_VERSION@"
+let yum = "@YUM@"
+let rpm = "@RPM@"
+let yumdownloader = "@YUMDOWNLOADER@"
+let aptitude = "@APTITUDE@"
+let dpkg = "@DPKG@"
+let host_cpu = "@host_cpu@"
index 509db2c..d1b334d 100644 (file)
@@ -17,7 +17,7 @@ dnl Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
 dnl
 dnl Written by Richard W.M. Jones <rjones@redhat.com>
 
-AC_INIT(febootstrap,2.11)
+AC_INIT(febootstrap,3.0)
 AM_INIT_AUTOMAKE
 
 dnl Check for basic C environment.
@@ -37,27 +37,34 @@ AC_SYS_LARGEFILE
 
 gl_INIT
 
+# OCaml and ocamlfind are required to compile.
+AC_PROG_OCAML
+if test "$OCAMLC" = "no"; then
+    AC_MSG_ERROR([You must install the OCaml compiler])
+fi
+AM_CONDITIONAL([HAVE_OCAMLOPT],[test "$OCAMLBEST" = "opt"])
+AC_PROG_FINDLIB
+if test "$OCAMLFIND" = "no"; then
+    AC_MSG_ERROR([You must install OCaml findlib (the ocamlfind command)])
+fi
+
+dnl Optional programs.
 AC_CHECK_PROG(PERLDOC,[perldoc],[perldoc],[no])
 if test "x$PERLDOC" = "xno" ; then
   AC_MSG_WARN([perldoc not found - install perl to make man pages])
 fi
 AM_CONDITIONAL(HAVE_PERLDOC,[test "$perldoc" != "no"])
 
-AC_CHECK_PROG(FAKEROOT,[fakeroot],[fakeroot],[no])
-if test "x$FAKEROOT" = "xno" ; then
-  AC_MSG_FAILURE([fakeroot program not found])
-fi
-
-AC_CHECK_PROG(FAKECHROOT,[fakechroot],[fakechroot],[no])
-if test "x$FAKECHROOT" = "xno" ; then
-  AC_MSG_FAILURE([fakechroot program not found])
-fi
-
+dnl For yum-rpm handler.
 AC_CHECK_PROG(YUM,[yum],[yum],[no])
-if test "x$YUM" = "xno" ; then
-  AC_MSG_FAILURE([yum program not found])
-fi
+AC_CHECK_PROG(RPM,[rpm],[rpm],[no])
+AC_CHECK_PROG(YUMDOWNLOADER,[yumdownloader],[yumdownloader],[no])
+
+dnl For Debian handler.
+AC_CHECK_PROG(APTITUDE,[aptitude],[aptitude],[no])
+AC_CHECK_PROG(DPKG,[dpkg],[dpkg],[no])
 
+dnl Required e2fsprogs, libs.
 AC_PATH_PROG([MKE2FS],[mke2fs],[no])
 if test "x$MKE2FS" = "xno" ; then
   AC_MSG_FAILURE([mke2fs program not found (is /sbin in your current path?)])
@@ -82,5 +89,5 @@ AC_CHECK_HEADER([ext2fs/ext2fs.h],[],[
 ])
 
 AC_CONFIG_HEADERS([config.h])
-AC_CONFIG_FILES([Makefile lib/Makefile helper/Makefile examples/Makefile])
+AC_CONFIG_FILES([config.ml Makefile lib/Makefile helper/Makefile])
 AC_OUTPUT
diff --git a/examples/Makefile.am b/examples/Makefile.am
deleted file mode 100644 (file)
index 08fb42b..0000000
+++ /dev/null
@@ -1,23 +0,0 @@
-# febootstrap Makefile.am
-# (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>
-
-EXTRA_DIST = \
-       README \
-       minimal-filesystem.sh \
-       guestfs-test.sh
diff --git a/examples/README b/examples/README
deleted file mode 100644 (file)
index c52e7b0..0000000
+++ /dev/null
@@ -1,10 +0,0 @@
-You should read and edit these scripts before running them!
-
-You don't need to be root, in fact, you *shouldn't* be root
-when running these scripts!
-
-If you want to run the scripts without installing febootstrap,
-then you need to make your $PATH point to the top build
-directory, eg:
-
-  PATH=$(pwd)/..:$PATH
diff --git a/examples/guestfs-test.sh b/examples/guestfs-test.sh
deleted file mode 100755 (executable)
index f253969..0000000
+++ /dev/null
@@ -1,73 +0,0 @@
-#!/bin/sh -
-
-# Before running, make sure 'vmlinuz' in this examples directory is a
-# bootable Linux kernel or a symlink to one.  You can just use any
-# kernel out of the /boot directory for this.
-#
-# eg:
-# cd examples
-# ln -s /boot/vmlinuz-NNN vmlinuz
-#
-# Also make 'guest-image' be a symlink to a virtual machine disk image.
-
-# This is a realistic example for 'libguestfs', which contains a
-# selection of command-line tools, LVM, NTFS and an NFS server.
-
-set -e
-
-if [ $(id -u) -eq 0 ]; then
-    echo "Don't run this script as root.  Read instructions in script first."
-    exit 1
-fi
-
-if [ ! -e vmlinuz -o ! -e guest-image ]; then
-    echo "Read instructions in script first."
-    exit 1
-fi
-
-febootstrap \
-    -i bash \
-    -i coreutils \
-    -i lvm2 \
-    -i ntfs-3g \
-    -i nfs-utils \
-    -i util-linux-ng \
-    -i MAKEDEV \
-    fedora-10 ./guestfs $1
-
-echo -n "Before minimization: "; du -sh guestfs
-febootstrap-minimize --all ./guestfs
-echo -n "After minimization:  "; du -sh guestfs
-
-# Create the /init which will scan for and enable all LVM volume groups.
-
-create_init ()
-{
-  cat > /init <<'__EOF__'
-#!/bin/sh
-PATH=/sbin:/usr/sbin:$PATH
-MAKEDEV mem null port zero core full ram tty console fd \
-  hda hdb hdc hdd sda sdb sdc sdd loop sd
-mount -t proc /proc /proc
-mount -t sysfs /sys /sys
-mount -t devpts -o gid=5,mode=620 /dev/pts /dev/pts
-modprobe sata_nv pata_acpi ata_generic
-lvm vgscan --ignorelockingfailure
-lvm vgchange -ay --ignorelockingfailure
-/bin/bash -i
-__EOF__
-  chmod +x init
-}
-export -f create_init
-febootstrap-run ./guestfs -- bash -c create_init
-
-# Convert the filesystem to an initrd image.
-
-febootstrap-to-initramfs ./guestfs > guestfs-initrd.img
-
-# Now run qemu to boot this guestfs system.
-
-qemu-system-$(arch) \
-  -m 256 \
-  -kernel vmlinuz -initrd guestfs-initrd.img \
-  -hda guest-image -boot c
diff --git a/examples/minimal-filesystem.sh b/examples/minimal-filesystem.sh
deleted file mode 100755 (executable)
index e1766a9..0000000
+++ /dev/null
@@ -1,66 +0,0 @@
-#!/bin/sh -
-
-# Before running, make sure 'vmlinuz' in this examples directory is a
-# bootable Linux kernel or a symlink to one.  You can just use any
-# kernel out of the /boot directory for this.
-#
-# eg:
-# cd examples
-# ln -s /boot/vmlinuz-NNN vmlinuz
-
-# This creates a very minimal filesystem, just containing bash and a
-# few command line utilities.  One of the joys of Fedora is that even
-# this minimal install is still 200 MB ...
-
-set -e
-
-if [ $(id -u) -eq 0 ]; then
-    echo "Don't run this script as root.  Read instructions in script first."
-    exit 1
-fi
-
-if [ ! -e vmlinuz ]; then
-    echo "Read instructions in script first."
-    exit 1
-fi
-
-febootstrap -i bash -i coreutils fedora-10 ./minimal $1
-
-# ... but let's minimize it aggressively.
-
-echo -n "Before minimization: "; du -sh minimal
-febootstrap-minimize --all --pack-executables ./minimal
-echo -n "After minimization:  "; du -sh minimal
-
-# Create the /init which is just a simple script to give users an
-# interactive shell.
-
-create_init ()
-{
-  cat > /init <<'__EOF__'
-#!/bin/sh
-echo; echo; echo
-echo "Welcome to the minimal filesystem example"
-echo; echo; echo
-/bin/bash -i
-__EOF__
-  chmod +x /init
-}
-export -f create_init
-febootstrap-run ./minimal -- bash -c create_init
-
-# Convert the filesystem to an initrd image.
-
-febootstrap-to-initramfs ./minimal > minimal-initrd.img
-
-# This is needed because of crappiness with qemu.
-
-rm -f zero
-dd if=/dev/zero of=zero bs=2048 count=1
-
-# Now run qemu to boot this minimal system.
-
-qemu-system-$(arch) \
-  -m 256 \
-  -kernel vmlinuz -initrd minimal-initrd.img \
-  -hda zero -boot c
diff --git a/f10.log.gz b/f10.log.gz
deleted file mode 100644 (file)
index 3acdd49..0000000
Binary files a/f10.log.gz and /dev/null differ
diff --git a/fakechroot-2.8-relchroot.patch b/fakechroot-2.8-relchroot.patch
deleted file mode 100644 (file)
index 4d2b093..0000000
+++ /dev/null
@@ -1,63 +0,0 @@
-This patch must be applied to fakechroot 2.8 or SVN in order to
-enable correct handling of relative chroots, for rpm and yum.
-
-Note this patch has been included in upstream fakechroot >= 2.9
-
-Index: src/libfakechroot.c
-===================================================================
---- src/libfakechroot.c        (revision 311)
-+++ src/libfakechroot.c        (working copy)
-@@ -1147,7 +1147,7 @@
- {
-     char *ptr, *ld_library_path, *tmp, *fakechroot_path;
-     int status, len;
--    char dir[FAKECHROOT_MAXPATH], cwd[FAKECHROOT_MAXPATH], full_path[FAKECHROOT_MAXPATH];
-+    char dir[FAKECHROOT_MAXPATH], cwd[FAKECHROOT_MAXPATH];
- #if !defined(HAVE_SETENV)
-     char *envbuf;
- #endif
-@@ -1162,7 +1162,9 @@
-         return -1;
-     }
-     if (*path != '/') {
--        if (getcwd(cwd, FAKECHROOT_MAXPATH) == NULL) {
-+        if (next_getcwd == NULL) fakechroot_init();
-+
-+        if (next_getcwd(cwd, FAKECHROOT_MAXPATH) == NULL) {
-             errno = ENAMETOOLONG;
-             return -1;
-         }
-@@ -1171,24 +1173,22 @@
-             return -1;
-         }
-         if (strcmp(cwd, "/") == 0) {
--            snprintf(full_path, FAKECHROOT_MAXPATH, "/%s", path);
-+            snprintf(dir, FAKECHROOT_MAXPATH, "/%s", path);
-         }
-         else {
--            snprintf(full_path, FAKECHROOT_MAXPATH, "%s/%s", cwd, path);
-+            snprintf(dir, FAKECHROOT_MAXPATH, "%s/%s", cwd, path);
-         }
-     }
-     else {
--        snprintf(full_path, FAKECHROOT_MAXPATH, "%s", path);
--    }
-+      fakechroot_path = getenv("FAKECHROOT_BASE");
--    fakechroot_path = getenv("FAKECHROOT_BASE");
--
--    if (fakechroot_path != NULL) {
--        snprintf(dir, FAKECHROOT_MAXPATH, "%s%s", fakechroot_path, full_path);
-+      if (fakechroot_path != NULL) {
-+          snprintf(dir, FAKECHROOT_MAXPATH, "%s%s", fakechroot_path, path);
-+      }
-+      else {
-+          snprintf(dir, FAKECHROOT_MAXPATH, "%s", path);
-+      }
-     }
--    else {
--        snprintf(dir, FAKECHROOT_MAXPATH, "%s", full_path);
--    }
- #if defined(HAVE___XSTAT) && defined(_STAT_VER)
-     if ((status = next___xstat(_STAT_VER, dir, &sb)) != 0) {
diff --git a/fakechroot-2.9-cmdsubst.patch b/fakechroot-2.9-cmdsubst.patch
deleted file mode 100644 (file)
index 0d40cd7..0000000
+++ /dev/null
@@ -1,262 +0,0 @@
-Index: test/cmd-subst-pwd.sh
-===================================================================
---- test/cmd-subst-pwd.sh      (revision 0)
-+++ test/cmd-subst-pwd.sh      (revision 0)
-@@ -0,0 +1,3 @@
-+#!/bin/sh
-+
-+echo substituted
-
-Property changes on: test/cmd-subst-pwd.sh
-___________________________________________________________________
-Added: svn:executable
-   + *
-
-Index: test/cmd-subst.t
-===================================================================
---- test/cmd-subst.t   (revision 0)
-+++ test/cmd-subst.t   (revision 0)
-@@ -0,0 +1,37 @@
-+#!/bin/sh
-+
-+. ./tap.sh
-+
-+plan 5
-+
-+rm -rf testtree
-+
-+./testtree.sh testtree
-+test "`cat testtree/CHROOT`" = "testtree" || not
-+ok "testtree"
-+
-+t=`./fakechroot.sh testtree /bin/pwd`
-+test "$t" = "/" || not
-+ok "fakechroot pwd is /"
-+
-+export FAKECHROOT_CMD_SUBST="/bin/pwd=$(pwd)/cmd-subst-pwd.sh"
-+
-+t=`./fakechroot.sh testtree /bin/pwd`
-+test "$t" = "substituted" || not
-+ok "fakechroot substituted pwd (1)"
-+
-+export FAKECHROOT_CMD_SUBST="/no/file=foo:/bin/pwd=$(pwd)/cmd-subst-pwd.sh"
-+
-+t=`./fakechroot.sh testtree /bin/pwd`
-+test "$t" = "substituted" || not
-+ok "fakechroot substituted pwd (2)"
-+
-+export FAKECHROOT_CMD_SUBST="/no/file=foo:/other/file=bar"
-+
-+t=`./fakechroot.sh testtree /bin/pwd`
-+test "$t" = "/" || not
-+ok "fakechroot not substituted pwd is /"
-+
-+rm -rf testtree
-+
-+end
-
-Property changes on: test/cmd-subst.t
-___________________________________________________________________
-Added: svn:executable
-   + *
-
-Index: test/Makefile.am
-===================================================================
---- test/Makefile.am   (revision 323)
-+++ test/Makefile.am   (working copy)
-@@ -1,6 +1,6 @@
- SUBDIRS = src
--TESTS = chroot.t escape-nested-chroot.t pwd.t touch.t
-+TESTS = chroot.t cmd-subst.t escape-nested-chroot.t pwd.t touch.t
- suffix =
-Index: src/libfakechroot.c
-===================================================================
---- src/libfakechroot.c        (revision 323)
-+++ src/libfakechroot.c        (working copy)
-@@ -1467,7 +1467,38 @@
-     return execve (path, argv, environ);
- }
-+/* Parse the FAKECHROOT_CMD_SUBST environment variable (the first
-+ * parameter) and if there is a match with filename, return the
-+ * substitution in cmd_subst.  Returns non-zero if there was a match.
-+ *
-+ * FAKECHROOT_CMD_SUBST=cmd=subst:cmd=subst:...
-+ */
-+static int
-+try_cmd_subst (char *env, const char *filename, char *cmd_subst)
-+{
-+    int len = strlen (filename), len2;
-+    char *p;
-+    if (env == NULL) return 0;
-+
-+    do {
-+      p = strchrnul (env, ':');
-+
-+      if (strncmp (env, filename, len) == 0 && env[len] == '=') {
-+          len2 = p - &env[len+1];
-+          if (len2 >= FAKECHROOT_MAXPATH)
-+              len2 = FAKECHROOT_MAXPATH - 1;
-+          strncpy (cmd_subst, &env[len+1], len2);
-+          cmd_subst[len2] = '\0';
-+          return 1;
-+      }
-+
-+      env = p;
-+    } while (*env++ != '\0');
-+
-+    return 0;
-+}
-+
- /* #include <unistd.h> */
- int execve (const char *filename, char *const argv [], char *const envp[])
- {
-@@ -1479,32 +1510,16 @@
-     char *env;
-     char tmp[FAKECHROOT_MAXPATH], newfilename[FAKECHROOT_MAXPATH], argv0[FAKECHROOT_MAXPATH];
-     char *ptr;
--    unsigned int i, j, n, len;
-+    unsigned int i, j, n, len, r, newenvppos;
-     size_t sizeenvp;
-     char c;
-     char *fakechroot_path, *fakechroot_ptr, fakechroot_buf[FAKECHROOT_MAXPATH];
-     char *envkey[] = { "FAKECHROOT", "FAKECHROOT_BASE",
-                        "FAKECHROOT_VERSION", "FAKECHROOT_EXCLUDE_PATH",
-+                       "FAKECHROOT_CMD_SUBST",
-                        "LD_LIBRARY_PATH", "LD_PRELOAD" };
-+    const int nr_envkey = sizeof envkey / sizeof envkey[0];
--    strncpy(argv0, filename, FAKECHROOT_MAXPATH);
--
--    expand_chroot_path(filename, fakechroot_path, fakechroot_ptr, fakechroot_buf);
--    strcpy(tmp, filename);
--    filename = tmp;
--
--    if ((file = open(filename, O_RDONLY)) == -1) {
--        errno = ENOENT;
--        return -1;
--    }
--
--    i = read(file, hashbang, FAKECHROOT_MAXPATH-2);
--    close(file);
--    if (i == -1) {
--        errno = ENOENT;
--        return -1;
--    }
--
-     if (next_execve == NULL) fakechroot_init();
-     /* Scan envp and check its size */
-@@ -1514,39 +1529,69 @@
-     }
-     /* Copy envp to newenvp */
--    newenvp = malloc( sizeenvp * sizeof (char *) + sizeof(envkey) );
-+    newenvp = malloc( (sizeenvp + 1) * sizeof (char *) );
-     if (newenvp == NULL) {
-         errno = ENOMEM;
-         return -1;
-     }
--    for (ep = envp, i = 0; *ep != NULL; ++ep) {
--        for (j = 0; j < sizeof (envkey) / sizeof (char *); j++) {
-+    for (ep = envp, newenvppos = 0; *ep != NULL; ++ep) {
-+        for (j = 0; j < nr_envkey; j++) {
-             len = strlen (envkey[j]);
-             if (strncmp (*ep, envkey[j], len) == 0 && (*ep)[len] == '=')
-                 goto skip;
-         }
--        newenvp[i] = *ep;
--        i++;
-+        newenvp[newenvppos] = *ep;
-+        newenvppos++;
-     skip: ;
-     }
-+    newenvp[newenvppos] = NULL;
-+    strncpy(argv0, filename, FAKECHROOT_MAXPATH);
-+
-+    r = try_cmd_subst (getenv ("FAKECHROOT_CMD_SUBST"), filename, tmp);
-+    if (r) {
-+        filename = tmp;
-+
-+        /* FAKECHROOT_CMD_SUBST escapes the chroot.  newenvp here does
-+         * not contain LD_PRELOAD and the other special environment
-+         * variables.
-+         */
-+        return next_execve(filename, argv, newenvp);
-+    }
-+
-+    expand_chroot_path(filename, fakechroot_path, fakechroot_ptr, fakechroot_buf);
-+    strcpy(tmp, filename);
-+    filename = tmp;
-+
-+    if ((file = open(filename, O_RDONLY)) == -1) {
-+        errno = ENOENT;
-+        return -1;
-+    }
-+
-+    i = read(file, hashbang, FAKECHROOT_MAXPATH-2);
-+    close(file);
-+    if (i == -1) {
-+        errno = ENOENT;
-+        return -1;
-+    }
-+
-     /* Add our variables to newenvp */
--    newenvp = realloc( newenvp, i * sizeof(char *) + sizeof(envkey) );
-+    newenvp = realloc( newenvp, (newenvppos + nr_envkey + 1) * sizeof(char *) );
-     if (newenvp == NULL) {
-         errno = ENOMEM;
-         return -1;
-     }
--    for (j = 0; j < sizeof(envkey) / sizeof(char *); j++) {
-+    for (j = 0; j < nr_envkey; j++) {
-         env = getenv(envkey[j]);
-         if (env != NULL) {
--            newenvp[i] = malloc(strlen(envkey[j]) + strlen(env) + 3);
--            strcpy(newenvp[i], envkey[j]);
--            strcat(newenvp[i], "=");
--            strcat(newenvp[i], env);
--            i++;
-+            newenvp[newenvppos] = malloc(strlen(envkey[j]) + strlen(env) + 3);
-+            strcpy(newenvp[newenvppos], envkey[j]);
-+            strcat(newenvp[newenvppos], "=");
-+            strcat(newenvp[newenvppos], env);
-+            newenvppos++;
-         }
-     }
--    newenvp[i] = NULL;
-+    newenvp[newenvppos] = NULL;
-     /* No hashbang in argv */
-     if (hashbang[0] != '#' || hashbang[1] != '!')
-Index: man/fakechroot.pod
-===================================================================
---- man/fakechroot.pod (revision 323)
-+++ man/fakechroot.pod (working copy)
-@@ -139,6 +139,21 @@
- The list of directories which are excluded from being chrooted.  The elements
- of list are separated with colon.
-+=item B<FAKECHROOT_CMD_SUBST>
-+
-+A list of command substitutions.  If a program tries to execute one of
-+the commands given (path relative to the chroot) then the substitute
-+command runs instead (path to substitute command is not chrooted).
-+
-+For example:
-+
-+ FAKECHROOT_CMD_SUBST=/sbin/ldconfig=/tmp/ldconfig-wrapper
-+
-+will substitute C</tmp/ldconfig-wrapper> for C</sbin/ldconfig>.
-+
-+Give as many substitute commands as you want, separated by C<:>
-+(colon) characters.
-+
- =item B<LD_LIBRARY_PATH>, B<LD_PRELOAD>
- Fakechroot is implemented by wrapping system calls.  This is accomplished by
diff --git a/fakechroot-svn-no-dup-envs.patch b/fakechroot-svn-no-dup-envs.patch
deleted file mode 100644 (file)
index 87f8e6f..0000000
+++ /dev/null
@@ -1,38 +0,0 @@
-Note, this patch has been included in upstream fakechroot >= 2.9
-
-Index: src/libfakechroot.c
-===================================================================
---- src/libfakechroot.c        (revision 311)
-+++ src/libfakechroot.c        (working copy)
-@@ -1479,7 +1479,7 @@
-     char *env;
-     char tmp[FAKECHROOT_MAXPATH], newfilename[FAKECHROOT_MAXPATH], argv0[FAKECHROOT_MAXPATH];
-     char *ptr;
--    unsigned int i, j, n;
-+    unsigned int i, j, n, len;
-     size_t sizeenvp;
-     char c;
-     char *fakechroot_path, *fakechroot_ptr, fakechroot_buf[FAKECHROOT_MAXPATH];
-@@ -1519,12 +1519,19 @@
-         errno = ENOMEM;
-         return -1;
-     }
--    for (ep = envp, i = 0; *ep != NULL; ++ep, ++i) {
--        newenvp[i] = *ep;
-+    for (ep = envp, i = 0; *ep != NULL; ++ep) {
-+      for (j = 0; j < sizeof (envkey) / sizeof (char *); j++) {
-+          len = strlen (envkey[j]);
-+          if (strncmp (*ep, envkey[j], len) == 0 && (*ep)[len] == '=')
-+              goto skip;
-+      }
-+      newenvp[i] = *ep;
-+      i++;
-+    skip: ;
-     }
-     /* Add our variables to newenvp */
--    newenvp = realloc( newenvp, ((sizeenvp + 1) * sizeof(char *) + sizeof(envkey)) );
-+    newenvp = realloc( newenvp, i * sizeof(char *) + sizeof(envkey) );
-     if (newenvp == NULL) {
-         errno = ENOMEM;
-         return -1;
diff --git a/febootstrap-install.pod b/febootstrap-install.pod
deleted file mode 100644 (file)
index 5035fee..0000000
+++ /dev/null
@@ -1,49 +0,0 @@
-=head1 NAME
-
-febootstrap-install - Install a file in an febootstrap root filesystem
-
-=head1 SYNOPSIS
-
- febootstrap-install ROOT LOCALFILE TARGETPATH MODE OWNER[.GROUP]
-
-=head1 DESCRIPTION
-
-This can be used to safely install a new file in an febootstrap
-root filesystem.
-
-Just copying a file in isn't usually safe, for reasons which are to do
-with the L<fakeroot(8)> command.
-
-=head1 EXAMPLE
-
- febootstrap-install initramfs new-fstab /etc/fstab ugo=rw root.root
-
-=head1 SEE ALSO
-
-L<febootstrap(8)>,
-L<febootstrap-run(8)>,
-L<fakeroot(1)>,
-L<fakechroot(1)>.
-
-=head1 AUTHORS
-
-Richard W.M. Jones <rjones @ redhat . com>
-
-=head1 COPYRIGHT
-
-(C) Copyright 2009 Red Hat Inc.,
-L<http://people.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-install.sh b/febootstrap-install.sh
deleted file mode 100755 (executable)
index 76b99b7..0000000
+++ /dev/null
@@ -1,45 +0,0 @@
-#!/bin/bash -
-# febootstrap-install
-# (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
-
-usage ()
-{
-    echo "Usage: febootstrap-install ROOT LOCALFILE TARGETPATH MODE OWNER[.GROUP]"
-    echo "Please read febootstrap-install(8) man page for more information."
-}
-
-if [ $# != 5 ]; then
-    usage
-    exit 1
-fi
-
-set -e
-
-# This is a carefully chosen sequence of commands which
-# tries not to disturb any inode numbers apart from the
-# one for the new file.
-cp "$2" "$1"/"$3"
-ino=$(ls -i "$1"/"$3" | awk '{print $1}')
-cp "$1"/fakeroot.log "$1"/fakeroot.log.old
-grep -v "ino=$ino," "$1"/fakeroot.log.old > "$1"/fakeroot.log
-rm "$1"/fakeroot.log.old
-febootstrap-run "$1" -- chmod "$4" "$3"
-febootstrap-run "$1" -- chown "$5" "$3"
diff --git a/febootstrap-minimize.pod b/febootstrap-minimize.pod
deleted file mode 100644 (file)
index 4665d87..0000000
+++ /dev/null
@@ -1,173 +0,0 @@
-=head1 NAME
-
-febootstrap-minimize - Minimize an febootstrap image
-
-=head1 SYNOPSIS
-
- febootstrap-minimize [--options] DIR
-
-=head1 DESCRIPTION
-
-I<febootstrap-minimize> minimizes an L<febootstrap(8)>-created
-filesystem.  This means that unneeded files and cruft are removed from
-the image.
-
-If no options are given, the default is to minimize the image as much
-as possible.  This means, for example, that locales are removed so the
-image will only be usable in US-English, there will be no
-documentation or manual pages, and the image will only work in a UTC
-timezone.
-
-Note that image minimization involves deleting files that have been
-installed by RPM.  Thus after minimization, it is no longer guaranteed
-that RPM will function correctly on the image.  You should only do
-this as a final step for "throwaway" appliances that do not need to be
-modified or upgraded in future.
-
-=head1 OPTIONS
-
-=over 4
-
-=item B<--all>
-
-Perform all minimization operations, to produce the smallest possible
-image.  Note in particular that locales are discarded.
-
-You can perform all minimization operations I<except> X, Y and Z by
-doing:
-
- febootstrap-minimize --all --keep-X --keep-Y --keep-Z ...
-
-(C<--all> can be omitted since it is the default).
-
-=item B<--none>
-
-Start with no minimization operations.  You can specify I<only>
-minimization operations X, Y and Z like this:
-
- febootstrap-minimize --none --drop-X --drop-Y --drop-Z ...
-
-=item B<--keep-locales>
-
-=item B<--drop-locales>
-
-Keep or drop locale support.
-
-=item B<--keep-docs>
-
-=item B<--drop-docs>
-
-Keep or drop documentation, man pages and info files.
-
-=item B<--keep-cracklib>
-
-=item B<--drop-cracklib>
-
-Keep or drop cracklib libraries.
-
-=item B<--keep-i18n>
-
-=item B<--drop-i18n>
-
-Keep or drop C</usr/share/i18n>.
-
-=item B<--keep-zoneinfo>
-
-=item B<--drop-zoneinfo>
-
-Keep or drop all timezones (except UTC which is never deleted).
-
-=item B<--keep-rpmdb>
-
-=item B<--drop-rpmdb>
-
-Keep or drop the RPM and YUM package databases.  Obviously RPM and YUM will be
-completely non-functional if you drop these.
-
-=item B<--keep-yum-cache>
-
-=item B<--drop-yum-cache>
-
-Keep or drop the yum cache.  Note that L<febootstrap(8)> has already
-deleted this directory unless you ran it with the C<--no-clean>
-option.
-
-=item B<--keep-services>
-
-=item B<--drop-services>
-
-Keep or drop the C</etc/services> file.  If dropped, this file is
-replaced with a very minimal one which just lists the most common
-services.  For less common services you will have to refer to them by
-port number instead of name.
-
-=item B<--keep-sln>
-
-=item B<--drop-sln>
-
-Keep or drop C</sbin/sln> (statically linked C<ln>).  This is not
-really required in minimal appliances.
-
-=item B<--keep-ldconfig>
-
-=item B<--drop-ldconfig>
-
-Keep or drop C</sbin/ldconfig>, C</etc/ld.so.cache> and
-C</var/cache/ldconfig> (the dynamic linking cache).  This is not
-needed.  Dynamic linking during program execution will be marginally
-slower.
-
-=item B<--pack-executables>
-
-This option has been removed in febootstrap 2.5.  In previous versions
-it was used to pack executables using the external C<upx> program.
-However it was not enabled by default and never worked very
-effectively.
-
-=back
-
-=head1 TODO
-
-=over 4
-
-=item *
-
-Deduplicate files with the same content (by hardlinking them).
-See the program L<hardlink(1)>.
-
-=item *
-
-Remove unused binaries.
-
-=item *
-
-Remove unused libraries.
-
-=back
-
-=head1 SEE ALSO
-
-L<febootstrap(8)>.
-
-=head1 AUTHORS
-
-Richard W.M. Jones <rjones @ redhat . com>
-
-=head1 COPYRIGHT
-
-(C) Copyright 2009 Red Hat Inc.,
-L<http://people.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-minimize.sh b/febootstrap-minimize.sh
deleted file mode 100755 (executable)
index 15782f7..0000000
+++ /dev/null
@@ -1,281 +0,0 @@
-#!/bin/bash -
-# febootstrap minimize
-# (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,all,none,keep-locales,drop-locales,keep-docs,drop-docs,keep-cracklib,drop-cracklib,keep-i18n,drop-i18n,keep-zoneinfo,drop-zoneinfo,keep-rpmdb,drop-rpmdb,keep-yum-cache,drop-yum-cache,keep-services,drop-services,keep-sln,drop-sln,keep-ldconfig,drop-ldconfig \
-        -n febootstrap-minimize -- "$@"`
-if [ $? != 0 ]; then
-    echo "febootstrap-minimize: problem parsing the command line arguments"
-    exit 1
-fi
-eval set -- "$TEMP"
-
-set_all ()
-{
-  keep_locales=no
-     keep_docs=no
- keep_cracklib=no
-     keep_i18n=no
- keep_zoneinfo=no
-    keep_rpmdb=no
-keep_yum_cache=no
- keep_services=no
-      keep_sln=no
- keep_ldconfig=no
-}
-
-set_none ()
-{
-  keep_locales=yes
-     keep_docs=yes
- keep_cracklib=yes
-     keep_i18n=yes
- keep_zoneinfo=yes
-    keep_rpmdb=yes
-keep_yum_cache=yes
- keep_services=yes
-      keep_sln=yes
- keep_ldconfig=yes
-}
-
-set_all
-
-usage ()
-{
-    echo "Usage: febootstrap-minimize [--options] DIR"
-    echo "Please read febootstrap-minimize(8) man page for more information."
-}
-
-while true; do
-    case "$1" in
-       --all)
-           set_all
-           shift;;
-       --none)
-           set_none
-           shift;;
-       --keep-locales)
-           keep_locales=yes
-           shift;;
-       --drop-locales)
-           keep_locales=no
-           shift;;
-       --keep-docs)
-           keep_docs=yes
-           shift;;
-       --drop-docs)
-           keep_docs=no
-           shift;;
-       --keep-cracklib)
-           keep_cracklib=yes
-           shift;;
-       --drop-cracklib)
-           keep_cracklib=no
-           shift;;
-       --keep-i18n)
-           keep_i18n=yes
-           shift;;
-       --drop-i18n)
-           keep_i18n=no
-           shift;;
-       --keep-zoneinfo)
-           keep_zoneinfo=yes
-           shift;;
-       --drop-zoneinfo)
-           keep_zoneinfo=no
-           shift;;
-       --keep-rpmdb)
-           keep_rpmdb=yes
-           shift;;
-       --drop-rpmdb)
-           keep_rpmdb=no
-           shift;;
-       --keep-yum-cache)
-           keep_yum_cache=yes
-           shift;;
-       --drop-yum-cache)
-           keep_yum_cache=no
-           shift;;
-       --keep-services)
-           keep_services=yes
-           shift;;
-       --drop-services)
-           keep_services=no
-           shift;;
-       --keep-sln)
-           keep_sln=yes
-           shift;;
-       --drop-sln)
-           keep_sln=no
-           shift;;
-       --keep-ldconfig)
-           keep_ldconfig=yes
-           shift;;
-       --drop-ldconfig)
-           keep_ldconfig=no
-           shift;;
-       --help)
-           usage
-           exit 0;;
-       --)
-           shift
-           break;;
-       *)
-           echo "Internal error!"
-           exit 1;;
-    esac
-done
-
-if [ $# -lt 1 ]; then
-    usage
-    exit 1
-fi
-
-target="$1"
-
-if [ ! -d "$target" ]; then
-    echo "febootstrap-minimize: $target: target directory not found"
-    exit 1
-fi
-
-# 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
-
-#----------------------------------------------------------------------
-
-# ***NOTE*** Wildcards cannot be passed to febootstrap-run.
-
-if [ "$keep_locales" != "yes" ]; then
-    febootstrap-run "$target" -- rm -rf usr/lib/locale
-    febootstrap-run "$target" -- rm -rf usr/share/locale
-    febootstrap-run "$target" -- rm -rf usr/lib/gconv usr/lib64/gconv
-    febootstrap-run "$target" -- rm -f usr/bin/localedef
-    febootstrap-run "$target" -- rm -f usr/sbin/build-locale-archive
-fi
-
-if [ "$keep_docs" != "yes" ]; then
-    febootstrap-run "$target" -- rm -rf usr/share/man
-    febootstrap-run "$target" -- rm -rf usr/share/doc
-    febootstrap-run "$target" -- rm -rf usr/share/info
-    febootstrap-run "$target" -- rm -rf usr/share/gnome/help
-fi
-
-if [ "$keep_cracklib" != "yes" ]; then
-    febootstrap-run "$target" -- rm -rf usr/share/cracklib
-fi
-
-if [ "$keep_i18n" != "yes" ]; then
-    febootstrap-run "$target" -- rm -rf usr/share/i18n
-fi
-
-if [ "$keep_zoneinfo" != "yes" ]; then
-    mv "$target"/usr/share/zoneinfo/{UCT,UTC,Universal,Zulu,GMT*,*.tab} \
-      "$target"
-    febootstrap-run "$target" -- rm -rf usr/share/zoneinfo
-    febootstrap-run "$target" -- mkdir -p --mode=0755 usr/share/zoneinfo
-    mv "$target"/{UCT,UTC,Universal,Zulu,GMT*,*.tab} \
-      "$target"/usr/share/zoneinfo/
-fi
-
-if [ "$keep_rpmdb" != "yes" ]; then
-    febootstrap-run "$target" -- rm -rf var/lib/rpm
-    febootstrap-run "$target" -- mkdir -p --mode=0755 var/lib/rpm
-    febootstrap-run "$target" -- rm -rf var/lib/yum
-    febootstrap-run "$target" -- mkdir -p --mode=0755 var/lib/yum
-fi
-
-if [ "$keep_yum_cache" != "yes" ]; then
-    febootstrap-run "$target" -- rm -rf var/cache/yum
-    febootstrap-run "$target" -- mkdir -p --mode=0755 var/cache/yum
-fi
-
-if [ "$keep_services" != "yes" ]; then
-    # NB: Overwrite the same file so that we have the same inode,
-    # since fakeroot tracks files by inode number.
-    cat > "$target"/etc/services <<'__EOF__'
-tcpmux 1/tcp
-tcpmux 1/udp
-echo 7/tcp
-echo 7/udp
-discard 9/tcp sink null
-discard 9/udp sink null
-ftp 21/tcp
-ftp 21/udp fsp fspd
-ssh 22/tcp
-ssh 22/udp
-telnet 23/tcp
-telnet 23/udp
-smtp 25/tcp mail
-smtp 25/udp mail
-time 37/tcp timserver
-time 37/udp timserver
-nameserver 42/tcp name
-nameserver 42/udp name
-domain 53/tcp
-domain 53/udp
-bootps 67/tcp
-bootps 67/udp
-bootpc 68/tcp dhcpc
-bootpc 68/udp dhcpc
-tftp 69/tcp
-tftp 69/udp
-finger 79/tcp
-finger 79/udp
-http 80/tcp www www-http
-http 80/udp www www-http
-http 80/sctp
-kerberos 88/tcp kerberos5 krb5
-kerberos 88/udp kerberos5 krb5
-pop3 110/tcp pop-3
-pop3 110/udp pop-3
-sunrpc 111/tcp portmapper rpcbind
-sunrpc 111/udp portmapper rpcbind
-auth 113/tcp authentication tap ident
-auth 113/udp authentication tap ident
-ntp 123/tcp
-ntp 123/udp
-imap 143/tcp imap2
-imap 143/udp imap2
-snmp 161/tcp
-snmp 161/udp
-snmptrap 162/tcp
-snmptrap 162/udp snmp-trap
-__EOF__
-fi
-
-if [ "$keep_sln" != "yes" ]; then
-    febootstrap-run "$target" -- rm -f sbin/sln
-fi
-
-if [ "$keep_ldconfig" != "yes" ]; then
-    febootstrap-run "$target" -- rm -f sbin/ldconfig
-    febootstrap-run "$target" -- rm -f etc/ld.so.cache
-    febootstrap-run "$target" -- rm -rf var/cache/ldconfig
-    febootstrap-run "$target" -- mkdir -p --mode=0755 var/cache/ldconfig
-fi
diff --git a/febootstrap-run.pod b/febootstrap-run.pod
deleted file mode 100644 (file)
index 0e5d1f3..0000000
+++ /dev/null
@@ -1,95 +0,0 @@
-=head1 NAME
-
-febootstrap-run - Run extra commands in febootstrap root filesystem
-
-=head1 SYNOPSIS
-
- febootstrap-run [--options] DIR [--] [CMD ...]
-
-=head1 DESCRIPTION
-
-This can be used to run extra commands in the febootstrap root
-filesystem.  It is just a simple wrapper around the standard
-C<fakeroot> and C<fakechroot> commands.
-
-If given, the C<CMD ...> is run inside the root filesystem.  The
-command acts as if it was run as root and chrooted into the root
-filesystem.
-
-If the command is omitted, then we start a shell.
-
-If C<CMD ...> could contain anything starting with a C<-> character
-then use C<--> to separate C<febootstrap-run> parameters from the
-command:
-
- febootstrap-run ./f10 -- ls -l
-
-=head1 OPTIONS
-
-=over 4
-
-=item B<--ro>
-
-Usually any changes to permissions made by the command are recorded in
-the C<fakeroot.log> file.  However if C<--ro> flag is given, then
-changes to permissions are not recorded.  (Note: changes to file
-contents still happen).
-
-=back
-
-=head1 EXAMPLES
-
-Remove a directory subtree safely:
-
- febootstrap-run initramfs -- rm -r /etc
-
-(This requires that you have a compatible 'rm' command in the root).
-
-Another way to do complex operations from a script is to export a
-function:
-
- #!/bin/bash
-
- do_stuff ()
- {
-     # complex operations inside the root
- }
- export -f do_stuff
- febootstrap-run root -- bash -c do_stuff
-
-=head1 ENVIRONMENT VARIABLES
-
-Some L<fakechroot(1)> environment variables are applicable.  In
-particular you may want to set:
-
- export FAKECHROOT_EXCLUDE_PATH=/proc
-
-=head1 SEE ALSO
-
-L<febootstrap(8)>,
-L<febootstrap-install(8)>,
-L<fakeroot(1)>,
-L<fakechroot(1)>.
-
-=head1 AUTHORS
-
-Richard W.M. Jones <rjones @ redhat . com>
-
-=head1 COPYRIGHT
-
-(C) Copyright 2009 Red Hat Inc.,
-L<http://people.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-run.sh b/febootstrap-run.sh
deleted file mode 100755 (executable)
index 6c5e9b9..0000000
+++ /dev/null
@@ -1,88 +0,0 @@
-#!/bin/bash -
-# febootstrap-run
-# (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,ro \
-        -n febootstrap-run -- "$@"`
-if [ $? != 0 ]; then
-    echo "febootstrap-run: problem parsing the command line arguments"
-    exit 1
-fi
-eval set -- "$TEMP"
-
-readonly=no
-
-usage ()
-{
-    echo "Usage: febootstrap-run [--options] DIR [CMD]"
-    echo "Please read febootstrap-run(8) man page for more information."
-}
-
-while true; do
-    case "$1" in
-       --ro)
-           readonly=yes
-           shift;;
-       --help)
-           usage
-           exit 0;;
-       --)
-           shift
-           break;;
-       *)
-           echo "Internal error!"
-           exit 1;;
-    esac
-done
-
-if [ $# -lt 1 ]; then
-    usage
-    exit 1
-fi
-
-target="$1"
-shift
-
-if [ $(id -u) -eq 0 ]; then
-    chroot "$target" "$@"
-    exit 0
-fi
-
-if [ $(id -u) -ne 0 -a ! -f "$target"/fakeroot.log ]; then
-    echo "febootstrap-run: $target: not a root filesystem"
-    exit 1
-fi
-
-if [ "$readonly" = "no" ]; then
-    if [ $(id -u) -ne 0 ]; then
-       fakeroot -i "$target"/fakeroot.log -s "$target"/fakeroot.log \
-           fakechroot -s \
-           chroot "$target" "$@"
-    else
-       chroot "$target" "$@"
-    fi
-else
-    fakeroot -i "$target"/fakeroot.log \
-       fakechroot -s \
-       chroot "$target" "$@"
-fi
diff --git a/febootstrap-to-initramfs.pod b/febootstrap-to-initramfs.pod
deleted file mode 100644 (file)
index c3eab0a..0000000
+++ /dev/null
@@ -1,114 +0,0 @@
-=head1 NAME
-
-febootstrap-to-initramfs - Convert febootstrap root to initramfs (cpio) file.
-
-=head1 SYNOPSIS
-
- febootstrap-to-initramfs [--files=filelist] DIR > initrd.img
-
-=head1 DESCRIPTION
-
-I<febootstrap-to-initramfs> converts the filesystem created by
-L<febootstrap(8)> into an initramfs image.  This allows the new system
-to be booted on real hardware or inside a QEMU-based virtual machine.
-
-An initramfs image is just a compressed cpio file, so you could
-uncompress it with L<gunzip(1)> and use L<cpio(1)> to convert it into
-other formats.
-
-The permissions inside the initrd image are corrected automatically
-(see the discussion of fakeroot logfile in the L<febootstrap(8)>
-page).  You do I<not> need to run this command as root.
-
-=head1 OPTIONS
-
-=over 4
-
-=item --files=filelist
-
-C<filelist> should be a file containing a list of the files to be
-added to the initramfs (one per line).  Only those files are added and
-any others are ignored.
-
-When the C<--files> option is not given, all files in C<DIR> are added
-to the initramfs image.
-
-=item --nocompress
-
-This prevents the initramfs image from being compressed.
-
-Linux can boot from uncompressed initramfs images (in fact, faster),
-but they take up a lot more space on disk.
-
-=back
-
-=head1 /init
-
-Normal initramfs images start by executing the program or script
-called C</init>.  febootstrap does not create this script, so you may
-wish to, particularly for very minimal bootstraps that don't have the
-normal SysVinit/upstart machinery.  It's also required if the kernel
-cannot find a "real" root filesystem (the root filesystem that we
-built and placed in an initramfs doesn't count).
-
-Linux will try to run the following commands in turn, unless you
-override it using the C<init=I<cmd>> kernel option:
-
-=over 4
-
-=item *
-
-/init
-
-=item *
-
-/sbin/init
-
-=item *
-
-/etc/init
-
-=item *
-
-/bin/init
-
-=item *
-
-/bin/sh
-
-=back
-
-=head1 MEMORY REQUIREMENTS
-
-Initramfs images are uncompressed by the kernel into memory.  When
-booting the new system you will need at least enough free RAM to store
-the B<uncompressed> filesystem plus extra to run any programs.  Bear
-this in mind when creating very large filesystems.
-
-=head1 SEE ALSO
-
-L<febootstrap(8)>,
-L<cpio(1)>.
-
-=head1 AUTHORS
-
-Richard W.M. Jones <rjones @ redhat . com>
-
-=head1 COPYRIGHT
-
-(C) Copyright 2009 Red Hat Inc.,
-L<http://people.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-initramfs.sh b/febootstrap-to-initramfs.sh
deleted file mode 100755 (executable)
index dc72963..0000000
+++ /dev/null
@@ -1,98 +0,0 @@
-#!/bin/bash -
-# febootstrap-to-initramfs
-# (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 files:,nocompress,help \
-        -n febootstrap-to-initramfs -- "$@"`
-if [ $? != 0 ]; then
-    echo "febootstrap-to-initramfs: problem parsing the command line arguments"
-    exit 1
-fi
-eval set -- "$TEMP"
-
-compress=yes
-files=
-
-usage ()
-{
-    echo "Usage: febootstrap-to-initramfs [--files=filelist] [--nocompress] DIR"
-    echo "Please read febootstrap-to-initramfs(8) man page for more information."
-}
-
-while true; do
-    case "$1" in
-       --files)
-           files=$2
-           shift 2;;
-       --help)
-           usage
-           exit 0;;
-       --nocompress)
-           compress=no
-           shift;;
-       --)
-           shift
-           break;;
-       *)
-           echo "Internal error!"
-           exit 1;;
-    esac
-done
-
-if [ $# -ne 1 ]; then
-    usage
-    exit 1
-fi
-
-cd "$1" > /dev/null
-
-if [ ! -f fakeroot.log -a $(id -u) -ne 0 ]; then
-    echo "no fakeroot.log and not running as root"
-    exit 1
-fi
-
-set -e
-
-(
-if [ -f fakeroot.log ]; then
-    if [ -z "$files" ]; then
-       fakeroot -i fakeroot.log \
-       sh -c 'find -not -name fakeroot.log -a -print0 | cpio --quiet -o -0 -H newc'
-    else
-       fakeroot -i fakeroot.log \
-       sh -c 'cpio --quiet -o -H newc' < $files
-    fi
-else
-    if [ -z "$files" ]; then
-       find -not -name fakeroot.log -a -print0 | cpio --quiet -o -0 -H newc
-    else
-       cpio --quiet -o -H newc < $files
-    fi
-fi
-) | (
-if [ "$compress" = "yes" ]; then
-    gzip --best
-else
-    cat
-fi
-)
diff --git a/febootstrap-to-supermin.pod b/febootstrap-to-supermin.pod
deleted file mode 100644 (file)
index 123327c..0000000
+++ /dev/null
@@ -1,206 +0,0 @@
-=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 usually of just two files, but can
-contain several files and directories from the list below:
-
-=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.
-
-Note that this file is a cpio file in cpio "newc" format, and is
-I<not> compressed (unlike initramfs files which are compressed cpio
-files).
-
-=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.*
-
-=item any directory
-
-You can specify a directory which should contain image file(s)
-and hostfile(s).
-
-Using a directory is useful either to keep the appliance-related files
-together, or to make more complex appliances containing optional bits.
-
-=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-2010 Red Hat Inc.,
-L<http://people.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
deleted file mode 100755 (executable)
index b6a2fd9..0000000
+++ /dev/null
@@ -1,174 +0,0 @@
-#!/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")
-
-    # For quoting problems with the bash =~ operator, see bash FAQ
-    # question E14 here http://tiswww.case.edu/php/chet/bash/FAQ and
-    # http://bugs.debian.org/cgi-bin/bugreport.cgi?bug=487387#25
-    # (RHBZ#566511).
-    p_etc='^\./etc'
-    p_dev='^\./dev'
-    p_var='^\./var'
-    p_lib_modules='^\./lib/modules/'
-    p_builddir='^\./builddir'
-    p_ld_so='^ld-[.0-9]+\.so$'
-    p_libbfd='^libbfd-.*\.so$'
-    p_libgcc='^libgcc_s-.*\.so\.([0-9]+)$'
-    p_libntfs3g='^libntfs-3g\.so\..*$'
-    p_lib123so='^lib(.*)-[-.0-9]+\.so$'
-    p_lib123so123='^lib(.*)-[-.0-9]+\.so\.([0-9]+)\.'
-    p_libso123='^lib(.*)\.so\.([0-9]+)\.'
-
-    # Ignore fakeroot.log.
-    if [ "$path" = "./fakeroot.log" ]; then
-       :
-
-    # All we're going to keep are the special files /init,
-    # configuration files (/etc), devices and modifiable stuff (/var).
-    elif [ "$path" = "./init" ]; then
-        echo "$path" >&5
-
-    # Get timezone configuration from local system.
-    elif [ "$path" = "./etc/localtime" ]; then
-        echo "$path" >&6
-
-    elif [[ "$path" =~ $p_etc || "$path" =~ $p_dev || "$path" =~ $p_var ]]
-    then
-        echo "$path" >&5
-
-    # Kernel modules are always copied in from the host, including all
-    # the dependency information files.
-    elif [[ "$path" =~ $p_lib_modules ]]; then
-       :
-
-    # On mock/Koji, exclude bogus /builddir directory which for some
-    # reason contains some yum temporary files (RHBZ#566512).
-    elif [[ "$path" =~ $p_builddir ]]; then
-        :
-
-    # 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" =~ $p_ld_so ]]; then
-        echo "$dir/ld-*.so" >&6
-
-    # Special case for libbfd
-    elif [[ "$file" =~ $p_libbfd ]]; then
-        echo "$dir/libbfd-*.so" >&6
-
-    # Special case for libgcc_s-<gccversion>-<date>.so.N
-    elif [[ "$file" =~ $p_libgcc ]]; then
-        echo "$dir/libgcc_s-*.so.${BASH_REMATCH[1]}" >&6
-
-    # Special case for libntfs-3g.so.*
-    elif [[ "$file" =~ $p_libntfs3g ]]; then
-       [ -n "$libntfs3g_once" ] || echo "$dir/libntfs-3g.so.*" >&6
-       libntfs3g_once=1
-
-    # libfoo-1.2.3.so
-    elif [[ "$file" =~ $p_lib123so ]]; then
-        echo "$dir/lib${BASH_REMATCH[1]}-*.so" >&6
-
-    # libfoo-1.2.3.so.1.2.3 (but NOT '*.so.N')
-    elif [[ "$file" =~ $p_lib123so123 ]]; then
-        echo "$dir/lib${BASH_REMATCH[1]}-*.so.${BASH_REMATCH[2]}.*" >&6
-
-    # libfoo.so.1.2.3 (but NOT '*.so.N')
-    elif [[ "$file" =~ $p_libso123 ]]; 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.ml b/febootstrap.ml
new file mode 100644 (file)
index 0000000..966a316
--- /dev/null
@@ -0,0 +1,394 @@
+(* febootstrap 3
+ * Copyright (C) 2009-2010 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+ *)
+
+open Unix
+open Printf
+
+open Febootstrap_package_handlers
+open Febootstrap_utils
+open Febootstrap_cmdline
+
+(* Create a temporary directory for use by all the functions in this file. *)
+let tmpdir = tmpdir ()
+
+let () =
+  debug "%s %s" Config.package_name Config.package_version;
+
+  (* Instead of printing out warnings as we go along, accumulate them
+   * in lists and print them all out at the end.
+   *)
+  let warn_unreadable = ref [] in
+
+  (* Determine which package manager this system uses. *)
+  check_system ();
+  let ph = get_package_handler () in
+
+  debug "selected package handler: %s" (get_package_handler_name ());
+
+  (* Not --names: check files exist. *)
+  if not names_mode then (
+    List.iter (
+      fun pkg ->
+        if not (file_exists pkg) then (
+          eprintf "febootstrap: %s: no such file (did you miss out the --names option?)\n" pkg;
+          exit 1
+        )
+    ) packages
+  );
+
+  (* --names: resolve the package list to a full list of package names
+   * (including dependencies).
+   *)
+  let packages =
+    if names_mode then (
+      let packages = ph.ph_resolve_dependencies_and_download packages in
+      debug "resolved packages: %s" (String.concat " " packages);
+      packages
+    )
+    else packages in
+
+  (* Get the list of files. *)
+  let files =
+    List.flatten (
+      List.map (
+        fun pkg ->
+          let files = ph.ph_list_files pkg in
+          List.map (fun (filename, ft) -> filename, ft, pkg) files
+      ) packages
+    ) in
+
+  (* Sort and combine duplicate files. *)
+  let files =
+    let files = List.sort compare files in
+
+    let combine (name1, ft1, pkg1) (name2, ft2, pkg2) =
+      (* Rules for combining files. *)
+      if ft1.ft_config || ft2.ft_config then (
+        eprintf "febootstrap: error: %s is a config file which is listed in two packages (%s, %s)\n"
+          name1 pkg1 pkg2;
+        exit 1
+      );
+      if (ft1.ft_dir || ft2.ft_dir) && (not (ft1.ft_dir && ft2.ft_dir)) then (
+        eprintf "febootstrap: error: %s appears as both directory and ordinary file (%s, %s)\n"
+          name1 pkg1 pkg2;
+        exit 1
+      );
+      if ft1.ft_ghost then
+        (name2, ft2, pkg2)
+      else
+        (name1, ft1, pkg1)
+    in
+
+    let rec loop = function
+      | [] -> []
+      | (name1, _, _ as f1) :: (name2, _, _ as f2) :: fs when name1 = name2 ->
+          let f = combine f1 f2 in loop (f :: fs)
+      | f :: fs -> f :: loop fs
+    in
+    loop files in
+
+  (* Because we may have excluded some packages, and also because of
+   * distribution packaging errors, it's not necessarily true that a
+   * directory is created before each file in that directory.
+   * Determine those missing directories and add them now.
+   *)
+  let files =
+    let insert_dir, dir_seen =
+      let h = Hashtbl.create (List.length files) in
+      let insert_dir dir = Hashtbl.replace h dir true in
+      let dir_seen dir = Hashtbl.mem h dir in
+      insert_dir, dir_seen
+    in
+    let files =
+      List.map (
+        fun (path, { ft_dir = is_dir }, _ as f) ->
+          if is_dir then
+            insert_dir path;
+
+          let rec loop path =
+            let parent = Filename.dirname path in
+            if dir_seen parent then []
+            else (
+              insert_dir parent;
+              let newdir = (parent, { ft_dir = true; ft_config = false;
+                                      ft_ghost = false; ft_mode = 0o40755 },
+                            "") in
+              newdir :: loop parent
+            )
+          in
+          List.rev (f :: loop path)
+      ) files in
+    List.flatten files in
+
+  (* Debugging. *)
+  debug "%d files and directories" (List.length files);
+  if false then (
+    List.iter (
+      fun (name, { ft_dir = dir; ft_ghost = ghost; ft_config = config;
+                   ft_mode = mode }, pkg) ->
+        printf "%s [%s%s%s%o] from %s\n" name
+          (if dir then "dir " else "")
+          (if ghost then "ghost " else "")
+          (if config then "config " else "")
+          mode
+          pkg
+    ) files
+  );
+
+  (* Split the list of files into ones for hostfiles and ones for base image. *)
+  let p_hmac = Str.regexp "/\\.*\\.hmac$" in
+
+  let hostfiles = ref []
+  and baseimgfiles = ref [] in
+  List.iter (
+    fun (path, {ft_dir = dir; ft_ghost = ghost; ft_config = config} ,_ as f) ->
+      (* Ignore boot files, kernel, kernel modules.  Supermin appliances
+       * are booted from external kernel and initrd, and
+       * febootstrap-supermin-helper copies the host kernel modules.
+       * Note we want to keep the /boot and /lib/modules directory entries.
+       *)
+      if string_prefix "/boot/" path then ()
+      else if string_prefix "/lib/modules/" path then ()
+
+      (* Always write directory names to both output files. *)
+      else if dir then (
+        hostfiles := f :: !hostfiles;
+        baseimgfiles := f :: !baseimgfiles;
+      )
+
+      (* Timezone configuration is config, but copy it from host system. *)
+      else if path = "/etc/localtime" then
+        hostfiles := f :: !hostfiles
+
+      (* Ignore FIPS files (.*.hmac) (RHBZ#654638). *)
+      else if Str.string_match p_hmac path 0 then ()
+
+      (* Ghost files are created empty in the base image. *)
+      else if ghost then
+        baseimgfiles := f :: !baseimgfiles
+
+      (* For config files we can't rely on the host-installed copy
+       * since the admin may have modified then.  We have to get the
+       * original file from the package and put it in the base image.
+       *)
+      else if config then
+        baseimgfiles := f :: !baseimgfiles
+
+      (* Anything else comes from the host. *)
+      else
+        hostfiles := f :: !hostfiles
+  ) files;
+  let hostfiles = List.rev !hostfiles
+  and baseimgfiles = List.rev !baseimgfiles in
+
+  (* Write hostfiles. *)
+
+  (* Regexps used below. *)
+  let p_ld_so = Str.regexp "^ld-[.0-9]+\\.so$" in
+  let p_libbfd = Str.regexp "^libbfd-.*\\.so$" in
+  let p_libgcc = Str.regexp "^libgcc_s-.*\\.so\\.\\([0-9]+\\)$" in
+  let p_libntfs3g = Str.regexp "^libntfs-3g\\.so\\..*$" in
+  let p_lib123so = Str.regexp "^lib\\(.*\\)-[-.0-9]+\\.so$" in
+  let p_lib123so123 =
+    Str.regexp "^lib\\(.*\\)-[-.0-9]+\\.so\\.\\([0-9]+\\)\\." in
+  let p_libso123 = Str.regexp "^lib\\(.*\\)\\.so\\.\\([0-9]+\\)\\." in
+  let ntfs3g_once = ref false in
+
+  let chan = open_out (tmpdir // "hostfiles") in
+  List.iter (
+    fun (path, {ft_dir = is_dir; ft_ghost = ghost; ft_config = config;
+                ft_mode = mode }, _) ->
+      let dir = Filename.dirname path in
+      let file = Filename.basename path in
+
+      if is_dir then
+        fprintf chan "%s\n" path
+
+      (* Warn about hostfiles which are unreadable by non-root.  We
+       * won't be able to add those to the appliance at run time, but
+       * there's not much else we can do about it except get the
+       * distros to fix this nonsense.
+       *)
+      else if mode land 0o004 = 0 then
+        warn_unreadable := path :: !warn_unreadable
+
+      (* Replace fixed numbers in some library names by wildcards. *)
+      else if Str.string_match p_ld_so file 0 then
+        fprintf chan "%s/ld-*.so\n" dir
+
+      (* Special case for libbfd. *)
+      else if Str.string_match p_libbfd file 0 then
+        fprintf chan "%s/libbfd-*.so\n" dir
+
+      (* Special case for libgcc_s-<gccversion>-<date>.so.N *)
+      else if Str.string_match p_libgcc file 0 then
+        fprintf chan "%s/libgcc_s-*.so.%s\n" dir (Str.matched_group 1 file)
+
+      (* Special case for libntfs-3g.so.* *)
+      else if Str.string_match p_libntfs3g file 0 then (
+        if not !ntfs3g_once then (
+          fprintf chan "%s/libntfs-3g.so.*\n" dir;
+          ntfs3g_once := true
+        )
+      )
+
+      (* libfoo-1.2.3.so *)
+      else if Str.string_match p_lib123so file 0 then
+        fprintf chan "%s/lib%s-*.so\n" dir (Str.matched_group 1 file)
+
+      (* libfoo-1.2.3.so.123 (but NOT '*.so.N') *)
+      else if Str.string_match p_lib123so123 file 0 then
+        fprintf chan "%s/lib%s-*.so.%s.*\n" dir
+          (Str.matched_group 1 file) (Str.matched_group 2 file)
+
+      (* libfoo.so.1.2.3 (but NOT '*.so.N') *)
+      else if Str.string_match p_libso123 file 0 then
+        fprintf chan "%s/lib%s.so.%s.*\n" dir
+          (Str.matched_group 1 file) (Str.matched_group 2 file)
+
+      (* Anything else comes from the host. *)
+      else
+        fprintf chan "%s\n" path
+  ) hostfiles;
+  close_out chan;
+
+  (* Write base.img.
+   *
+   * We have to create directories and copy files to tmpdir/root
+   * and then call out to cpio to construct the initrd.
+   *)
+  let rootdir = tmpdir // "root" in
+  mkdir rootdir 0o755;
+  List.iter (
+    fun (path, { ft_dir = is_dir; ft_ghost = ghost; ft_config = config;
+                 ft_mode = mode }, pkg) ->
+      (* Always write directory names to both output files. *)
+      if is_dir then (
+        (* Directory permissions are fixed up below. *)
+        if path <> "/" then mkdir (rootdir // path) 0o755
+      )
+
+      (* Ghost files are just touched with the correct perms. *)
+      else if ghost then (
+        let chan = open_out (rootdir // path) in
+        close_out chan;
+        chmod (rootdir // path) (mode land 0o777 lor 0o400)
+      )
+
+      (* For config files we can't rely on the host-installed copy
+       * since the admin may have modified it.  We have to get the
+       * original file from the package.
+       *)
+      else if config then (
+        let outfile = ph.ph_get_file_from_package pkg path in
+
+        (* Note that the output config file might not be a regular file. *)
+        let statbuf = lstat outfile in
+
+        let destfile = rootdir // path in
+
+        (* Depending on the file type, copy it to destination. *)
+        match statbuf.st_kind with
+        | S_REG ->
+            (* Unreadable files (eg. /etc/gshadow).  Make readable. *)
+            if statbuf.st_perm = 0 then chmod outfile 0o400;
+            let cmd =
+              sprintf "cp %s %s"
+                (Filename.quote outfile) (Filename.quote destfile) in
+            run_command cmd;
+            chmod destfile (mode land 0o777 lor 0o400)
+        | S_LNK ->
+            let link = readlink outfile in
+            symlink link destfile
+        | S_DIR -> assert false
+        | S_CHR
+        | S_BLK
+        | S_FIFO
+        | S_SOCK ->
+            eprintf "febootstrap: error: %s: don't know how to handle this type of file\n" path;
+            exit 1
+      )
+
+      else
+        assert false (* should not be reached *)
+  ) baseimgfiles;
+
+  (* Fix up directory permissions, in reverse order.  Since we don't
+   * want to have a read-only directory that we can't write into above.
+   *)
+  List.iter (
+    fun (path, { ft_dir = is_dir; ft_mode = mode }, _) ->
+      if is_dir then chmod (rootdir // path) (mode land 0o777 lor 0o700)
+  ) (List.rev baseimgfiles);
+
+  (* Construct the 'base.img' initramfs.  Feed in the list of filenames
+   * partly because we conveniently have them, and partly because
+   * this results in a nice alphabetical ordering in the cpio file.
+   *)
+  (*let cmd = sprintf "ls -lR %s" rootdir in
+  ignore (Sys.command cmd);*)
+  let cmd =
+    sprintf "(cd %s && cpio --quiet -o -0 -H newc) > %s"
+      rootdir (tmpdir // "base.img") in
+  let chan = open_process_out cmd in
+  List.iter (fun (path, _, _) -> fprintf chan ".%s\000" path) baseimgfiles;
+  let stat = close_process_out chan in
+  (match stat with
+   | WEXITED 0 -> ()
+   | WEXITED i ->
+       eprintf "febootstrap: command '%s' failed (returned %d), see earlier error messages\n" cmd i;
+       exit i
+   | WSIGNALED i ->
+       eprintf "febootstrap: command '%s' killed by signal %d" cmd i;
+       exit 1
+   | WSTOPPED i ->
+       eprintf "febootstrap: command '%s' stopped by signal %d" cmd i;
+       exit 1
+  );
+
+  (* Undo directory permissions, because rm -rf can't delete files in
+   * unreadable directories.
+   *)
+  List.iter (
+    fun (path, { ft_dir = is_dir; ft_mode = mode }, _) ->
+      if is_dir then chmod (rootdir // path) 0o755
+  ) (List.rev baseimgfiles);
+
+  (* Print warnings. *)
+  if warnings then (
+    (match !warn_unreadable with
+     | [] -> ()
+     | paths ->
+         eprintf "febootstrap: warning: some host files are unreadable by non-root\nGet your distro to fix these files:\n";
+         List.iter
+           (fun path -> eprintf "\t%s\n" path)
+           (List.sort compare paths)
+    );
+  );
+
+  (* Near-atomically copy files to the final output directory. *)
+  let cmd =
+    sprintf "mv %s %s"
+      (Filename.quote (tmpdir // "base.img"))
+      (Filename.quote (outputdir // "base.img")) in
+  run_command cmd;
+  let cmd =
+    sprintf "mv %s %s"
+      (Filename.quote (tmpdir // "hostfiles"))
+      (Filename.quote (outputdir // "hostfiles")) in
+  run_command cmd
index b1e512b..c76b4eb 100644 (file)
+=encoding utf8
+
 =head1 NAME
 
-febootstrap - Bootstrap a basic Fedora system (like Debian debootstrap)
+febootstrap - Bootstrapping tool for creating supermin appliances
 
 =head1 SYNOPSIS
 
- febootstrap [--options] REPO TARGET [MIRROR]
-
-=head1 EXAMPLES
-
- febootstrap fedora-10 /tmp/f10
- febootstrap rawhide /tmp/rawhide
- febootstrap rawhide /tmp/rawhide http://mymirror/rawhide/x86_64/os
- febootstrap --groupinstall="Mail Server" fedora-10 /tmp/mailserver
+ febootstrap [-o OUTPUTDIR] --names LIST OF PKGS ...
+ febootstrap [-o OUTPUTDIR] PKG FILE NAMES ...
 
 =head1 DESCRIPTION
 
-febootstrap creates a Fedora root filesystem, based on the Fedora
-version specified by I<REPO> under the directory specified by
-I<TARGET>.  Optionally I<MIRROR> can point to a local mirror
-(otherwise the public Fedora mirrors are used).  I<REPO> names are
-C<fedora-I<VERSION>> (eg. C<fedora-10>) or C<rawhide>.
-
-febootstrap does I<not> need to be run as root.  If for some reason
-you do run it as root, then it works slightly differently and may have
-side effects such as stopping or starting system daemons.
-
-For more advanced needs, take a look at L<mock(1)>, C<livecd-creator>
-and I<thincrust.net>'s C<appliance-creator>.
-
-The normal output is a root directory located at I<TARGET> and
-a fakeroot logfile at C<I<TARGET>/fakeroot.log>.
+febootstrap is a tool for building supermin appliances.  These are
+tiny appliances (similar to virtual machines), usually around 100KB in
+size, which get fully instantiated on-the-fly in a fraction of a
+second when you need to boot one of them.
+
+Originally "fe" in febootstrap stood for "Fedora", but this tool is
+now distro-independent and can build supermin appliances for several
+popular Linux distros, and adding support for others is reasonably
+easy.
+
+Note that this manual page documents febootstrap 3.x which is a
+complete rewrite and quite different from version 2.x.  If you are
+looking for the febootstrap 2.x tools, then this is not the right
+place.
+
+=head2 BASIC OPERATION
+
+There are two modes for using febootstrap.  With the I<--names>
+parameter, febootstrap takes a list of package names and creates a
+supermin appliance containing those packages and all dependencies that
+those packages require.  In this mode febootstrap usually needs
+network access because it may need to consult package repositories in
+order to work out dependencies and download packages.
+
+Without I<--names>, febootstrap takes a list of packages (ie.
+filenames of locally available packages).  This package set must be
+complete and consistent with no dependencies outside the set of
+packages you provide.  In this mode febootstrap does not require any
+network access.  It works by looking at the package files themselves.
+
+By "package" we mean the RPM, DEB, (etc.) package.  A package name
+might be the fully qualified name (eg. C<coreutils-8.5-7.fc14.x86_64>)
+or some abbreviation (eg. C<coreutils>).  The precise format of the
+name and what abbreviations are allowed depends on the package
+manager.
+
+The supermin appliance that febootstrap writes consists of two files
+called C<hostfiles> and C<base.img> (it is common for users to add
+more files).  By default these are written to the current directory.
+If you specify the I<-o OUTPUTDIR> option then these files are written
+to the named directory instead (traditionally this directory is named
+C<supermin.d> but you can call it whatever you want).
+
+In all cases febootstrap can only build a supermin appliance which is
+identical in distro, version and architecture to the host.  It does
+I<not> do cross-builds.
+
+=head2 MINIMIZING THE SUPERMIN APPLIANCE
+
+You may want to "minimize" the supermin appliance in order to save
+time and space when it is instantiated.  Typically you might want to
+remove documentation, info files, man pages and locales.  We used to
+provide a separate tool called C<febootstrap-minimize> for this
+purpose, but it is no longer provided.  Instead you can post-process
+C<hostfiles> yourself to remove any files or directories that you
+don't want (by removing lines from the file).  Be careful what you
+remove because files may be necessary for correct operation of the
+appliance.
+
+=head2 KERNEL AND KERNEL MODULES
+
+Usually the kernel and kernel modules are I<not> included in the
+supermin appliance.  When the appliance is instantiated, the kernel
+modules from the host kernel are copied in, and it is booted using the
+host kernel.
+
+=head2 BOOTING AND CACHING THE SUPERMIN APPLIANCE
+
+To instantiate and boot the supermin appliance you need to use the
+separate tool L<febootstrap-supermin-helper(8)>.  For fastest boot
+times you should cache the output of that tool.  See the libguestfs
+source file C<src/appliance.c> for an example of how this is done.
+
+=head2 ENFORCING AVAILABILITY OF HOSTFILES
+
+L<febootstrap-supermin-helper(8)> builds the appliance by copying in
+host files as listed in C<hostfiles>.  For this to work those host
+files must be available.  We usually enforce this by adding
+requirements (eg. RPM C<Requires:> lines) on the package that uses the
+supermin appliance, so that package cannot be installed without
+pulling in the dependent packages and thus making sure the host files
+are available.
 
 =head1 OPTIONS
 
 =over 4
 
-=item B<-i package>
-
-=item B<--install=package>
+=item B<--help>
 
-=item B<-g "group">
+Display brief command line usage, and exit.
 
-=item B<--groupinstall="group">
+=item B<--exclude REGEXP>
 
-Specify the package or group to install.  To list multiple packages or
-groups, you must give multiple C<-i> or C<-g> options.  Group names
-can contain spaces, so use quotes where necessary.
+After doing dependency resolution, exclude packages which match the
+regular expression.
 
-These are passed directly to C<yum install> or C<yum groupinstall>
-commands, and thus any dependencies are also resolved by yum.  You can
-also use shell globs and filenames here, as with ordinary yum.
+This option is only used with I<--names>, and it can be given multiple
+times on the command line.
 
-If no packages or groups are given, then we install the C<Core> group
-which is a small working Fedora installation (but by no means
-minimal).  Use C<yum groupinfo Core> to list the packages currently in
-the C<Core> group.
+=item B<--names>
 
-=item B<--no-clean>
+Provide a list of package names, instead of providing packages
+directly.  In this mode febootstrap may require network access.  See
+L</BASIC OPERATION> above.
 
-Normally febootstrap will clean up the yum repository
-(C</var/cache/yum> inside the image).  This contains the downloaded
-RPMs and metadata.  However if you give the C<--no-clean> option, then
-the yum repository is left.  This is useful if you want to run further
-yum commands inside the filesystem by hand.
+=item B<--no-warnings>
 
-=item B<-p "proxyurl">
+Don't print warnings about packaging problems.
 
-=item B<--proxy="proxyurl">
+=item B<-o outputdir>
 
-URL to the proxy server that yum should use.
+Select the output directory where the two supermin appliance files are
+written (C<hostfiles> and C<base.img>).  The default directory is the
+current directory.  Note that if this files exist already in the
+output directory then they will be overwritten.
 
-=item B<-u source>
+=item B<-v>
 
-=item B<--updates=source>
+=item B<--verbose>
 
-Pull in updates from an additional updates repository.  The possible
-sources are:
-
-=over 4
+Enable verbose messages.
 
-=item -u C<http://...> (a URL)
+=item B<-V>
 
-Get updates from the specific URL.
+=item B<--version>
 
-=item -u C<updates-released-fN> (an updates repository name)
-
-Get updates from the public mirrors of the named repository
-(eg. C<updates-released-f10>).  See REPOSITORIES below.
-
-=item -u C<none> (default)
-
-Don't add an updates repository.  This is the default.
-
-=back
+Print the package name and version number, and exit.
 
 =back
 
-=head1 REPOSITORIES
-
-You can list available repositories by visiting this URL:
-
-L<http://mirrors.fedoraproject.org/mirrorlist?repo=help&arch=i386>
-
-(If necessary replace C<i386> with your architecture, but it seems
-unlikely that this list will change based on architecture).
-
-=head1 RUNNING EXTRA COMMANDS IN THE ROOT FILESYSTEM
-
-If you want to run further commands inside the root filesystem, for
-example additional C<yum> installs, then use C<febootstrap-run>.  See
-the L<febootstrap-run(8)> manual page for more details.
-
-You have to be careful about modifying files in the root filesystem
-directly (without using C<febootstrap-run>).  It's easy to confuse
-fakeroot and end up with the wrong permissions on files (see FAKEROOT
-LOGFILE below).
-
-C<febootstrap-run> runs the command inside the root filesystem, which
-means it won't normally have access to files outside the root.  You
-can use C<FAKECHROOT_EXCLUDE_PATH> environment variable (see
-L<fakechroot(1)>) or copy files into the root first.
-
-=head2 FAKEROOT LOGFILE
-
-When febootstrap is run as non-root (the normal case) we use fakeroot
-so that yum thinks it is running as root.  Fakeroot keeps track of
-"real" file permissions in a log file which is saved into the target
-directory as C<I<TARGET>/fakeroot.log>.
-
-This logfile is indexed by inode number, which makes certain
-operations safe and other operations unsafe.
-Files should be replaced only by doing:
-
- echo updated-content > old-file
-
-(since that preserves the original inode).
-
-Deleting files and then creating new ones (even with a different name)
-is usually unsafe, because the new files might reuse inodes claimed by
-the old files, and so appear with peculiar permissions
-(eg. unreadable, or as a symbolic link).
-
-Deleting files is also usually unsafe, although the reasons are more
-subtle.  If you just use C<rm> then the inode number is not deleted
-from C<fakeroot.log> which means it can be reused by another file
-later on.
+=head1 SEE ALSO
 
-In most cases it's usually safest to use C<febootstrap-run>.
+L<febootstrap-supermin-helper(8)>,
+L<http://people.redhat.com/~rjones/febootstrap/>,
+L<guestfs(3)>,
+L<http://libguestfs.org/>.
 
-You can use the fakeroot logfile in a number of ways:
+=head1 AUTHORS
 
 =over 4
 
 =item *
 
-Use L<febootstrap-run(8)> to run a command with the faked file
-permissions.
-
-=item *
-
-Use L<febootstrap-install(8)> to install a file with permissions
-in the root filesystem.
+Richard W.M. Jones L<http://people.redhat.com/~rjones/>
 
 =item *
 
-Generate an initramfs (compressed cpio) file containing the correct
-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).
+Matthew Booth L<mbooth@redhat.com>
 
 =back
 
-=head1 RUNNING FEBOOTSTRAP AS ROOT
-
-There is some rudimentary support for running C<febootstrap> as root.
-However it is not well-tested and generally not recommended.
-
-=head1 COMPARISON TO DEBOOTSTRAP
-
-febootstrap cannot do cross-architecture installs (C<debootstrap
---foreign>).  The reason is that C<%pre> and C<%post> scripts cannot
-run.  It may be possible to defer running of scriptlets (which is
-basically how debootstrap works), and patches to do this are welcomed.
-
-febootstrap cannot do 32-on-64 bit installs.  The reason is that
-fakeroot and fakechroot do not load the correct preload library.  This
-is really a bug in fakeroot/fakechroot, which we think would be easy
-to fix.  (debootstrap deals with this case the same as for
-C<--foreign> installs - see previous point).
-
-=head1 OTHER RESTRICTIONS AND BUGS
-
-The following programs are not run during C<%post> scriptlets (because
-they are all statically linked, and fakechroot cannot run statically
-linked programs).
-
-=over 4
-
-=item C</sbin/ldconfig> (from many packages)
-
-=item C</usr/sbin/glibc_post_upgrade> (from C<glibc>)
-
-=item C</usr/sbin/build-locale-archive> (from C<glibc-common>)
-
-=item C</usr/sbin/libgcc_post_upgrade> (from C<libgcc>)
-
-=back
-
-If you wish, you can run them the first time you boot into the new
-machine.
-
-febootstrap recreates the repository anew each time, and this causes
-yum to download all the RPMs every time.  This is very wasteful, and
-we should provide a way to cache the repository.
-
-=head1 HOME PAGE
-
-L<http://people.redhat.com/~rjones/febootstrap>
-
-=head1 SEE ALSO
-
-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)>,
-L<rpm(8)>.
-
-=head1 ALTERNATIVES
-
-L<mock(1)>,
-L<http://fedoraproject.org/wiki/FedoraLiveCD/LiveCDHowTo>,
-L<http://thincrust.net/>,
-L<debootstrap(8)>,
-C<ubuntu-vm-builder>.
-
-=head1 AUTHORS
-
-Richard W.M. Jones <rjones @ redhat . com>
-
 =head1 COPYRIGHT
 
-(C) Copyright 2009 Red Hat Inc.,
-L<http://people.redhat.com/~rjones/febootstrap>.
+Copyright (C) 2009-2010 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
diff --git a/febootstrap.sh b/febootstrap.sh
deleted file mode 100755 (executable)
index 2965a7e..0000000
+++ /dev/null
@@ -1,245 +0,0 @@
-#!/bin/bash -
-# febootstrap
-# (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 g:i:p:u: \
-        --long groupinstall:,group-install:,help,install:,noclean,no-clean,proxy:,updates: \
-        -n febootstrap -- "$@"`
-if [ $? != 0 ]; then
-    echo "febootstrap: problem parsing the command line arguments"
-    exit 1
-fi
-eval set -- "$TEMP"
-
-declare -a packages
-packages[0]="@Core"
-i=0
-
-clean=yes
-
-usage ()
-{
-    echo "Usage: febootstrap [--options] REPO TARGET [MIRROR]"
-    echo "Please read febootstrap(8) man page for more information."
-}
-
-while true; do
-    case "$1" in
-       -i|--install)
-           packages[i++]="$2"
-           shift 2;;
-       -g|--groupinstall|--group-install)
-           packages[i++]="@$2"
-           shift 2;;
-       -p|--proxy)
-           proxy="proxy=$2"
-           shift 2;;
-       -u|--updates)
-           updates="$2";
-           shift 2;;
-       --noclean|--no-clean)
-           clean=no
-           shift;;
-       --help)
-           usage
-           exit 0;;
-       --)
-           shift
-           break;;
-       *)
-           echo "Internal error!"
-           exit 1;;
-    esac
-done
-
-if [ $# -lt 2 -o $# -gt 3 ]; then
-    usage
-    exit 1
-fi
-
-repo="$1"
-target="$2"
-mirror="$3"
-
-# Architecture is currently always the same as the current arch.  We
-# cannot do --foreign builds.  See discussion in the manpage.
-arch=$(uname -m)
-case $arch in
-    i?86) arch=i386 ;;
-esac
-
-# 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
-
-# Create the temporary repository configuration.  The name of the
-# repository is always 'febootstrap'.
-cat > $tmpdir/febootstrap.repo <<__EOF__
-[febootstrap]
-name=febootstrap $repo $arch
-failovermethod=priority
-enabled=1
-gpgcheck=0
-$proxy
-__EOF__
-
-# "Mirror" parameter is a bit misnamed, but it means a local mirror,
-# instead of the public Fedora mirrors.
-if [ -n "$mirror" ]; then
-    echo "baseurl=$mirror" >> "$tmpdir"/febootstrap.repo
-else
-    echo "mirrorlist=http://mirrors.fedoraproject.org/mirrorlist?repo=$repo&arch=$arch" >> "$tmpdir"/febootstrap.repo
-fi
-
-# Add the updates repository if asked.
-case "$updates" in
-    ""|none|no)
-       ;;
-    *://*)
-       cat >> $tmpdir/febootstrap.repo <<EOF
-
-[febootstrap-updates]
-name=febootstrap updates $arch
-failovermethod=priority
-enabled=1
-gpgcheck=0
-$proxy
-baseurl=$updates
-EOF
-addrepo=febootstrap-updates
-       ;;
-    *)
-       cat >> $tmpdir/febootstrap.repo <<EOF
-
-[febootstrap-updates]
-name=febootstrap $updates $arch
-failovermethod=priority
-enabled=1
-gpgcheck=0
-$proxy
-mirrorlist=http://mirrors.fedoraproject.org/mirrorlist?repo=$updates&arch=$arch
-EOF
-addrepo=febootstrap-updates
-       ;;
-esac
-
-# Create the target filesystem.
-rm -rf "$target"
-mkdir "$target"
-
-# Target must be an absolute path.
-target=$(cd "$target" > /dev/null; pwd)
-
-# This is necessary to keep yum happy.  It's not clear why yum can't
-# just create this file itself.
-mkdir -p "$target"/var/cache/yum/febootstrap/packages
-
-# NB: REQUIRED for useradd/groupadd to run properly.
-#
-# However this causes 'filesystem' RPM install to give the
-# following error.  Not sure how serious the error is:
-# error: unpacking of archive failed on file /proc: cpio: utime
-export FAKECHROOT_EXCLUDE_PATH=/proc
-
-# Substitute some statically-linked commands.  This is only supported
-# in fakechroot > 2.9.  For previous versions of fakechroot it is
-# ignored.
-export FAKECHROOT_CMD_SUBST=/sbin/ldconfig=/bin/true:/usr/sbin/glibc_post_upgrade.i686=/bin/true:/usr/sbin/glibc_post_upgrade.x86_64=/bin/true:/usr/sbin/build-locale-archive=/bin/true:/usr/sbin/libgcc_post_upgrade=/bin/true:/sbin/new-kernel-pkg=/bin/true:/usr/sbin/nscd=/bin/true
-
-# Use the libraries inside the chroot.
-export LD_LIBRARY_PATH="$target"/usr/lib64:"$target"/usr/lib:"$target"/lib64:"$target"/usr/lib
-
-# Make the device nodes inside the fake chroot.
-# (Copied from mock/backend.py)  Why isn't there a base package which
-# creates these?
-make_device_nodes ()
-{
-    mkdir "$target"/proc
-    mkdir "$target"/sys
-    mkdir "$target"/dev
-    (
-       cd "$target"/dev
-       mkdir pts
-       mkdir shm
-       mkdir mapper
-       mknod null c 1 3;    chmod 0666 null
-       mknod full c 1 7;    chmod 0666 full
-       mknod zero c 1 5;    chmod 0666 zero
-       mknod random c 1 8;  chmod 0666 random
-       mknod urandom c 1 9; chmod 0444 urandom
-       mknod tty c 5 0;     chmod 0666 tty
-       mknod console c 5 1; chmod 0600 console
-       mknod ptmx c 5 2;    chmod 0666 ptmx
-       ln -sf /proc/self/fd/0 stdin
-       ln -sf /proc/self/fd/1 stdout
-       ln -sf /proc/self/fd/2 stderr
-    )
-}
-export -f make_device_nodes
-export target
-
-if [ $(id -u) -ne 0 ]; then
-    fakeroot -s "$target"/fakeroot.log \
-    make_device_nodes
-else
-    make_device_nodes
-fi
-
-repos=febootstrap
-if [ -n "$addrepo" ]; then
-    repos="$repos,$addrepo"
-fi
-
-# Run yum.
-run_yum ()
-{
-    yum \
-       -y -c "$tmpdir"/febootstrap.repo \
-       --disablerepo=* --enablerepo=$repos \
-       --noplugins --nogpgcheck \
-       --installroot="$target" \
-       install "$@"
-}
-export -f run_yum
-export tmpdir
-export repos
-
-if [ $(id -u) -ne 0 ]; then
-    # Bash doesn't support exporting array variables, hence this
-    # tortuous workaround.
-    fakeroot -i "$target"/fakeroot.log -s "$target"/fakeroot.log \
-    fakechroot -s \
-    bash -c 'run_yum "$@"' run_yum "${packages[@]}"
-else
-    run_yum "${packages[@]}"
-fi
-
-# Clean up the yum repository.
-if [ "$clean" = "yes" ]; then
-    febootstrap-run "$target" -- rm -rf /var/cache/yum/febootstrap
-    febootstrap-run "$target" -- rm -rf /var/cache/yum/febootstrap-updates
-fi
diff --git a/febootstrap_cmdline.ml b/febootstrap_cmdline.ml
new file mode 100644 (file)
index 0000000..3ce1029
--- /dev/null
@@ -0,0 +1,82 @@
+(* febootstrap 3
+ * Copyright (C) 2009-2010 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+ *)
+
+open Printf
+
+let excludes = ref []
+let names_mode = ref false
+let outputdir = ref "."
+let packages = ref []
+let verbose = ref false
+let warnings = ref true
+
+let print_version () =
+  printf "%s %s\n" Config.package_name Config.package_version;
+  exit 0
+
+let add_exclude re =
+  excludes := Str.regexp re :: !excludes
+
+let argspec = Arg.align [
+  "--exclude", Arg.String add_exclude,
+    "regexp Exclude packages matching regexp";
+  "--names", Arg.Set names_mode,
+    " Specify set of root package names on command line";
+  "--no-warnings", Arg.Clear warnings,
+    " Suppress warnings";
+  "-o", Arg.Set_string outputdir,
+    "outputdir Set output directory (default: \".\")";
+  "-v", Arg.Set verbose,
+    " Enable verbose output";
+  "--verbose", Arg.Set verbose,
+    " Enable verbose output";
+  "-V", Arg.Unit print_version,
+    " Print package name and version, and exit";
+  "--version", Arg.Unit print_version,
+    " Print package name and version, and exit";
+]
+let anon_fn str =
+  packages := str :: !packages
+
+let usage_msg =
+  "\
+febootstrap - bootstrapping tool for creating supermin appliances
+Copyright (C) 2009-2010 Red Hat Inc.
+
+Usage:
+ febootstrap [-o OUTPUTDIR] --names LIST OF PKGS ...
+ febootstrap [-o OUTPUTDIR] PKG FILE NAMES ...
+
+For full instructions see the febootstrap(8) man page.
+
+Options:\n"
+
+let () =
+  Arg.parse argspec anon_fn usage_msg;
+  if !packages = [] then (
+    eprintf "febootstrap: no packages listed on the command line\n";
+    exit 1
+  )
+
+let excludes = List.rev !excludes
+let names_mode = !names_mode
+let outputdir = !outputdir
+let packages = List.rev !packages
+let warnings = !warnings
+
+let debug fs = ksprintf (fun str -> if !verbose then print_endline str) fs
diff --git a/febootstrap_cmdline.mli b/febootstrap_cmdline.mli
new file mode 100644 (file)
index 0000000..cbb6b87
--- /dev/null
@@ -0,0 +1,39 @@
+(* febootstrap 3
+ * Copyright (C) 2009-2010 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+ *)
+
+(** Command line parsing. *)
+
+val debug : ('a, unit, string, unit) format4 -> 'a
+  (** Print string (like printf), but only if --verbose was given on
+      the command line. *)
+
+val excludes : Str.regexp list
+  (** List of package regexps to exclude. *)
+
+val names_mode : bool
+  (** True if [--names] was given on the command line (otherwise
+      {!packages} is a list of filenames). *)
+
+val outputdir : string
+  (** Output directory. *)
+
+val packages : string list
+  (** List of packages or package names as supplied on the command line. *)
+
+val warnings : bool
+  (** If true, print warnings.  [--no-warnings] sets this to false. *)
diff --git a/febootstrap_debian.ml b/febootstrap_debian.ml
new file mode 100644 (file)
index 0000000..c0cfbac
--- /dev/null
@@ -0,0 +1,132 @@
+(* febootstrap 3
+ * Copyright (C) 2009-2010 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+ *)
+
+(* Debian support. *)
+
+open Unix
+open Printf
+
+open Febootstrap_package_handlers
+open Febootstrap_utils
+open Febootstrap_cmdline
+
+(* Create a temporary directory for use by all the functions in this file. *)
+let tmpdir = tmpdir ()
+
+let debian_detect () =
+  file_exists "/etc/debian_version" &&
+    Config.aptitude <> "no" && Config.dpkg <> "no"
+
+let debian_resolve_dependencies_and_download names =
+  let cmd =
+    sprintf "apt-cache depends --recurse -i %s | grep -v '^[<[:space:]]'"
+      (String.concat " " (List.map Filename.quote names)) in
+  let pkgs = run_command_get_lines cmd in
+
+  (* Exclude packages matching [--exclude] regexps on the command line. *)
+  let pkgs =
+    List.filter (
+      fun name ->
+        not (List.exists (fun re -> Str.string_match re name 0) excludes)
+    ) pkgs in
+
+  (* Download the packages. *)
+  let cmd =
+    sprintf "cd %s && aptitude download %s"
+      (Filename.quote tmpdir)
+      (String.concat " " (List.map Filename.quote pkgs)) in
+  run_command cmd;
+
+  (* Find out what aptitude downloaded. *)
+  let files = Sys.readdir tmpdir in
+
+  let pkgs = List.map (
+    fun pkg ->
+      (* Look for 'pkg_*.deb' in the list of files. *)
+      let pre = pkg ^ "_" in
+      let r = ref "" in
+      try
+       for i = 0 to Array.length files - 1 do
+         if string_prefix pre files.(i) then (
+           r := files.(i);
+           files.(i) <- "";
+           raise Exit
+         )
+       done;
+       eprintf "febootstrap: aptitude: error: no file was downloaded corresponding to package %s\n" pkg;
+       exit 1
+      with
+         Exit -> !r
+  ) pkgs in
+
+  List.sort compare pkgs
+
+let debian_list_files pkg =
+  debug "unpacking %s ..." pkg;
+
+  (* We actually need to extract the file in order to get the
+   * information about modes etc.
+   *)
+  let pkgdir = tmpdir // pkg ^ ".d" in
+  mkdir pkgdir 0o755;
+  let cmd =
+    sprintf "dpkg-deb --fsys-tarfile %s | (cd %s && tar xf -)"
+      (tmpdir // pkg) pkgdir in
+  run_command cmd;
+
+  let cmd = sprintf "cd %s && find ." pkgdir in
+  let lines = run_command_get_lines cmd in
+
+  let files = List.map (
+    fun path ->
+      assert (path.[0] = '.');
+      (* No leading '.' *)
+      let path =
+       if path = "." then "/"
+       else String.sub path 1 (String.length path - 1) in
+
+      (* Find out what it is and get the canonical filename. *)
+      let statbuf = lstat (pkgdir // path) in
+      let is_dir = statbuf.st_kind = S_DIR in
+
+      (* No per-file metadata like in RPM, but we can synthesize it
+       * from the path.
+       *)
+      let config = statbuf.st_kind = S_REG && string_prefix path "/etc/" in
+
+      let mode = statbuf.st_perm in
+
+      (path, { ft_dir = is_dir; ft_config = config; ft_mode = mode;
+              ft_ghost = false })
+  ) lines in
+
+  files
+
+(* Easy because we already unpacked the archive above. *)
+let debian_get_file_from_package pkg file =
+  tmpdir // pkg ^ ".d" // file
+
+let () =
+  let ph = {
+    ph_detect = debian_detect;
+    ph_resolve_dependencies_and_download =
+      debian_resolve_dependencies_and_download;
+    ph_list_files = debian_list_files;
+    ph_get_file_from_package = debian_get_file_from_package;
+  } in
+  register_package_handler "debian" ph
diff --git a/febootstrap_package_handlers.ml b/febootstrap_package_handlers.ml
new file mode 100644 (file)
index 0000000..72bb172
--- /dev/null
@@ -0,0 +1,79 @@
+(* febootstrap 3
+ * Copyright (C) 2009-2010 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+ *)
+
+open Unix
+open Printf
+
+open Febootstrap_utils
+open Febootstrap_cmdline
+
+type package_handler = {
+  ph_detect : unit -> bool;
+  ph_resolve_dependencies_and_download : string list -> string list;
+  ph_list_files : string -> (string * file_type) list;
+  ph_get_file_from_package : string -> string -> string
+}
+and file_type = {
+  ft_dir : bool;
+  ft_config : bool;
+  ft_ghost : bool;
+  ft_mode : int;
+}
+
+let tmpdir = tmpdir ()
+
+let handlers = ref []
+
+let register_package_handler name ph =
+  debug "registering package handler: %s" name;
+  handlers := (name, ph) :: !handlers
+
+let handler = ref None
+
+let check_system () =
+  try
+    handler := Some (
+      List.find (
+        fun (_, ph) ->
+          ph.ph_detect ()
+      ) !handlers
+    )
+  with Not_found ->
+    eprintf "\
+febootstrap: could not detect package manager used by this system or distro.
+
+If this is a new Linux distro, or not Linux, or a Linux distro that uses
+an unusual packaging format then you may need to port febootstrap.  If
+you are expecting that febootstrap should work on this system or distro
+then it may be that the package detection code is not working.
+";
+    exit 1
+
+let rec get_package_handler () =
+  match !handler with
+  | Some (_, ph) -> ph
+  | None ->
+      check_system ();
+      get_package_handler ()
+
+let rec get_package_handler_name () =
+  match !handler with
+  | Some (name, _) -> name
+  | None ->
+      check_system ();
+      get_package_handler_name ()
diff --git a/febootstrap_package_handlers.mli b/febootstrap_package_handlers.mli
new file mode 100644 (file)
index 0000000..673e448
--- /dev/null
@@ -0,0 +1,65 @@
+(* febootstrap 3
+ * Copyright (C) 2009-2010 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+ *)
+
+(** Generic package handler code. *)
+
+type package_handler = {
+  ph_detect : unit -> bool;
+  (** Detect if the current system uses this package manager. *)
+
+  ph_resolve_dependencies_and_download : string list -> string list;
+  (** [ph_resolve_dependencies_and_download pkgs]
+      Take a list of package names, and using the package manager
+      resolve those to a list of all the packages that are required
+      including dependencies.  Download the full list of packages and
+      dependencies into a tmpdir.  Return the list of full filenames.
+
+      Note this should also process the [excludes] list. *)
+
+  ph_list_files : string -> (string * file_type) list;
+  (** [ph_list_files pkg] lists the files and file metadata in the
+      package called [pkg] (a package file). *)
+
+  ph_get_file_from_package : string -> string -> string;
+  (** [ph_get_file_from_package pkg file] extracts the
+      single named file [file] from [pkg].  The path of the
+      extracted file is returned. *)
+}
+
+(* These file types are inspired by the metadata specifically
+ * stored by RPM.  We should look at what other package formats
+ * can use too.
+ *)
+and file_type = {
+  ft_dir : bool;               (** Is a directory. *)
+  ft_config : bool;            (** Is a configuration file. *)
+  ft_ghost : bool;             (** Is a ghost (created empty) file. *)
+  ft_mode : int;               (** File mode. *)
+}
+
+val register_package_handler : string -> package_handler -> unit
+  (** Register a package handler. *)
+
+val check_system : unit -> unit
+  (** Check which package manager this system uses. *)
+
+val get_package_handler : unit -> package_handler
+  (** Get the selected package manager for this system. *)
+
+val get_package_handler_name : unit -> string
+  (** Get the name of the selected package manager for this system. *)
diff --git a/febootstrap_utils.ml b/febootstrap_utils.ml
new file mode 100644 (file)
index 0000000..04c91ad
--- /dev/null
@@ -0,0 +1,145 @@
+(* febootstrap 3
+ * Copyright (C) 2009-2010 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+ *)
+
+open Unix
+open Printf
+
+let (//) = Filename.concat
+
+let file_exists name =
+  try access name [F_OK]; true
+  with Unix_error _ -> false
+
+let dir_exists name =
+  try (stat name).st_kind = S_DIR
+  with Unix_error _ -> false
+
+let rec uniq ?(cmp = Pervasives.compare) = function
+  | [] -> []
+  | [x] -> [x]
+  | x :: y :: xs when cmp x y = 0 ->
+      uniq ~cmp (x :: xs)
+  | x :: y :: xs ->
+      x :: uniq ~cmp (y :: xs)
+
+let sort_uniq ?(cmp = Pervasives.compare) xs =
+  let xs = List.sort cmp xs in
+  let xs = uniq ~cmp xs in
+  xs
+
+let rec input_all_lines chan =
+  try let line = input_line chan in line :: input_all_lines chan
+  with End_of_file -> []
+
+let run_command_get_lines cmd =
+  let chan = open_process_in cmd in
+  let lines = input_all_lines chan in
+  let stat = close_process_in chan in
+  (match stat with
+   | WEXITED 0 -> ()
+   | WEXITED i ->
+       eprintf "febootstrap: command '%s' failed (returned %d), see earlier error messages\n" cmd i;
+       exit i
+   | WSIGNALED i ->
+       eprintf "febootstrap: command '%s' killed by signal %d" cmd i;
+       exit 1
+   | WSTOPPED i ->
+       eprintf "febootstrap: command '%s' stopped by signal %d" cmd i;
+       exit 1
+  );
+  lines
+
+let run_command cmd =
+  if Sys.command cmd <> 0 then (
+    eprintf "febootstrap: %s: command failed, see earlier errors\n" cmd;
+    exit 1
+  )
+
+let run_python code args =
+  let cmd = sprintf "python -c %s %s"
+    (Filename.quote code)
+    (String.concat " " (List.map Filename.quote args)) in
+  if Sys.command cmd <> 0 then (
+    eprintf "febootstrap: external python program failed, see earlier error messages\n";
+    exit 1
+  )
+
+let tmpdir () =
+  let chan = open_in "/dev/urandom" in
+  let data = String.create 16 in
+  really_input chan data 0 (String.length data);
+  close_in chan;
+  let data = Digest.to_hex (Digest.string data) in
+  (* Note this is secure, because if the name already exists, even as a
+   * symlink, mkdir(2) will fail.
+   *)
+  let tmpdir = Filename.temp_dir_name // sprintf "febootstrap%s.tmp" data in
+  Unix.mkdir tmpdir 0o700;
+  at_exit
+    (fun () ->
+       let cmd = sprintf "rm -rf %s" (Filename.quote tmpdir) in
+       ignore (Sys.command cmd));
+  tmpdir
+
+let rec find s sub =
+  let len = String.length s in
+  let sublen = String.length sub in
+  let rec loop i =
+    if i <= len-sublen then (
+      let rec loop2 j =
+        if j < sublen then (
+          if s.[i+j] = sub.[j] then loop2 (j+1)
+          else -1
+        ) else
+          i (* found *)
+      in
+      let r = loop2 0 in
+      if r = -1 then loop (i+1) else r
+    ) else
+      -1 (* not found *)
+  in
+  loop 0
+
+let rec string_split sep str =
+  let len = String.length str in
+  let seplen = String.length sep in
+  let i = find str sep in
+  if i = -1 then [str]
+  else (
+    let s' = String.sub str 0 i in
+    let s'' = String.sub str (i+seplen) (len-i-seplen) in
+    s' :: string_split sep s''
+  )
+
+let string_prefix p str =
+  let len = String.length str in
+  let plen = String.length p in
+  len >= plen && String.sub str 0 plen = p
+
+let path_prefix p path =
+  let len = String.length path in
+  let plen = String.length p in
+  path = p || (len > plen && String.sub path 0 (plen+1) = (p ^ "/"))
+
+let rec filter_map f = function
+  | [] -> []
+  | x :: xs ->
+      let x = f x in
+      match x with
+      | None -> filter_map f xs
+      | Some x -> x :: filter_map f xs
diff --git a/febootstrap_utils.mli b/febootstrap_utils.mli
new file mode 100644 (file)
index 0000000..3087ee0
--- /dev/null
@@ -0,0 +1,73 @@
+(* febootstrap 3
+ * Copyright (C) 2009-2010 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+ *)
+
+(** Utilities. *)
+
+val file_exists : string -> bool
+  (** Return [true] iff file exists. *)
+
+val dir_exists : string -> bool
+  (** Return [true] iff dir exists. *)
+
+val uniq : ?cmp:('a -> 'a -> int) -> 'a list -> 'a list
+  (** Uniquify a list (the list must be sorted first). *)
+
+val sort_uniq : ?cmp:('a -> 'a -> int) -> 'a list -> 'a list
+  (** Sort and uniquify a list. *)
+
+val input_all_lines : in_channel -> string list
+  (** Input all lines from a channel, returning a list of lines. *)
+
+val run_command_get_lines : string -> string list
+  (** Run the command and read the list of lines that it prints to stdout. *)
+
+val run_command : string -> unit
+  (** Run a command using {!Sys.command} and exit if it fails.  Be careful
+      when constructing the command to properly quote any arguments
+      (using {!Filename.quote}). *)
+
+val run_python : string -> string list -> unit
+  (** [run_python code args] runs Python [code] with arguments [args].
+      This does not return anything, but exits with an error message
+      if the Python code returns an error. *)
+
+val tmpdir : unit -> string
+  (** [tmpdir ()] returns a newly created temporary directory.  The
+      tmp directory is automatically removed when the program exits.
+      Note that a fresh temporary directory is returned each time you
+      call this function. *)
+
+val (//) : string -> string -> string
+  (** [x // y] concatenates file paths [x] and [y] into a single path. *)
+
+val find : string -> string -> int
+(** [find str sub] searches for [sub] in [str], returning the index
+    or -1 if not found. *)
+
+val string_split : string -> string -> string list
+  (** [string_split sep str] splits [str] at [sep]. *)
+
+val string_prefix : string -> string -> bool
+  (** [string_prefix prefix str] returns true iff [str] starts with [prefix]. *)
+
+val path_prefix : string -> string -> bool
+  (** [path_prefix prefix path] returns true iff [path] is [prefix] or
+      [path] starts with [prefix/]. *)
+
+val filter_map : ('a -> 'b option) -> 'a list -> 'b list
+  (** map + filter *)
diff --git a/febootstrap_yum_rpm.ml b/febootstrap_yum_rpm.ml
new file mode 100644 (file)
index 0000000..43021cc
--- /dev/null
@@ -0,0 +1,222 @@
+(* febootstrap 3
+ * Copyright (C) 2009-2010 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+ *)
+
+(* Yum and RPM support. *)
+
+open Unix
+open Printf
+
+open Febootstrap_package_handlers
+open Febootstrap_utils
+open Febootstrap_cmdline
+
+(* Create a temporary directory for use by all the functions in this file. *)
+let tmpdir = tmpdir ()
+
+let yum_rpm_detect () =
+  (file_exists "/etc/redhat-release" || file_exists "/etc/fedora-release") &&
+    Config.yum <> "no" && Config.rpm <> "no"
+
+let yum_rpm_resolve_dependencies_and_download names =
+  (* Liberate this data from python. *)
+  let py = "
+import yum
+import yum.misc
+import sys
+
+yb = yum.YumBase ()
+#yum.logginglevels.setDebugLevel(0) -- doesn't work?
+
+# Look up the base packages from the command line.
+deps = dict ()
+pkgs = yb.pkgSack.returnPackages (patterns=sys.argv[2:])
+for pkg in pkgs:
+    deps[pkg] = False
+
+# Recursively find all the dependencies.
+stable = False
+while not stable:
+    stable = True
+    for pkg in deps.keys():
+        if deps[pkg] == False:
+            deps[pkg] = []
+            stable = False
+            for r in pkg.requires:
+                ps = yb.whatProvides (r[0], r[1], r[2])
+                best = yb._bestPackageFromList (ps.returnPackages ())
+                if best.name != pkg.name:
+                    deps[pkg].append (best)
+                    if not deps.has_key (best):
+                        deps[best] = False
+            deps[pkg] = yum.misc.unique (deps[pkg])
+
+# Write it to a file because yum spews garbage on stdout.
+f = open (sys.argv[1], \"w\")
+for pkg in deps.keys ():
+    f.write (\"%s %s %s %s %s\\n\" %
+             (pkg.name, pkg.epoch, pkg.version, pkg.release, pkg.arch))
+f.close ()
+" in
+  let tmpfile = tmpdir // "names.tmp" in
+  run_python py (tmpfile :: names);
+  let chan = open_in tmpfile in
+  let lines = input_all_lines chan in
+  close_in chan;
+
+  (* Get fields. *)
+  let pkgs =
+    List.map (
+      fun line ->
+        match string_split " " line with
+        | [name; epoch; version; release; arch] ->
+            name, int_of_string epoch, version, release, arch
+        | _ ->
+            eprintf "febootstrap: bad output from python script: '%s'" line;
+            exit 1
+    ) lines in
+
+  (* Something of a hack for x86_64: exclude all i[3456]86 packages. *)
+  let pkgs =
+    if Config.host_cpu = "x86_64" then (
+      List.filter (
+        function (_, _, _, _, ("i386"|"i486"|"i586"|"i686")) -> false
+        | _ -> true
+      ) pkgs
+    )
+    else pkgs in
+
+  (* Drop the kernel package to save time. *)
+  let pkgs =
+    List.filter (function ("kernel",_,_,_,_) -> false | _ -> true) pkgs in
+
+  (* Exclude packages matching [--exclude] regexps on the command line. *)
+  let pkgs =
+    List.filter (
+      fun (name, _, _, _, _) ->
+        not (List.exists (fun re -> Str.string_match re name 0) excludes)
+    ) pkgs in
+
+  (* Sort the list of packages, and remove duplicates (by name).
+   * XXX This is not quite right: we really want to keep the latest
+   * package if duplicates are found, but that would require a full
+   * version compare function.
+   *)
+  let pkgs = List.sort (fun a b -> compare b a) pkgs in
+  let pkgs =
+    let cmp (name1, _, _, _, _) (name2, _, _, _, _) = compare name1 name2 in
+    uniq ~cmp pkgs in
+  let pkgs = List.sort compare pkgs in
+
+  (* Construct package names. *)
+  let pkgnames = List.map (
+    function
+    | name, 0, version, release, arch ->
+        sprintf "%s-%s-%s.%s" name version release arch
+    | name, epoch, version, release, arch ->
+        sprintf "%d:%s-%s-%s.%s" epoch name version release arch
+  ) pkgs in
+
+  if pkgnames = [] then (
+    eprintf "febootstrap: yum-rpm: error: no packages to download\n";
+    exit 1
+  );
+
+  let cmd = sprintf "yumdownloader --destdir %s %s"
+    (Filename.quote tmpdir)
+    (String.concat " " (List.map Filename.quote pkgnames)) in
+  run_command cmd;
+
+  (* Return list of package filenames. *)
+  List.map (
+    (* yumdownloader doesn't include epoch in the filename *)
+    fun (name, _, version, release, arch) ->
+      sprintf "%s/%s-%s-%s.%s.rpm" tmpdir name version release arch
+  ) pkgs
+
+let rec yum_rpm_list_files pkg =
+  (* Run rpm -qlp with some extra magic. *)
+  let cmd =
+    sprintf "rpm -q --qf '[%%{FILENAMES} %%{FILEFLAGS:fflags} %%{FILEMODES}\\n]' -p %s"
+      pkg in
+  let lines = run_command_get_lines cmd in
+
+  let files =
+    filter_map (
+      fun line ->
+        match string_split " " line with
+        | [filename; flags; mode] ->
+            let test_flag = String.contains flags in
+            let mode = int_of_string mode in
+            if test_flag 'd' then None  (* ignore documentation *)
+            else
+              Some (filename, {
+                      ft_dir = mode land 0o40000 <> 0;
+                      ft_ghost = test_flag 'g'; ft_config = test_flag 'c';
+                      ft_mode = mode;
+                    })
+        | _ ->
+            eprintf "febootstrap: bad output from rpm command: '%s'" line;
+            exit 1
+    ) lines in
+
+  (* I've never understood why the base packages like 'filesystem' don't
+   * contain any /dev nodes at all.  This leaves every program that
+   * bootstraps RPMs to create a varying set of device nodes themselves.
+   * This collection was copied from mock/backend.py.
+   *)
+  let files =
+    let b = Filename.basename pkg in
+    if string_prefix "filesystem-" b then (
+      let dirs = [ "/proc"; "/sys"; "/dev"; "/dev/pts"; "/dev/shm";
+                   "/dev/mapper" ] in
+      let dirs =
+        List.map (fun name ->
+                    name, { ft_dir = true; ft_ghost = false;
+                            ft_config = false; ft_mode = 0o40755 }) dirs in
+      let devs = [ "/dev/null"; "/dev/full"; "/dev/zero"; "/dev/random";
+                   "/dev/urandom"; "/dev/tty"; "/dev/console";
+                   "/dev/ptmx"; "/dev/stdin"; "/dev/stdout"; "/dev/stderr" ] in
+      (* No need to set the mode because these will go into hostfiles. *)
+      let devs =
+        List.map (fun name ->
+                    name, { ft_dir = false; ft_ghost = false;
+                            ft_config = false; ft_mode = 0o644 }) devs in
+      dirs @ devs @ files
+    ) else files in
+
+  files
+
+let yum_rpm_get_file_from_package pkg file =
+  debug "extracting %s from %s ..." file (Filename.basename pkg);
+
+  let outfile = tmpdir // file in
+  let cmd =
+    sprintf "rpm2cpio %s | (cd %s && cpio --quiet -id .%s)"
+      (Filename.quote pkg) (Filename.quote tmpdir) (Filename.quote file) in
+  run_command cmd;
+  outfile
+
+let () =
+  let ph = {
+    ph_detect = yum_rpm_detect;
+    ph_resolve_dependencies_and_download =
+      yum_rpm_resolve_dependencies_and_download;
+    ph_list_files = yum_rpm_list_files;
+    ph_get_file_from_package = yum_rpm_get_file_from_package;
+  } in
+  register_package_handler "yum-rpm" ph
index 804fa49..fb356a4 100644 (file)
@@ -63,12 +63,9 @@ febootstrap-supermin-helper.8: febootstrap-supermin-helper.pod
          --release "$(PACKAGE_NAME)-$(PACKAGE_VERSION)" \
          $< > $@
 
-febootstrap-supermin-helper.txt: febootstrap-supermin-helper.pod
-       pod2text $< > $@
-
 endif
 
 EXTRA_DIST = \
-       febootstrap-supermin-helper.8 febootstrap-supermin-helper.txt \
-         febootstrap-supermin-helper.pod \
-         elf-default-arch
+       febootstrap-supermin-helper.8 \
+       febootstrap-supermin-helper.pod \
+       elf-default-arch
diff --git a/m4/ocaml.m4 b/m4/ocaml.m4
new file mode 100644 (file)
index 0000000..479f3a8
--- /dev/null
@@ -0,0 +1,240 @@
+dnl autoconf macros for OCaml
+dnl
+dnl Copyright © 2009      Richard W.M. Jones
+dnl Copyright © 2009      Stefano Zacchiroli
+dnl Copyright © 2000-2005 Olivier Andrieu
+dnl Copyright © 2000-2005 Jean-Christophe Filliâtre
+dnl Copyright © 2000-2005 Georges Mariano
+dnl
+dnl For documentation, please read the ocaml.m4 man page.
+
+AC_DEFUN([AC_PROG_OCAML],
+[dnl
+  # checking for ocamlc
+  AC_CHECK_TOOL([OCAMLC],[ocamlc],[no])
+
+  if test "$OCAMLC" != "no"; then
+     OCAMLVERSION=`$OCAMLC -v | sed -n -e 's|.*version* *\(.*\)$|\1|p'`
+     AC_MSG_RESULT([OCaml version is $OCAMLVERSION])
+     # If OCAMLLIB is set, use it
+     if test "$OCAMLLIB" = ""; then
+        OCAMLLIB=`$OCAMLC -where 2>/dev/null || $OCAMLC -v|tail -1|cut -d ' ' -f 4`
+     else
+        AC_MSG_RESULT([OCAMLLIB previously set; preserving it.])
+     fi
+     AC_MSG_RESULT([OCaml library path is $OCAMLLIB])
+
+     AC_SUBST([OCAMLVERSION])
+     AC_SUBST([OCAMLLIB])
+
+     # checking for ocamlopt
+     AC_CHECK_TOOL([OCAMLOPT],[ocamlopt],[no])
+     OCAMLBEST=byte
+     if test "$OCAMLOPT" = "no"; then
+       AC_MSG_WARN([Cannot find ocamlopt; bytecode compilation only.])
+     else
+       TMPVERSION=`$OCAMLOPT -v | sed -n -e 's|.*version* *\(.*\)$|\1|p' `
+       if test "$TMPVERSION" != "$OCAMLVERSION" ; then
+           AC_MSG_RESULT([versions differs from ocamlc; ocamlopt discarded.])
+           OCAMLOPT=no
+       else
+           OCAMLBEST=opt
+       fi
+     fi
+
+     AC_SUBST([OCAMLBEST])
+
+     # checking for ocamlc.opt
+     AC_CHECK_TOOL([OCAMLCDOTOPT],[ocamlc.opt],[no])
+     if test "$OCAMLCDOTOPT" != "no"; then
+       TMPVERSION=`$OCAMLCDOTOPT -v | sed -n -e 's|.*version* *\(.*\)$|\1|p' `
+       if test "$TMPVERSION" != "$OCAMLVERSION" ; then
+           AC_MSG_RESULT([versions differs from ocamlc; ocamlc.opt discarded.])
+       else
+           OCAMLC=$OCAMLCDOTOPT
+       fi
+     fi
+
+     # checking for ocamlopt.opt
+     if test "$OCAMLOPT" != "no" ; then
+       AC_CHECK_TOOL([OCAMLOPTDOTOPT],[ocamlopt.opt],[no])
+       if test "$OCAMLOPTDOTOPT" != "no"; then
+          TMPVERSION=`$OCAMLOPTDOTOPT -v | sed -n -e 's|.*version* *\(.*\)$|\1|p' `
+          if test "$TMPVERSION" != "$OCAMLVERSION" ; then
+             AC_MSG_RESULT([version differs from ocamlc; ocamlopt.opt discarded.])
+          else
+             OCAMLOPT=$OCAMLOPTDOTOPT
+          fi
+        fi
+     fi
+
+     AC_SUBST([OCAMLOPT])
+  fi
+
+  AC_SUBST([OCAMLC])
+
+  # checking for ocaml toplevel
+  AC_CHECK_TOOL([OCAML],[ocaml],[no])
+
+  # checking for ocamldep
+  AC_CHECK_TOOL([OCAMLDEP],[ocamldep],[no])
+
+  # checking for ocamlmktop
+  AC_CHECK_TOOL([OCAMLMKTOP],[ocamlmktop],[no])
+
+  # checking for ocamlmklib
+  AC_CHECK_TOOL([OCAMLMKLIB],[ocamlmklib],[no])
+
+  # checking for ocamldoc
+  AC_CHECK_TOOL([OCAMLDOC],[ocamldoc],[no])
+
+  # checking for ocamlbuild
+  AC_CHECK_TOOL([OCAMLBUILD],[ocamlbuild],[no])
+])
+
+
+AC_DEFUN([AC_PROG_OCAMLLEX],
+[dnl
+  # checking for ocamllex
+  AC_CHECK_TOOL([OCAMLLEX],[ocamllex],[no])
+  if test "$OCAMLLEX" != "no"; then
+    AC_CHECK_TOOL([OCAMLLEXDOTOPT],[ocamllex.opt],[no])
+    if test "$OCAMLLEXDOTOPT" != "no"; then
+       OCAMLLEX=$OCAMLLEXDOTOPT
+    fi
+  fi
+  AC_SUBST([OCAMLLEX])
+])
+
+AC_DEFUN([AC_PROG_OCAMLYACC],
+[dnl
+  AC_CHECK_TOOL([OCAMLYACC],[ocamlyacc],[no])
+  AC_SUBST([OCAMLYACC])
+])
+
+
+AC_DEFUN([AC_PROG_CAMLP4],
+[dnl
+  AC_REQUIRE([AC_PROG_OCAML])dnl
+
+  # checking for camlp4
+  AC_CHECK_TOOL([CAMLP4],[camlp4],[no])
+  if test "$CAMLP4" != "no"; then
+     TMPVERSION=`$CAMLP4 -v 2>&1| sed -n -e 's|.*version *\(.*\)$|\1|p'`
+     if test "$TMPVERSION" != "$OCAMLVERSION" ; then
+       AC_MSG_RESULT([versions differs from ocamlc])
+        CAMLP4=no
+     fi
+  fi
+  AC_SUBST([CAMLP4])
+
+  # checking for companion tools
+  AC_CHECK_TOOL([CAMLP4BOOT],[camlp4boot],[no])
+  AC_CHECK_TOOL([CAMLP4O],[camlp4o],[no])
+  AC_CHECK_TOOL([CAMLP4OF],[camlp4of],[no])
+  AC_CHECK_TOOL([CAMLP4OOF],[camlp4oof],[no])
+  AC_CHECK_TOOL([CAMLP4ORF],[camlp4orf],[no])
+  AC_CHECK_TOOL([CAMLP4PROF],[camlp4prof],[no])
+  AC_CHECK_TOOL([CAMLP4R],[camlp4r],[no])
+  AC_CHECK_TOOL([CAMLP4RF],[camlp4rf],[no])
+  AC_SUBST([CAMLP4BOOT])
+  AC_SUBST([CAMLP4O])
+  AC_SUBST([CAMLP4OF])
+  AC_SUBST([CAMLP4OOF])
+  AC_SUBST([CAMLP4ORF])
+  AC_SUBST([CAMLP4PROF])
+  AC_SUBST([CAMLP4R])
+  AC_SUBST([CAMLP4RF])
+])
+
+
+AC_DEFUN([AC_PROG_FINDLIB],
+[dnl
+  AC_REQUIRE([AC_PROG_OCAML])dnl
+
+  # checking for ocamlfind
+  AC_CHECK_TOOL([OCAMLFIND],[ocamlfind],[no])
+  AC_SUBST([OCAMLFIND])
+])
+
+
+dnl Thanks to Jim Meyering for working this next bit out for us.
+dnl XXX We should define AS_TR_SH if it's not defined already
+dnl (eg. for old autoconf).
+AC_DEFUN([AC_CHECK_OCAML_PKG],
+[dnl
+  AC_REQUIRE([AC_PROG_FINDLIB])dnl
+
+  AC_MSG_CHECKING([for OCaml findlib package $1])
+
+  unset found
+  unset pkg
+  found=no
+  for pkg in $1 $2 ; do
+    if $OCAMLFIND query $pkg >/dev/null 2>/dev/null; then
+      AC_MSG_RESULT([found])
+      AS_TR_SH([OCAML_PKG_$1])=$pkg
+      found=yes
+      break
+    fi
+  done
+  if test "$found" = "no" ; then
+    AC_MSG_RESULT([not found])
+    AS_TR_SH([OCAML_PKG_$1])=no
+  fi
+
+  AC_SUBST(AS_TR_SH([OCAML_PKG_$1]))
+])
+
+
+AC_DEFUN([AC_CHECK_OCAML_MODULE],
+[dnl
+  AC_MSG_CHECKING([for OCaml module $2])
+
+  cat > conftest.ml <<EOF
+open $3
+EOF
+  unset found
+  for $1 in $$1 $4 ; do
+    if $OCAMLC -c -I "$$1" conftest.ml >&5 2>&5 ; then
+      found=yes
+      break
+    fi
+  done
+
+  if test "$found" ; then
+    AC_MSG_RESULT([$$1])
+  else
+    AC_MSG_RESULT([not found])
+    $1=no
+  fi
+  AC_SUBST([$1])
+])
+
+
+dnl XXX Cross-compiling
+AC_DEFUN([AC_CHECK_OCAML_WORD_SIZE],
+[dnl
+  AC_REQUIRE([AC_PROG_OCAML])dnl
+  AC_MSG_CHECKING([for OCaml compiler word size])
+  cat > conftest.ml <<EOF
+  print_endline (string_of_int Sys.word_size)
+  EOF
+  OCAML_WORD_SIZE=`$OCAML conftest.ml`
+  AC_MSG_RESULT([$OCAML_WORD_SIZE])
+  AC_SUBST([OCAML_WORD_SIZE])
+])
+
+AC_DEFUN([AC_CHECK_OCAML_OS_TYPE],
+[dnl
+  AC_REQUIRE([AC_PROG_OCAML])dnl
+  AC_MSG_CHECKING([OCaml Sys.os_type])
+
+  cat > conftest.ml <<EOF
+  print_string(Sys.os_type);;
+EOF
+
+  OCAML_OS_TYPE=`$OCAML conftest.ml`
+  AC_MSG_RESULT([$OCAML_OS_TYPE])
+  AC_SUBST([OCAML_OS_TYPE])
+])