--- /dev/null
+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
*~
+*.cmi
+*.cmo
+*.cmx
*.o
*.a
.deps
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
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
-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.
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
-----------------------
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")
+
+++ /dev/null
-- 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
--- /dev/null
+(* 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@"
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.
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?)])
])
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
+++ /dev/null
-# 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
+++ /dev/null
-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
+++ /dev/null
-#!/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
+++ /dev/null
-#!/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
+++ /dev/null
-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) {
+++ /dev/null
-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
+++ /dev/null
-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;
+++ /dev/null
-=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.
+++ /dev/null
-#!/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"
+++ /dev/null
-=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.
+++ /dev/null
-#!/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
+++ /dev/null
-=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.
+++ /dev/null
-#!/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
+++ /dev/null
-=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.
+++ /dev/null
-#!/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
-)
+++ /dev/null
-=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.
+++ /dev/null
-#!/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
--- /dev/null
+(* 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
+=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
+++ /dev/null
-#!/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
--- /dev/null
+(* 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
--- /dev/null
+(* 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. *)
--- /dev/null
+(* 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
--- /dev/null
+(* 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 ()
--- /dev/null
+(* 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. *)
--- /dev/null
+(* 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
--- /dev/null
+(* 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 *)
--- /dev/null
+(* 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
--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
--- /dev/null
+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])
+])