[Libguestfs] [PATCH] appliance: Use supermin >= 5.

Richard W.M. Jones rjones at redhat.com
Tue Feb 25 23:36:42 UTC 2014


This requires the new version of supermin (5.1.0).
---
 .gitignore                                    |   6 +-
 README                                        |   5 +-
 appliance/Makefile.am                         | 102 ++--
 appliance/excludefiles.in                     |  29 ++
 appliance/excludelist.in                      |  57 ---
 appliance/hostfiles.in                        |  13 +
 appliance/libguestfs-make-fixed-appliance.in  |   2 +-
 appliance/libguestfs-make-fixed-appliance.pod |   3 +-
 appliance/make.sh.in                          |  36 +-
 appliance/packagelist.in                      |  88 ++--
 configure.ac                                  |  79 +--
 examples/guestfs-performance.pod              |  11 +-
 fish/guestfish.pod                            |  10 +-
 src/appliance.c                               | 675 ++++----------------------
 src/guestfs.pod                               |  21 +-
 test-tool/libguestfs-test-tool.pod            |   3 +-
 16 files changed, 274 insertions(+), 866 deletions(-)
 create mode 100644 appliance/excludefiles.in
 delete mode 100644 appliance/excludelist.in
 create mode 100644 appliance/hostfiles.in

diff --git a/.gitignore b/.gitignore
index 1ee7775..2813872 100644
--- a/.gitignore
+++ b/.gitignore
@@ -37,7 +37,10 @@ Makefile.in
 /align/stamp-virt-alignment-scan.pod
 /align/virt-alignment-scan
 /align/virt-alignment-scan.1
-/appliance/excludelist
+/appliance/daemon.tar.gz
+/appliance/excludefiles
+/appliance/hostfiles
+/appliance/init.tar.gz
 /appliance/libguestfs-make-fixed-appliance
 /appliance/libguestfs-make-fixed-appliance.1
 /appliance/make.sh
@@ -45,6 +48,7 @@ Makefile.in
 /appliance/stamp-libguestfs-make-fixed-appliance.pod
 /appliance/stamp-supermin
 /appliance/supermin.d
+/appliance/udev-rules.tar.gz
 /autom4te.cache
 /bash/virt-builder
 /bash/virt-cat
diff --git a/README b/README
index 3f477f0..8fdc041 100644
--- a/README
+++ b/README
@@ -61,12 +61,11 @@ The full requirements are described below.
 |              |             |   |  - virtio-block                         |
 |              |             |   |  - virtio-net                           |
 +--------------+-------------+---+-----------------------------------------+
-| supermin     | 4.1.0       | R | This is required on all distros.        |
-| febootstrap  | 3.20        |   | 'supermin' is the new name for          |
+| supermin     | 5.1.0       | R | This is required on all distros.        |
+|              |             |   | 'supermin' is the new name for          |
 |              |             |   | 'febootstrap'.                          |
 |              |             |   | For alternatives, see:                  |
 |              |             |   | libguestfs.org/download/binaries/appliance/
-|              |             |   | febootstrap 2.x WILL NOT WORK           |
 +--------------+-------------+---+-----------------------------------------+
 | glibc        |             | R | We use various glibc-isms.              |
 |              |             |   | Also glibc provides XDR, rpcgen.        |
diff --git a/appliance/Makefile.am b/appliance/Makefile.am
index a9cc9fe..b98ddf5 100644
--- a/appliance/Makefile.am
+++ b/appliance/Makefile.am
@@ -1,5 +1,5 @@
 # libguestfs
-# Copyright (C) 2009 Red Hat Inc.
+# Copyright (C) 2009-2014 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
@@ -19,8 +19,9 @@ include $(top_srcdir)/subdir-rules.mk
 
 EXTRA_DIST = \
 	99-guestfs-serial.rules \
-	excludelist.in \
+	excludefiles.in \
 	guestfsd.suppressions \
+	hostfiles.in \
 	init \
 	libguestfs-make-fixed-appliance.in \
 	libguestfs-make-fixed-appliance.pod \
@@ -32,24 +33,42 @@ superminfsdir = $(libdir)/guestfs/supermin.d
 
 fs_DATA =
 superminfs_DATA = \
-	supermin.d/init.img \
-	supermin.d/udev-rules.img
-
-if SUPERMIN_HELPER_COMPRESSED_CPIO
-GZ = .gz
-endif
+	supermin.d/init.tar.gz \
+	supermin.d/udev-rules.tar.gz
 
 if ENABLE_DAEMON
 superminfs_DATA += \
-	supermin.d/daemon.img$(GZ)
+	supermin.d/daemon.tar.gz
 endif
 
 if ENABLE_APPLIANCE
 superminfs_DATA += \
-	supermin.d/base.img$(GZ) \
+	supermin.d/base.tar.gz \
+	supermin.d/packages \
+	supermin.d/excludefiles \
 	supermin.d/hostfiles
 endif
 
+supermin.d/base.tar.gz \
+supermin.d/daemon.tar.gz \
+supermin.d/excludefiles \
+supermin.d/hostfiles \
+supermin.d/init.tar.gz \
+supermin.d/packages \
+supermin.d/udev-rules.tar.gz: stamp-supermin
+stamp-supermin: make.sh \
+	  packagelist \
+	  hostfiles \
+	  excludefiles \
+	  daemon.tar.gz \
+	  init.tar.gz \
+	  udev-rules.tar.gz
+	rm -f $@ supermin.d/base.tar.gz supermin.d/packages
+	./make.sh
+	cp -t supermin.d \
+	    daemon.tar.gz excludefiles hostfiles init.tar.gz udev-rules.tar.gz
+	touch $@
+
 # This used to be a configure-generated file.  However config.status
 # always touches the destination file, which means the appliance got
 # rebuilt too often.
@@ -65,57 +84,47 @@ if VALGRIND_DAEMON
 PACKAGELIST_CPP_FLAGS += -DVALGRIND_DAEMON=1
 endif
 
+excludefiles: excludefiles.in Makefile
+	m4 $(PACKAGELIST_CPP_FLAGS) $< | \
+	grep -v '^[[:space:]]*$$' | grep -v '^#' > $@-t
+	cmp -s $@ $@-t || mv $@-t $@
+	rm -f $@-t
+
+hostfiles: hostfiles.in Makefile
+	m4 $(PACKAGELIST_CPP_FLAGS) $< | \
+	grep -v '^[[:space:]]*$$' | grep -v '^#' > $@-t
+	cmp -s $@ $@-t || mv $@-t $@
+	rm -f $@-t
+
 packagelist: packagelist.in Makefile
-	cpp -undef $(PACKAGELIST_CPP_FLAGS) < $< | \
+	m4 $(PACKAGELIST_CPP_FLAGS) $< | \
 	grep -v '^[[:space:]]*$$' | grep -v '^#' > $@-t
 	cmp -s $@ $@-t || mv $@-t $@
 	rm -f $@-t
 
-excludelist: excludelist.in Makefile
-	cpp -undef $(PACKAGELIST_CPP_FLAGS) < $< | \
-	grep -v '^[[:space:]]*$$' | grep -v '^#' > $@-t
-	cmp -s $@ $@-t || mv $@-t $@
-	rm -f $@-t
-
-supermin.d/base.img$(GZ) supermin.d/hostfiles: stamp-supermin
-stamp-supermin: make.sh packagelist excludelist
-	rm -f $@ supermin.d/base.img$(GZ) supermin.d/hostfiles
-	./make.sh
-if SUPERMIN_HELPER_COMPRESSED_CPIO
-	gzip -9 supermin.d/base.img
-endif
-	touch $@
-
-supermin.d/daemon.img$(GZ): ../daemon/guestfsd guestfsd.suppressions
-	rm -f $@ $@-t $@-tt
+daemon.tar.gz: ../daemon/guestfsd guestfsd.suppressions
+	rm -f $@ $@-t
 	rm -rf tmp-d
 	mkdir -p tmp-d$(DAEMON_SUPERMIN_DIR) tmp-d/etc
 	ln ../daemon/guestfsd tmp-d$(DAEMON_SUPERMIN_DIR)/guestfsd
 	ln $(srcdir)/guestfsd.suppressions tmp-d/etc/guestfsd.suppressions
-	( cd tmp-d && find | cpio --quiet -o -H newc ) > $@-t
+	( cd tmp-d && tar zcf - * ) > $@-t
 	rm -r tmp-d
-if SUPERMIN_HELPER_COMPRESSED_CPIO
-	gzip -9 -c $@-t > $@-tt
-	mv $@-tt $@-t
-endif
 	mv $@-t $@
 
-supermin.d/init.img: init
-	rm -rf init.tmp $@ $@-t
-	mkdir init.tmp
-	cp $< init.tmp
-	(cd init.tmp; echo "init" | cpio --quiet -o -H newc) > $@-t
-	rm -r init.tmp
+init.tar.gz: init
+	rm -f $@ $@-t
+	tar zcf $@-t init
 	mv $@-t $@
 
 # We should put this file in /lib/udev/rules.d, but put it in /etc so
 # we don't have to deal with all the UsrMove crap in Fedora.
-supermin.d/udev-rules.img: 99-guestfs-serial.rules
+udev-rules.tar.gz: 99-guestfs-serial.rules
 	rm -f $@ $@-t
 	rm -rf tmp-u
 	mkdir -p tmp-u/etc/udev/rules.d
 	for f in $^; do ln $$f tmp-u/etc/udev/rules.d/$$(basename $$f); done
-	( cd tmp-u && find | cpio --quiet -o -H newc ) > $@-t
+	( cd tmp-u && tar zcf - etc ) > $@-t
 	rm -r tmp-u
 	mv $@-t $@
 
@@ -144,7 +153,14 @@ stamp-libguestfs-make-fixed-appliance.pod: libguestfs-make-fixed-appliance.pod
 
 # Make clean.
 
-CLEANFILES = packagelist excludelist \
+CLEANFILES = \
+	*~ \
+	daemon.tar.gz \
+	excludefiles \
+	hostfiles \
+	init.tar.gz \
 	libguestfs-make-fixed-appliance.1 \
+	packagelist \
 	stamp-libguestfs-make-fixed-appliance.pod \
-	supermin.d/*
+	supermin.d/* \
+	udev-rules.tar.gz
diff --git a/appliance/excludefiles.in b/appliance/excludefiles.in
new file mode 100644
index 0000000..9fce4a3
--- /dev/null
+++ b/appliance/excludefiles.in
@@ -0,0 +1,29 @@
+dnl This is the list of files excluded from the appliance, even if
+dnl they appear in packagelist.in (or more likely, as dependencies of
+dnl packages in packagelist.in).
+dnl
+dnl List is a list of wildcards, one per line, prefixed by a '-' character.
+dnl
+dnl This file is processed by m4 with one of the
+dnl following symbols defined (depending on the distro):
+dnl
+dnl   REDHAT=1     For Fedora, RHEL, EPEL and workalikes.
+dnl   DEBIAN=1     For Debian.
+dnl   UBUNTU=1     For Ubuntu.
+dnl   ARCHLINUX=1  For Archlinux.
+dnl
+dnl Note that any matching file will be dropped from the appliance.
+dnl Of course, this may break the appliance, so be careful.
+
+dnl The right kernel modules are added back by supermin.
+-/boot/*
+-/lib/modules/*
+
+-/usr/lib/locale/*
+-/usr/share/locale/*
+-/usr/share/man/*
+-/usr/share/doc/*
+-/usr/share/info/*
+-/usr/share/gnome/help/*
+-/usr/share/cracklib/*
+-/usr/share/i18n/*
diff --git a/appliance/excludelist.in b/appliance/excludelist.in
deleted file mode 100644
index aa4c406..0000000
--- a/appliance/excludelist.in
+++ /dev/null
@@ -1,57 +0,0 @@
-/* This is the list of distro packages which are
- * excluded from the appliance, even if they appear in
- * packagelist.in (or more likely, as dependencies of
- * packages in packagelist.in).
- *
- * List is a list of basic regular expressions, one per line.
- *
- * This file is processed by cpp with one of the
- * following symbols defined (depending on the distro):
- *
- *   REDHAT=1     For Fedora, RHEL, EPEL and workalikes.
- *   DEBIAN=1     For Debian.
- *   UBUNTU=1     For Ubuntu.
- *   ARCHLINUX=1  For Archlinux.
- *
- * Note that any file provided by one of these packages will
- * be dropped from the appliance.  Of course, this may break
- * the appliance, so be careful.  Other files are also dropped
- * from the appliance such as docs and man pages: see 'make.sh.in'
- * for the full details.
- */
-
-/* Basically the same with a few minor tweaks. */
-#ifdef UBUNTU
-#define DEBIAN 1
-#endif
-
-/* Don't need any Perl or Python appearing in the appliance. */
-^perl
-^python
-
-/* Plymouth is a graphical boot thing - not needed. */
-^plymouth
-
-/* Linux firmware. */
-^linux-firmware
-
-/* Keyboard maps - appliance is not interactive. */
-^kbd-misc
-
-#ifdef REDHAT
-
-/* Linux kernel.  febootstrap <= 3.18 used to exclude the kernel
- * package (only) by default, but since 3.19 it doesn't do this any
- * longer.
- */
-^kernel
-
-^fedora-logos
-^redhat-logos
-^dracut
-
-#endif
-
-#ifdef DEBIAN
-^file-rc
-#endif
diff --git a/appliance/hostfiles.in b/appliance/hostfiles.in
new file mode 100644
index 0000000..5ad46ea
--- /dev/null
+++ b/appliance/hostfiles.in
@@ -0,0 +1,13 @@
+dnl This is the list of extra files added to appliance.
+dnl
+dnl List is a list of wildcards, one per line.
+dnl
+dnl This file is processed by m4 with one of the
+dnl following symbols defined (depending on the distro):
+dnl
+dnl   REDHAT=1     For Fedora, RHEL, EPEL and workalikes.
+dnl   DEBIAN=1     For Debian.
+dnl   UBUNTU=1     For Ubuntu.
+dnl   ARCHLINUX=1  For Archlinux.
+
+/usr/share/augeas/lenses/*.aug
diff --git a/appliance/libguestfs-make-fixed-appliance.in b/appliance/libguestfs-make-fixed-appliance.in
index 121edc5..500f406 100644
--- a/appliance/libguestfs-make-fixed-appliance.in
+++ b/appliance/libguestfs-make-fixed-appliance.in
@@ -105,7 +105,7 @@ guestfish -a /dev/null run
 # Find the location of the appliance.
 cachedir="$(guestfish get-cachedir)"
 euid="$(id -u)"
-appliancedir="$cachedir/.guestfs-$euid"
+appliancedir="$cachedir/.guestfs-$euid/appliance.d"
 
 cp "$appliancedir/kernel" "$outputdir/kernel"
 cp "$appliancedir/initrd" "$outputdir/initrd"
diff --git a/appliance/libguestfs-make-fixed-appliance.pod b/appliance/libguestfs-make-fixed-appliance.pod
index e5a216d..ab0b0c9 100644
--- a/appliance/libguestfs-make-fixed-appliance.pod
+++ b/appliance/libguestfs-make-fixed-appliance.pod
@@ -130,7 +130,7 @@ be set using the C<LIBGUESTFS_PATH> environment variable.
 
 Normally a supermin appliance is located on this path (see
 L<supermin(1)/SUPERMIN APPLIANCE>).  libguestfs reconstructs this
-into a full appliance by running L<supermin-helper(1)>.
+into a full appliance by running C<supermin --build>.
 
 However, a simpler "fixed appliance" can also be used.  libguestfs
 detects this by looking for a directory on the path containing four
@@ -167,7 +167,6 @@ libguestfs, please see the L<guestfs(3)> manual page.
 
 L<guestfs(3)>,
 L<supermin(1)>,
-L<supermin-helper(1)>,
 L<xz(1)>,
 L<http://libguestfs.org/>,
 L<http://qemu.org/>.
diff --git a/appliance/make.sh.in b/appliance/make.sh.in
index 7fa494e..e23d368 100755
--- a/appliance/make.sh.in
+++ b/appliance/make.sh.in
@@ -20,42 +20,14 @@ unset CDPATH
 
 set -e
 
-# Turn excludelist file into command line arguments.
-exec 5<excludelist
-while read regexp <&5; do
-    excludes="$excludes --exclude $regexp"
-done
-exec 5<&-
+# Run supermin.
 
-# Run supermin on the package list.
-# NB: Keep using --yum-config (deprecated alias) here since both old
-# and new supermin still support it.
 if [ "x at SUPERMIN_PACKAGER_CONFIG@" != "xno" ]; then
-    extra="--yum-config @SUPERMIN_PACKAGER_CONFIG@"
+    extra="--packager-config @SUPERMIN_PACKAGER_CONFIG@"
 fi
 if [ "x at SUPERMIN_EXTRA_OPTIONS@" != "xno" ]; then
     extra="$extra @SUPERMIN_EXTRA_OPTIONS@"
 fi
 
-echo @SUPERMIN@ -v -o supermin.d --names $(< packagelist ) $excludes $extra
- at SUPERMIN@ -v -o supermin.d --names $(< packagelist ) $excludes $extra
-
-# Remove some things that we don't want in the appliance.  This is
-# copied from the old febootstrap-minimize.  However minimization is
-# not so important now that we are caching the appliance.
-< supermin.d/hostfiles \
-grep -v '^/usr/lib/locale' |
-grep -v '^/usr/share/locale' |
-grep -v '^/usr/share/man/' |
-grep -v '^/usr/share/doc/' |
-grep -v '^/usr/share/info/' |
-grep -v '^/usr/share/gnome/help/' |
-grep -v '^/usr/share/cracklib/' |
-grep -v '^/usr/share/i18n/' > supermin.d/hostfiles-t
-
-# Include any Augeas lenses from the host.
-if grep -q /usr/share/augeas/lenses supermin.d/hostfiles-t; then
-    echo "/usr/share/augeas/lenses/*.aug" >> supermin.d/hostfiles-t
-fi
-
-mv supermin.d/hostfiles-t supermin.d/hostfiles
+echo @SUPERMIN@ --prepare -v -o supermin.d $(< packagelist ) $extra
+ at SUPERMIN@ --prepare -v -o supermin.d $(< packagelist ) $extra
diff --git a/appliance/packagelist.in b/appliance/packagelist.in
index 2963033..d26bffb 100644
--- a/appliance/packagelist.in
+++ b/appliance/packagelist.in
@@ -1,34 +1,31 @@
-/* This is the list of distro packages which are
- * installed on the appliance.
- *
- * This file is processed by cpp with one of the
- * following symbols defined (depending on the distro):
- *
- *   REDHAT=1     For Fedora, RHEL, EPEL and workalikes.
- *   DEBIAN=1     For Debian.
- *   UBUNTU=1     For Ubuntu.
- *   ARCHLINUX=1  For Archlinux.
- *
- * There is also a list of packages which are excluded if they appear
- * as dependencies of the packages below.  See: excludelist.in
- *
- * To add arbitrary extra packages, use:
- *
- *   ./configure --with-extra-packages="gdb valgrind [etc]"
- */
+dnl This is the list of distro packages which are
+dnl installed on the appliance.
+dnl
+dnl This file is processed by m4 with one of the
+dnl following symbols defined (depending on the distro):
+dnl
+dnl   REDHAT=1     For Fedora, RHEL, EPEL and workalikes.
+dnl   DEBIAN=1     For Debian.
+dnl   UBUNTU=1     For Ubuntu.
+dnl   ARCHLINUX=1  For Archlinux.
+dnl
+dnl There is also a list of packages which are excluded if they appear
+dnl as dependencies of the packages below.  See: excludelist.in
+dnl
+dnl To add arbitrary extra packages, use:
+dnl
+dnl   ./configure --with-extra-packages="gdb valgrind [etc]"
 
-/* Basically the same with a few minor tweaks. */
-#ifdef UBUNTU
-#define DEBIAN 1
-#endif
+dnl Basically the same with a few minor tweaks.
+ifelse(UBUNTU,1,define(DEBIAN,1))
 
-#ifdef REDHAT
+ifelse(REDHAT,1,
   augeas-libs
   btrfs-progs
   cryptsetup
-  cryptsetup-luks /* old name used before Fedora 17 */
+  cryptsetup-luks      dnl old name used before Fedora 17
   e2fsprogs
-  /* e4fsprogs only exists on RHEL 5, will be ignored everywhere else. */
+  dnl e4fsprogs only exists on RHEL 5, will be ignored everywhere else.
   e4fsprogs
   genisoimage
   gfs-utils
@@ -40,7 +37,7 @@
   iputils
   kernel
   libcap
-  libldm /* only Fedora has this for now, but we should add it to others later*/
+  libldm               dnl only Fedora for now, others later
   nilfs-utils
   ntfsprogs
   ntfs-3g
@@ -49,14 +46,14 @@
   reiserfs-utils
   libselinux
   syslinux-extlinux
-  systemd /* for /sbin/reboot and udevd */
+  systemd              dnl for /sbin/reboot and udevd
   vim-minimal
   xz
   yajl
   zfs-fuse
-#endif /* REDHAT */
+)
 
-#ifdef DEBIAN
+ifelse(DEBIAN,1,
   bsdmainutils
   btrfs-tools
   cryptsetup
@@ -74,21 +71,21 @@
   libpcre3
   libyajl2
   linux-image
-  /* syslinux 'suggests' mtools, but in reality it's a hard dependency: */
+  dnl syslinux 'suggests' mtools, but in reality it's a hard dependency:
   mtools
   nilfs-tools
   ntfs-3g
   ntfsprogs
   openssh-client
   reiserfsprogs
-  sysvinit /* for /sbin/reboot */
+  sysvinit                  dnl for /sbin/reboot
   ufsutils
   vim-tiny
   xz-utils
   zfs-fuse
-#endif /* DEBIAN */
+)
 
-#ifdef ARCHLINUX
+ifelse(ARCHLINUX,1,
   augeas
   btrfs-progs
   cdrkit
@@ -111,9 +108,9 @@
   xz
   yajl
   zfs-fuse
-#endif /* ARCHLINUX */
+)
 
-#ifdef FRUGALWARE
+ifelse(FRUGALWARE,1,
   augeas
   btrfs-progs
   cryptsetup-luks
@@ -169,7 +166,7 @@
   tar
   util-linux
   xfsprogs
-#endif /* FRUGALWARE */
+)
 
 acl
 attr
@@ -196,11 +193,9 @@ lvm2
 lzop
 mdadm
 module-init-tools
-/*
-Enabling this pulls out 140 extra packages
-into the appliance:
-ocfs2-tools
-*/
+dnl Enabling this pulls out 140 extra packages
+dnl into the appliance:
+dnl ocfs2-tools
 parted
 procps
 procps-ng
@@ -214,17 +209,10 @@ tar
 udev
 util-linux
 util-linux-ng
-#ifndef UBUNTU
-/* on Ubuntu contains a file in /lib64 which conflicts with libc6 that has
- * /lib64 as a symbolic link
- */
 xfsprogs
-#endif
 zerofree
 
-#ifdef VALGRIND_DAEMON
-valgrind
-#endif
+ifelse(VALGRIND_DAEMON,1,valgrind)
 
-/* Define this by doing: ./configure --with-extra-packages="..." */
+dnl Define this by doing: ./configure --with-extra-packages="..."
 EXTRA_PACKAGES
diff --git a/configure.ac b/configure.ac
index 96ae786..3d83d0a 100644
--- a/configure.ac
+++ b/configure.ac
@@ -447,45 +447,10 @@ AM_CONDITIONAL([ENABLE_APPLIANCE],[test "x$ENABLE_APPLIANCE" = "xyes"])
 AC_MSG_RESULT([$ENABLE_APPLIANCE])
 AC_SUBST([ENABLE_APPLIANCE])
 
-dnl Check for supermin >= 4.1.0 or febootstrap >= 3.20.
-AC_CHECK_PROGS([SUPERMIN],
-               [supermin febootstrap],[no])
-AC_PATH_PROGS([SUPERMIN_HELPER],
-              [supermin-helper febootstrap-supermin-helper],[no])
-
-dnl supermin >= 4.1.4 supports compressed cpio images.
-AC_MSG_CHECKING([for supermin-helper version])
-supermin_helper_version=`$SUPERMIN_HELPER --version | awk '{print $2}'`
-AC_MSG_RESULT([$supermin_helper_version])
-AC_MSG_CHECKING([if supermin-helper supports compressed cpio images])
-supermin_helper_version_int=`echo "$supermin_helper_version" | awk -F. '{print $1 * 1000000 + $2 * 1000 + $3}'`
-echo supermin_helper_version_int=$supermin_helper_version_int >&AS_MESSAGE_LOG_FD
-if test $supermin_helper_version_int -ge 4001004; then
-    supermin_helper_compressed_cpio=yes
-else
-    supermin_helper_compressed_cpio=no
-fi
-AC_MSG_RESULT([$supermin_helper_compressed_cpio])
-AM_CONDITIONAL([SUPERMIN_HELPER_COMPRESSED_CPIO],
-               [test "x$supermin_helper_compressed_cpio" = "xyes"])
-
-dnl supermin >= 4.1.5 supports device trees and uses a new style command
-dnl syntax.
-AC_MSG_CHECKING([if supermin-helper supports device trees and new style command syntax])
-if test $supermin_helper_version_int -ge 4001005; then
-    supermin_helper_new_style_syntax=yes
-    AC_DEFINE([SUPERMIN_HELPER_NEW_STYLE_SYNTAX],[1],
-              [Define to 1 if you have supermin-helper >= 4.1.5.])
-else
-    supermin_helper_new_style_syntax=no
-fi
-AC_MSG_RESULT([$supermin_helper_new_style_syntax])
+dnl Check for supermin >= 5.1.0.
+AC_CHECK_PROGS([SUPERMIN],[supermin],[no])
 
 dnl Pass supermin --packager-config option.
-dnl
-dnl Note that in febootstrap >= 3.21 / supermin >= 4.1.0, this option
-dnl is now called --packager-config, although --yum-config can still
-dnl be used as a deprecated alias.
 SUPERMIN_PACKAGER_CONFIG=no
 
 AC_MSG_CHECKING([for --with-supermin-packager-config option])
@@ -496,22 +461,6 @@ AC_ARG_WITH([supermin-packager-config],
      AC_MSG_RESULT([$SUPERMIN_PACKAGER_CONFIG"])],
     [AC_MSG_RESULT([not set])])
 
-AC_MSG_CHECKING([for --with-febootstrap-yum-config option (deprecated)])
-AC_ARG_WITH([febootstrap-yum-config],
-    [AS_HELP_STRING([--with-febootstrap-yum-config=FILE],
-        [pass supermin --packager-config option @<:@default=no@:>@])],
-    [SUPERMIN_PACKAGER_CONFIG="$withval"
-     AC_MSG_RESULT([$SUPERMIN_PACKAGER_CONFIG"])],
-    [AC_MSG_RESULT([not set])])
-
-AC_MSG_CHECKING([for --with-febootstrap-packager-config option (deprecated)])
-AC_ARG_WITH([febootstrap-packager-config],
-    [AS_HELP_STRING([--with-febootstrap-packager-config=FILE],
-        [pass supermin --packager-config option @<:@default=no@:>@])],
-    [SUPERMIN_PACKAGER_CONFIG="$withval"
-     AC_MSG_RESULT([$SUPERMIN_PACKAGER_CONFIG"])],
-    [AC_MSG_RESULT([not set])])
-
 AC_SUBST([SUPERMIN_PACKAGER_CONFIG])
 
 dnl Pass additional supermin options.
@@ -528,21 +477,35 @@ AC_ARG_WITH([supermin-extra-options],
 AC_SUBST([SUPERMIN_EXTRA_OPTIONS])
 
 if test "x$ENABLE_APPLIANCE" = "xyes"; then
+    supermin_major_min=5
+    supermin_minor_min=1
+    supermin_min=$supermin_major_min.$supermin_minor_min
+
     test "x$SUPERMIN" = "xno" &&
-        AC_MSG_ERROR([supermin (formerly called febootstrap) must be installed])
-    dnl febootstrap 2.x did not support the --version parameter
+        AC_MSG_ERROR([supermin >= $supermin_min must be installed])
+
+    AC_MSG_CHECKING([supermin is new enough])
     $SUPERMIN --version >&AS_MESSAGE_LOG_FD 2>&1 ||
-        AC_MSG_ERROR([supermin (formerly called febootstrap) >= 3.20 must be installed, your version is too old])
+        AC_MSG_ERROR([supermin >= $supermin_min must be installed, your version is too old])
+    supermin_major="`$SUPERMIN --version | awk '{print $2}' | awk -F. '{print $1}'`"
+    supermin_minor="`$SUPERMIN --version | awk '{print $2}' | awk -F. '{print $2}'`"
+    AC_MSG_RESULT([$supermin_major.$supermin_minor])
+
+    if test "$supermin_major" -lt "$supermin_major_min" || \
+       ( test "$supermin_major" -eq "$supermin_major_min" && test "$supermin_minor" -lt "$supermin_minor_min" ); then
+        AC_MSG_ERROR([supermin >= $supermin_min must be installed, your version is too old])
+    fi
 fi
 
-AC_DEFINE_UNQUOTED([SUPERMIN_HELPER],["$SUPERMIN_HELPER"],[Name of supermin-helper program.])
+AC_DEFINE_UNQUOTED([SUPERMIN],["$SUPERMIN"],[Name of supermin program])
 
 dnl Which distro?
 dnl
 dnl This used to be Very Important but is now just used to select
 dnl which packages to install in the appliance, since the package
 dnl names vary slightly across distros.  (See
-dnl appliance/packagelist.in and appliance/excludelist.in)
+dnl appliance/packagelist.in, appliance/excludefiles.in,
+dnl appliance/hostfiles.in)
 AC_MSG_CHECKING([which Linux distro for package names])
 DISTRO=REDHAT
 if test -f /etc/debian_version; then
diff --git a/examples/guestfs-performance.pod b/examples/guestfs-performance.pod
index b2a5058..9f47e3c 100644
--- a/examples/guestfs-performance.pod
+++ b/examples/guestfs-performance.pod
@@ -153,7 +153,7 @@ L<libguestfs-make-fixed-appliance(1)>).
 
 In our testing we did not find that using a fixed appliance gave any
 measurable performance benefit, even when the appliance was located in
-memory (ie. on C</dev/shm>).  However there are three points to
+memory (ie. on C</dev/shm>).  However there are two points to
 consider:
 
 =over 4
@@ -166,14 +166,6 @@ times.
 
 =item 2.
 
-By default libguestfs (or rather, L<supermin-helper(1)>)
-searches over the root filesystem to find out if any host files have
-changed and if it needs to rebuild the appliance.  If these files are
-not cached and the root filesystem is on an HDD, then this generates
-lots of seeks.  Using a fixed appliance avoids this.
-
-=item 3.
-
 The appliance is loaded on demand.  A simple test such as:
 
  time guestfish -a /dev/null run
@@ -567,7 +559,6 @@ bit.
 =head1 SEE ALSO
 
 L<supermin(1)>,
-L<supermin-helper(1)>,
 L<guestfish(1)>,
 L<guestfs(3)>,
 L<guestfs-examples(3)>,
diff --git a/fish/guestfish.pod b/fish/guestfish.pod
index 31add50..a0a884d 100644
--- a/fish/guestfish.pod
+++ b/fish/guestfish.pod
@@ -1525,10 +1525,10 @@ the path of qemu/KVM.
 =item SUPERMIN_MODULES
 
 These two environment variables allow the kernel that libguestfs uses
-in the appliance to be selected.  If C<$SUPERMIN_KERNEL> is not
-set, then the most recent host kernel is chosen.  For more information
-about kernel selection, see L<supermin-helper(1)>.  This
-feature is only available in supermin / febootstrap E<ge> 3.8.
+in the appliance to be selected.  If C<$SUPERMIN_KERNEL> is not set,
+then the most recent host kernel is chosen.  For more information
+about kernel selection, see L<supermin(1)>.  This feature is only
+available in supermin / febootstrap E<ge> 3.8.
 
 =item TMPDIR
 
@@ -1629,7 +1629,7 @@ L<virt-win-reg(1)>,
 L<libguestfs-tools.conf(5)>,
 L<display(1)>,
 L<hexedit(1)>,
-L<supermin-helper(1)>.
+L<supermin(1)>.
 
 =head1 AUTHORS
 
diff --git a/src/appliance.c b/src/appliance.c
index 5115ebe..20815ec 100644
--- a/src/appliance.c
+++ b/src/appliance.c
@@ -52,18 +52,8 @@ static int dir_contains_files (const char *dir, ...);
 static int contains_old_style_appliance (guestfs_h *g, const char *path, void *data);
 static int contains_fixed_appliance (guestfs_h *g, const char *path, void *data);
 static int contains_supermin_appliance (guestfs_h *g, const char *path, void *data);
-static char *calculate_supermin_checksum (guestfs_h *g, const char *supermin_path);
-static int check_for_cached_appliance (guestfs_h *g, const char *supermin_path, const char *checksum, uid_t uid, char **kernel, char **dtb, char **initrd, char **appliance);
-static int build_supermin_appliance (guestfs_h *g, const char *supermin_path, const char *checksum, uid_t uid, char **kernel, char **dtb, char **initrd, char **appliance);
-static int hard_link_to_cached_appliance (guestfs_h *g, const char *cachedir, char **kernel, char **dtb, char **initrd, char **appliance);
-static int run_supermin_helper (guestfs_h *g, const char *supermin_path, const char *cachedir);
-
-/* RHBZ#790721: It makes no sense to have multiple threads racing to
- * build the appliance from within a single process, and the code
- * isn't safe for that anyway.  Therefore put a thread lock around
- * appliance building.
- */
-gl_lock_define_initialized (static, building_lock);
+static int build_supermin_appliance (guestfs_h *g, const char *supermin_path, uid_t uid, char **kernel, char **dtb, char **initrd, char **appliance);
+static int run_supermin_build (guestfs_h *g, const char *lockfile, const char *appliancedir, const char *supermin_path);
 
 /* Locate or build the appliance.
  *
@@ -83,60 +73,31 @@ gl_lock_define_initialized (static, building_lock);
  *
  * (1) Look for the first element of g->path which contains a
  * supermin appliance skeleton.  If no element has this, skip
- * straight to step (5).
+ * straight to step (3).
  *
- * (2) Calculate the checksum of this supermin appliance.
+ * (2) Call 'supermin --build' to build the full appliance (if it
+ * needs to be rebuilt).  If this is successful, return the full
+ * appliance.
  *
- * (3) Check whether a cached appliance with the checksum calculated
- * in (2) exists and passes basic security checks.  If so, return
- * this appliance.
- *
- * (4) Try to build the supermin appliance.  If this is successful,
- * return it.
- *
- * (5) Check each element of g->path, looking for a fixed appliance.
+ * (3) Check each element of g->path, looking for a fixed appliance.
  * If one is found, return it.
  *
- * (6) Check each element of g->path, looking for an old-style appliance.
+ * (4) Check each element of g->path, looking for an old-style appliance.
  * If one is found, return it.
  *
  * The supermin appliance cache directory lives in
  * $TMPDIR/.guestfs-$UID/ and consists of up to five files:
  *
- *   $TMPDIR/.guestfs-$UID/checksum       - the checksum
- *   $TMPDIR/.guestfs-$UID/kernel         - the kernel
- *   $TMPDIR/.guestfs-$UID/dtb            - the device tree (on ARM)
- *   $TMPDIR/.guestfs-$UID/initrd         - the supermin initrd
- *   $TMPDIR/.guestfs-$UID/root           - the appliance
- *
- * Since multiple instances of libguestfs with the same UID may be
- * racing to create an appliance, we need to be careful when building
- * and using the appliance.
- *
- * If a cached appliance with checksum exists (step (2) above) then we
- * make a hard link to it with our current PID, so that we have a copy
- * even if the appliance is replaced by another process building an
- * appliance afterwards:
- *
- *   $TMPDIR/.guestfs-$UID/kernel.$PID
- *   $TMPDIR/.guestfs-$UID/dtb.$PID
- *   $TMPDIR/.guestfs-$UID/initrd.$PID
- *   $TMPDIR/.guestfs-$UID/root.$PID
- *
- * A lock is taken on "checksum" while we perform the link.
- *
- * Linked files are deleted by a garbage collection sweep which can be
- * initiated by any libguestfs process with the same UID when the
- * corresponding PID no longer exists.  (This is safe: the parent is
- * always around in guestfs_launch() while qemu is starting up, and
- * after that qemu will either have finished with the files or be
- * holding them open, so we can unlink them).
- *
- * When building a new appliance (step (3)), it is built into randomly
- * named temporary files in the $TMPDIR.  Then a lock is acquired on
- * $TMPDIR/.guestfs-$UID/checksum (this file being created if
- * necessary), the files are renamed into their final location, and
- * the lock is released.
+ *   $TMPDIR/.guestfs-$UID/lock         - the supermin lock file
+ *   $TMPDIR/.guestfs-$UID/appliance.d/kernel  - the kernel
+ *   $TMPDIR/.guestfs-$UID/appliance.d/dtb     - the device tree (on ARM)
+ *   $TMPDIR/.guestfs-$UID/appliance.d/initrd  - the supermin initrd
+ *   $TMPDIR/.guestfs-$UID/appliance.d/root    - the appliance
+ *
+ * Multiple instances of libguestfs with the same UID may be racing to
+ * create an appliance.  However (since supermin >= 5) supermin
+ * provides a --lock flag and atomic update of the appliance.d
+ * subdirectory.
  */
 int
 guestfs___build_appliance (guestfs_h *g,
@@ -145,14 +106,9 @@ guestfs___build_appliance (guestfs_h *g,
                            char **initrd_rtn,
                            char **appliance_rtn)
 {
-  int r;
   char *kernel, *dtb, *initrd, *appliance;
 
-  gl_lock_lock (building_lock);
-  r = build_appliance (g, &kernel, &dtb, &initrd, &appliance);
-  gl_lock_unlock (building_lock);
-
-  if (r == -1)
+  if (build_appliance (g, &kernel, &dtb, &initrd, &appliance) == -1)
     return -1;
 
   /* Don't assign these until we know we're going to succeed, to avoid
@@ -182,24 +138,12 @@ build_appliance (guestfs_h *g,
   if (r == -1)
     return -1;
 
-  if (r == 1) {
-    /* Step (2): calculate checksum. */
-    CLEANUP_FREE char *checksum =
-      calculate_supermin_checksum (g, supermin_path);
-    if (checksum) {
-      /* Step (3): cached appliance exists? */
-      r = check_for_cached_appliance (g, supermin_path, checksum, uid,
-                                      kernel, dtb, initrd, appliance);
-      if (r != 0)
-        return r == 1 ? 0 : -1;
+  if (r == 1)
+    /* Step (2): build supermin appliance. */
+    return build_supermin_appliance (g, supermin_path, uid,
+                                     kernel, dtb, initrd, appliance);
 
-      /* Step (4): build supermin appliance. */
-      return build_supermin_appliance (g, supermin_path, checksum, uid,
-                                       kernel, dtb, initrd, appliance);
-    }
-  }
-
-  /* Step (5). */
+  /* Step (3). */
   r = find_path (g, contains_fixed_appliance, NULL, &path);
   if (r == -1)
     return -1;
@@ -223,7 +167,7 @@ build_appliance (guestfs_h *g,
     return 0;
   }
 
-  /* Step (6). */
+  /* Step (4). */
   r = find_path (g, contains_old_style_appliance, NULL, &path);
   if (r == -1)
     return -1;
@@ -264,82 +208,33 @@ contains_supermin_appliance (guestfs_h *g, const char *path, void *data)
   return dir_contains_files (path, "supermin.d", NULL);
 }
 
-#define MAX_CHECKSUM_LEN 256
-
-static void
-read_checksum (guestfs_h *g, void *checksumv, const char *line, size_t len)
-{
-  char *checksum = checksumv;
-
-  if (len > MAX_CHECKSUM_LEN)
-    return;
-  strcpy (checksum, line);
-}
-
-static int
-process_exists (int pid)
-{
-  if (kill (pid, 0) == 0)
-    return 1;
-
-  if (errno == ESRCH)
-    return 0;
-
-  return -1;
-}
-
-/* Garbage collect appliance hard links.  Files that match
- * (kernel|dtb|initrd|root).$PID where the corresponding PID doesn't
- * exist are deleted.  Note that errors in this function don't matter.
- * There may also be other libguestfs processes racing to do the same
- * thing here.
+/* Build supermin appliance from supermin_path to $TMPDIR/.guestfs-$UID.
+ *
+ * Returns:
+ * 0 = built
+ * -1 = error (aborts launch)
  */
-static void
-garbage_collect_appliances (const char *cachedir)
-{
-  DIR *dir;
-  struct dirent *d;
-  int pid;
-
-  dir = opendir (cachedir);
-  if (dir == NULL)
-    return;
-
-  while ((d = readdir (dir)) != NULL) {
-    if (sscanf (d->d_name, "kernel.%d", &pid) == 1 &&
-        process_exists (pid) == 0)
-      unlinkat (dirfd (dir), d->d_name, 0);
-    else if (sscanf (d->d_name, "dtb.%d", &pid) == 1 &&
-             process_exists (pid) == 0)
-      unlinkat (dirfd (dir), d->d_name, 0);
-    else if (sscanf (d->d_name, "initrd.%d", &pid) == 1 &&
-             process_exists (pid) == 0)
-      unlinkat (dirfd (dir), d->d_name, 0);
-    else if (sscanf (d->d_name, "root.%d", &pid) == 1 &&
-             process_exists (pid) == 0)
-      unlinkat (dirfd (dir), d->d_name, 0);
-  }
-
-  closedir (dir);
-}
-
 static int
-check_for_cached_appliance (guestfs_h *g,
-                            const char *supermin_path, const char *checksum,
-                            uid_t uid,
-                            char **kernel, char **dtb,
-			    char **initrd, char **appliance)
+build_supermin_appliance (guestfs_h *g,
+                          const char *supermin_path,
+                          uid_t uid,
+                          char **kernel, char **dtb,
+			  char **initrd, char **appliance)
 {
   CLEANUP_FREE char *tmpdir = guestfs_get_cachedir (g);
+  struct stat statbuf;
+  size_t len;
 
   /* len must be longer than the length of any pathname we can
    * generate in this function.
    */
-  size_t len = strlen (tmpdir) + 128;
+  len = strlen (tmpdir) + 128;
   char cachedir[len];
   snprintf (cachedir, len, "%s/.guestfs-%d", tmpdir, uid);
-  char filename[len];
-  snprintf (filename, len, "%s/checksum", cachedir);
+  char lockfile[len];
+  snprintf (lockfile, len, "%s/lock", cachedir);
+  char appliancedir[len];
+  snprintf (appliancedir, len, "%s/appliance.d", cachedir);
 
   ignore_value (mkdir (cachedir, 0755));
   ignore_value (chmod (cachedir, 0755)); /* RHBZ#921292 */
@@ -347,17 +242,16 @@ check_for_cached_appliance (guestfs_h *g,
   /* See if the cache directory exists and passes some simple checks
    * to make sure it has not been tampered with.
    */
-  struct stat statbuf;
   if (lstat (cachedir, &statbuf) == -1)
     return 0;
   if (statbuf.st_uid != uid) {
     error (g, _("security: cached appliance %s is not owned by UID %d"),
-           filename, uid);
+           cachedir, uid);
     return -1;
   }
   if (!S_ISDIR (statbuf.st_mode)) {
     error (g, _("security: cached appliance %s is not a directory (mode %o)"),
-           filename, statbuf.st_mode);
+           cachedir, statbuf.st_mode);
     return -1;
   }
   if ((statbuf.st_mode & 0022) != 0) {
@@ -366,302 +260,43 @@ check_for_cached_appliance (guestfs_h *g,
     return -1;
   }
 
-  (void) utime (cachedir, NULL);
-
-  garbage_collect_appliances (cachedir);
-
-  /* Try to open and acquire a lock on the checksum file. */
-  int fd = open (filename, O_RDONLY|O_CLOEXEC);
-  if (fd == -1)
-    return 0;
-#ifdef HAVE_FUTIMENS
-  (void) futimens (fd, NULL);
-#else
-  (void) futimes (fd, NULL);
-#endif
-  struct flock fl;
-  fl.l_type = F_RDLCK;
-  fl.l_whence = SEEK_SET;
-  fl.l_start = 0;
-  fl.l_len = 1;
- again:
-  if (fcntl (fd, F_SETLKW, &fl) == -1) {
-    if (errno == EINTR)
-      goto again;
-    perrorf (g, "fcntl: F_SETLKW: %s", filename);
-    close (fd);
-    return -1;
-  }
-
-  /* Read the checksum file. */
-  size_t clen = strlen (checksum);
-  char checksum_on_disk[clen];
-  ssize_t rr = read (fd, checksum_on_disk, clen);
-  if (rr == -1) {
-    perrorf (g, "read: %s", filename);
-    close (fd);
-    return -1;
-  }
-  if ((size_t) rr != clen) {
-    close (fd);
-    return 0;
-  }
-
-  if (memcmp (checksum, checksum_on_disk, clen) != 0) {
-    close (fd);
-    return 0;
-  }
-
-  /* At this point, cachedir exists, and checksum matches, and we have
-   * a read lock on the checksum file.  Make hard links to the files.
-   */
-  if (hard_link_to_cached_appliance (g, cachedir,
-                                     kernel, dtb, initrd, appliance) == -1) {
-    close (fd);
-    return -1;
-  }
-
-  /* Releases the lock on checksum. */
-  if (close (fd) == -1) {
-    perrorf (g, "close");
-    /* Allocated in hard_link_to_cached_appliance above, must be
-     * freed along this error path.
-     */
-    free (*kernel);
-    free (*dtb);
-    free (*initrd);
-    free (*appliance);
-    return -1;
-  }
-
-  /* Exists! */
-  return 1;
-}
-
-/* Build supermin appliance from supermin_path to $TMPDIR/.guestfs-$UID.
- *
- * Returns:
- * 0 = built
- * -1 = error (aborts launch)
- */
-static int
-build_supermin_appliance (guestfs_h *g,
-                          const char *supermin_path, const char *checksum,
-                          uid_t uid,
-                          char **kernel, char **dtb,
-			  char **initrd, char **appliance)
-{
-  CLEANUP_FREE char *tmpdir = guestfs_get_cachedir (g);
-  size_t len;
-
+  (void) utimes (cachedir, NULL);
   if (g->verbose)
     guestfs___print_timestamped_message (g, "begin building supermin appliance");
 
-  /* len must be longer than the length of any pathname we can
-   * generate in this function.
-   */
-  len = strlen (tmpdir) + 128;
-
-  /* Build the appliance into a temporary directory. */
-  char tmpcd[len];
-  snprintf (tmpcd, len, "%s/guestfs.XXXXXX", tmpdir);
-
-  if (mkdtemp (tmpcd) == NULL) {
-    perrorf (g, "mkdtemp");
-    return -1;
-  }
-
+  /* Build the appliance if it needs to be built. */
   if (g->verbose)
-    guestfs___print_timestamped_message (g, "run supermin-helper");
+    guestfs___print_timestamped_message (g, "run supermin");
 
-  int r = run_supermin_helper (g, supermin_path, tmpcd);
-  if (r == -1) {
-    guestfs___recursive_remove_dir (g, tmpcd);
+  if (run_supermin_build (g, lockfile, appliancedir, supermin_path) == -1)
     return -1;
-  }
 
   if (g->verbose)
     guestfs___print_timestamped_message (g, "finished building supermin appliance");
 
-  char cachedir[len];
-  snprintf (cachedir, len, "%s/.guestfs-%d", tmpdir, uid);
-  char filename[len];
-  char filename2[len];
-  snprintf (filename, len, "%s/checksum", cachedir);
-
-  /* Open and acquire write lock on checksum file.  The file might
-   * not exist, in which case we want to create it.
-   */
-  int fd = open (filename, O_WRONLY|O_CREAT|O_NOCTTY|O_CLOEXEC, 0644);
-  if (fd == -1) {
-    perrorf (g, "open: %s", filename);
-    guestfs___recursive_remove_dir (g, tmpcd);
-    return -1;
-  }
-  struct flock fl;
-  fl.l_type = F_WRLCK;
-  fl.l_whence = SEEK_SET;
-  fl.l_start = 0;
-  fl.l_len = 1;
- again:
-  if (fcntl (fd, F_SETLKW, &fl) == -1) {
-    if (errno == EINTR)
-      goto again;
-    perrorf (g, "fcntl: F_SETLKW: %s", filename);
-    close (fd);
-    guestfs___recursive_remove_dir (g, tmpcd);
-    return -1;
-  }
-
-  /* At this point we have acquired a write lock on the checksum
-   * file so we go ahead and replace it with the new checksum, and
-   * rename in appliance files into this directory.
-   */
-  size_t clen = strlen (checksum);
-  if (ftruncate (fd, clen) == -1) {
-    perrorf (g, "ftruncate: %s", filename);
-    close (fd);
-    guestfs___recursive_remove_dir (g, tmpcd);
-    return -1;
-  }
-
-  ssize_t rr = write (fd, checksum, clen);
-  if (rr == -1) {
-    perrorf (g, "write: %s", filename);
-    close (fd);
-    guestfs___recursive_remove_dir (g, tmpcd);
-    return -1;
-  }
-  if ((size_t) rr != clen) {
-    error (g, "partial write: %s", filename);
-    close (fd);
-    guestfs___recursive_remove_dir (g, tmpcd);
-    return -1;
-  }
-
-  snprintf (filename, len, "%s/kernel", tmpcd);
-  snprintf (filename2, len, "%s/kernel", cachedir);
-  unlink (filename2);
-  if (rename (filename, filename2) == -1) {
-    perrorf (g, "rename: %s %s", filename, filename2);
-    close (fd);
-    guestfs___recursive_remove_dir (g, tmpcd);
-    return -1;
-  }
-
-#ifdef DTB_WILDCARD
-  snprintf (filename, len, "%s/dtb", tmpcd);
-  snprintf (filename2, len, "%s/dtb", cachedir);
-  unlink (filename2);
-  if (rename (filename, filename2) == -1) {
-    perrorf (g, "rename: %s %s", filename, filename2);
-    close (fd);
-    guestfs___recursive_remove_dir (g, tmpcd);
-    return -1;
-  }
-#endif
-
-  snprintf (filename, len, "%s/initrd", tmpcd);
-  snprintf (filename2, len, "%s/initrd", cachedir);
-  unlink (filename2);
-  if (rename (filename, filename2) == -1) {
-    perrorf (g, "rename: %s %s", filename, filename2);
-    close (fd);
-    guestfs___recursive_remove_dir (g, tmpcd);
-    return -1;
-  }
-
-  snprintf (filename, len, "%s/root", tmpcd);
-  snprintf (filename2, len, "%s/root", cachedir);
-  unlink (filename2);
-  if (rename (filename, filename2) == -1) {
-    perrorf (g, "rename: %s %s", filename, filename2);
-    close (fd);
-    guestfs___recursive_remove_dir (g, tmpcd);
-    return -1;
-  }
-
-  guestfs___recursive_remove_dir (g, tmpcd);
-
-  /* Now finish off by linking to the cached appliance and returning it. */
-  if (hard_link_to_cached_appliance (g, cachedir,
-                                     kernel, dtb, initrd, appliance) == -1) {
-    close (fd);
-    return -1;
-  }
-
-  /* Releases the lock on checksum. */
-  if (close (fd) == -1) {
-    perrorf (g, "close");
-    /* Allocated in hard_link_to_cached_appliance above, must be
-     * freed along this error path.
-     */
-    free (*kernel);
-    free (*dtb);
-    free (*initrd);
-    free (*appliance);
-    return -1;
-  }
-
-  return 0;
-}
-
-/* NB: lock on checksum file must be held when this is called. */
-static int
-hard_link_to_cached_appliance (guestfs_h *g,
-                               const char *cachedir,
-                               char **kernel, char **dtb,
-			       char **initrd, char **appliance)
-{
-  pid_t pid = getpid ();
-  size_t len = strlen (cachedir) + 32;
-
+  /* Return the appliance filenames. */
   *kernel = safe_malloc (g, len);
+#ifdef DTB_WILDCARD
   *dtb = safe_malloc (g, len);
+#else
+  *dtb = NULL;
+#endif
   *initrd = safe_malloc (g, len);
   *appliance = safe_malloc (g, len);
-  snprintf (*kernel, len, "%s/kernel.%d", cachedir, pid);
-  snprintf (*dtb, len, "%s/dtb.%d", cachedir, pid);
-  snprintf (*initrd, len, "%s/initrd.%d", cachedir, pid);
-  snprintf (*appliance, len, "%s/root.%d", cachedir, pid);
+  snprintf (*kernel, len, "%s/kernel", appliancedir);
+#ifdef DTB_WILDCARD
+  snprintf (*dtb, len, "%s/dtb", appliancedir);
+#endif
+  snprintf (*initrd, len, "%s/initrd", appliancedir);
+  snprintf (*appliance, len, "%s/root", appliancedir);
+
+  /* Touch the files so they don't get deleted (as they are in /var/tmp). */
+  (void) utimes (*kernel, NULL);
+#ifdef DTB_WILDCARD
+  (void) utimes (*dtb, NULL);
+#endif
+  (void) utimes (*initrd, NULL);
 
-  char filename[len];
-  snprintf (filename, len, "%s/kernel", cachedir);
-  (void) unlink (*kernel);
-  if (link (filename, *kernel) == -1) {
-    perrorf (g, "link: %s %s", filename, *kernel);
-    goto error;
-  }
-  (void) utimes (filename, NULL);
-
-  snprintf (filename, len, "%s/dtb", cachedir);
-  (void) unlink (*dtb);
-  if (link (filename, *dtb) == -1) {
-    if (errno == ENOENT) {
-      /* dtb doesn't exist -- this is OK */
-      free (*dtb);
-      *dtb = NULL;
-    } else {
-      perrorf (g, "link: %s %s", filename, *kernel);
-      goto error;
-    }
-  }
-  (void) utimes (filename, NULL);
-
-  snprintf (filename, len, "%s/initrd", cachedir);
-  (void) unlink (*initrd);
-  if (link (filename, *initrd) == -1) {
-    perrorf (g, "link: %s %s", filename, *initrd);
-    goto error;
-  }
-  (void) utime (filename, NULL);
-
-  snprintf (filename, len, "%s/root", cachedir);
-  (void) unlink (*appliance);
-  if (link (filename, *appliance) == -1) {
-    perrorf (g, "link: %s %s", filename, *appliance);
-    goto error;
-  }
   /* Checking backend != "uml" is a big hack.  UML encodes the mtime
    * of the original backing file (in this case, the appliance) in the
    * COW file, and checks it when adding it to the VM.  If there are
@@ -675,211 +310,69 @@ hard_link_to_cached_appliance (guestfs_h *g,
    * XXX
    */
   if (STRNEQ (g->backend, "uml"))
-    (void) utime (filename, NULL);
+    (void) utimes (*appliance, NULL);
 
   return 0;
-
- error:
-  free (*kernel);
-  free (*dtb);
-  free (*initrd);
-  free (*appliance);
-  return -1;
 }
 
-/* Run supermin-helper and tell it to generate the
+/* Run supermin --build and tell it to generate the
  * appliance.
  */
-
-#ifdef SUPERMIN_HELPER_NEW_STYLE_SYNTAX
-
-/* supermin_path is a path which is known to contain a supermin
- * appliance.  Using supermin-helper -f checksum calculate
- * the checksum so we can see if it is cached.
- */
-static char *
-calculate_supermin_checksum (guestfs_h *g, const char *supermin_path)
-{
-  size_t len;
-  CLEANUP_CMD_CLOSE struct command *cmd = guestfs___new_command (g);
-  int pass_u_g_args = getuid () != geteuid () || getgid () != getegid ();
-  char checksum[MAX_CHECKSUM_LEN + 1] = { 0 };
-
-  guestfs___cmd_add_arg (cmd, SUPERMIN_HELPER);
-  if (g->verbose)
-    guestfs___cmd_add_arg (cmd, "--verbose");
-  if (pass_u_g_args) {
-    guestfs___cmd_add_arg (cmd, "-u");
-    guestfs___cmd_add_arg_format (cmd, "%d", geteuid ());
-    guestfs___cmd_add_arg (cmd, "-g");
-    guestfs___cmd_add_arg_format (cmd, "%d", getegid ());
-  }
-  guestfs___cmd_add_arg (cmd, "-f");
-  guestfs___cmd_add_arg (cmd, "checksum");
-  guestfs___cmd_add_arg (cmd, "--host-cpu");
-  guestfs___cmd_add_arg (cmd, host_cpu);
-  guestfs___cmd_add_arg_format (cmd, "%s/supermin.d", supermin_path);
-  guestfs___cmd_set_stdout_callback (cmd, read_checksum, checksum, 0);
-
-  /* Errors here are non-fatal, so we don't need to call error(). */
-  if (guestfs___cmd_run (cmd) == -1)
-    return NULL;
-
-  debug (g, "checksum of existing appliance: %s", checksum);
-
-  len = strlen (checksum);
-  if (len < 16) {               /* sanity check */
-    warning (g, "supermin-helper -f checksum returned a short string");
-    return NULL;
-  }
-
-  return safe_strndup (g, checksum, len);
-}
-
 static int
-run_supermin_helper (guestfs_h *g, const char *supermin_path,
-                     const char *cachedir)
+run_supermin_build (guestfs_h *g,
+                    const char *lockfile,
+                    const char *appliancedir,
+                    const char *supermin_path)
 {
   CLEANUP_CMD_CLOSE struct command *cmd = guestfs___new_command (g);
   int r;
+#if 0                           /* not supported in supermin 5 yet XXX */
   uid_t uid = getuid ();
   uid_t euid = geteuid ();
   gid_t gid = getgid ();
   gid_t egid = getegid ();
   int pass_u_g_args = uid != euid || gid != egid;
+#endif
 
-  guestfs___cmd_add_arg (cmd, SUPERMIN_HELPER);
+  guestfs___cmd_add_arg (cmd, SUPERMIN);
+  guestfs___cmd_add_arg (cmd, "--build");
   if (g->verbose)
     guestfs___cmd_add_arg (cmd, "--verbose");
+#if 0
   if (pass_u_g_args) {
     guestfs___cmd_add_arg (cmd, "-u");
     guestfs___cmd_add_arg_format (cmd, "%d", euid);
     guestfs___cmd_add_arg (cmd, "-g");
     guestfs___cmd_add_arg_format (cmd, "%d", egid);
   }
+#endif
   guestfs___cmd_add_arg (cmd, "--copy-kernel");
   guestfs___cmd_add_arg (cmd, "-f");
   guestfs___cmd_add_arg (cmd, "ext2");
   guestfs___cmd_add_arg (cmd, "--host-cpu");
   guestfs___cmd_add_arg (cmd, host_cpu);
+  guestfs___cmd_add_arg (cmd, "--if-newer");
+  guestfs___cmd_add_arg (cmd, "--lock");
+  guestfs___cmd_add_arg (cmd, lockfile);
 #ifdef DTB_WILDCARD
   guestfs___cmd_add_arg (cmd, "--dtb");
   guestfs___cmd_add_arg (cmd, DTB_WILDCARD);
 #endif
   guestfs___cmd_add_arg_format (cmd, "%s/supermin.d", supermin_path);
-  guestfs___cmd_add_arg (cmd, "--output-kernel");
-  guestfs___cmd_add_arg_format (cmd, "%s/kernel", cachedir);
-#ifdef DTB_WILDCARD
-  guestfs___cmd_add_arg (cmd, "--output-dtb");
-  guestfs___cmd_add_arg_format (cmd, "%s/dtb", cachedir);
-#endif
-  guestfs___cmd_add_arg (cmd, "--output-initrd");
-  guestfs___cmd_add_arg_format (cmd, "%s/initrd", cachedir);
-  guestfs___cmd_add_arg (cmd, "--output-appliance");
-  guestfs___cmd_add_arg_format (cmd, "%s/root", cachedir);
+  guestfs___cmd_add_arg (cmd, "-o");
+  guestfs___cmd_add_arg (cmd, appliancedir);
 
   r = guestfs___cmd_run (cmd);
   if (r == -1)
     return -1;
   if (!WIFEXITED (r) || WEXITSTATUS (r) != 0) {
-    guestfs___external_command_failed (g, r, SUPERMIN_HELPER, NULL);
+    guestfs___external_command_failed (g, r, SUPERMIN, NULL);
     return -1;
   }
 
   return 0;
 }
 
-#else /* ! SUPERMIN_HELPER_NEW_STYLE_SYNTAX */
-
-#ifdef DTB_WILDCARD
-#error "This architecture has device trees, so requires supermin-helper >= 4.1.5"
-#endif
-
-/* supermin_path is a path which is known to contain a supermin
- * appliance.  Using supermin-helper -f checksum calculate
- * the checksum so we can see if it is cached.
- */
-static char *
-calculate_supermin_checksum (guestfs_h *g, const char *supermin_path)
-{
-  size_t len;
-  CLEANUP_CMD_CLOSE struct command *cmd = guestfs___new_command (g);
-  int pass_u_g_args = getuid () != geteuid () || getgid () != getegid ();
-  char checksum[MAX_CHECKSUM_LEN + 1] = { 0 };
-
-  guestfs___cmd_add_arg (cmd, SUPERMIN_HELPER);
-  if (g->verbose)
-    guestfs___cmd_add_arg (cmd, "--verbose");
-  if (pass_u_g_args) {
-    guestfs___cmd_add_arg (cmd, "-u");
-    guestfs___cmd_add_arg_format (cmd, "%d", geteuid ());
-    guestfs___cmd_add_arg (cmd, "-g");
-    guestfs___cmd_add_arg_format (cmd, "%d", getegid ());
-  }
-  guestfs___cmd_add_arg (cmd, "-f");
-  guestfs___cmd_add_arg (cmd, "checksum");
-  guestfs___cmd_add_arg_format (cmd, "%s/supermin.d", supermin_path);
-  guestfs___cmd_add_arg (cmd, host_cpu);
-  guestfs___cmd_set_stdout_callback (cmd, read_checksum, checksum, 0);
-
-  /* Errors here are non-fatal, so we don't need to call error(). */
-  if (guestfs___cmd_run (cmd) == -1)
-    return NULL;
-
-  debug (g, "checksum of existing appliance: %s", checksum);
-
-  len = strlen (checksum);
-  if (len < 16) {               /* sanity check */
-    warning (g, "supermin-helper -f checksum returned a short string");
-    return NULL;
-  }
-
-  return safe_strndup (g, checksum, len);
-}
-
-static int
-run_supermin_helper (guestfs_h *g, const char *supermin_path,
-                     const char *cachedir)
-{
-  CLEANUP_CMD_CLOSE struct command *cmd = guestfs___new_command (g);
-  int r;
-  uid_t uid = getuid ();
-  uid_t euid = geteuid ();
-  gid_t gid = getgid ();
-  gid_t egid = getegid ();
-  int pass_u_g_args = uid != euid || gid != egid;
-
-  guestfs___cmd_add_arg (cmd, SUPERMIN_HELPER);
-  if (g->verbose)
-    guestfs___cmd_add_arg (cmd, "--verbose");
-  if (pass_u_g_args) {
-    guestfs___cmd_add_arg (cmd, "-u");
-    guestfs___cmd_add_arg_format (cmd, "%d", euid);
-    guestfs___cmd_add_arg (cmd, "-g");
-    guestfs___cmd_add_arg_format (cmd, "%d", egid);
-  }
-  guestfs___cmd_add_arg (cmd, "--copy-kernel");
-  guestfs___cmd_add_arg (cmd, "-f");
-  guestfs___cmd_add_arg (cmd, "ext2");
-  guestfs___cmd_add_arg_format (cmd, "%s/supermin.d", supermin_path);
-  guestfs___cmd_add_arg (cmd, host_cpu);
-  guestfs___cmd_add_arg_format (cmd, "%s/kernel", cachedir);
-  guestfs___cmd_add_arg_format (cmd, "%s/initrd", cachedir);
-  guestfs___cmd_add_arg_format (cmd, "%s/root", cachedir);
-
-  r = guestfs___cmd_run (cmd);
-  if (r == -1)
-    return -1;
-  if (!WIFEXITED (r) || WEXITSTATUS (r) != 0) {
-    guestfs___external_command_failed (g, r, SUPERMIN_HELPER, NULL);
-    return -1;
-  }
-
-  return 0;
-}
-
-#endif /* ! SUPERMIN_HELPER_NEW_STYLE_SYNTAX */
-
 /* Search elements of g->path, returning the first path element which
  * matches the predicate function 'pred'.
  *
diff --git a/src/guestfs.pod b/src/guestfs.pod
index 4d95a2f..1e6ec3f 100644
--- a/src/guestfs.pod
+++ b/src/guestfs.pod
@@ -3477,15 +3477,14 @@ debugging (set the environment variable C<LIBGUESTFS_DEBUG=1>).
 
 =item Create the appliance
 
-C<supermin-helper> is invoked to create the kernel, a
-small initrd and the appliance.
+C<supermin --build> is invoked to create the kernel, a small initrd
+and the appliance.
 
 The appliance is cached in C</var/tmp/.guestfs-E<lt>UIDE<gt>> (or in
 another directory if C<LIBGUESTFS_CACHEDIR> or C<TMPDIR> are set).
 
 For a complete description of how the appliance is created and cached,
-read the L<supermin(1)> and L<supermin-helper(1)> man
-pages.
+read the L<supermin(1)> man page.
 
 =item Start qemu and boot the kernel
 
@@ -3493,12 +3492,12 @@ qemu is invoked to boot the kernel.
 
 =item Run the initrd
 
-C<supermin-helper> builds a small initrd.  The initrd is
-not the appliance.  The purpose of the initrd is to load enough kernel
-modules in order that the appliance itself can be mounted and started.
+C<supermin --build> builds a small initrd.  The initrd is not the
+appliance.  The purpose of the initrd is to load enough kernel modules
+in order that the appliance itself can be mounted and started.
 
 The initrd is a cpio archive called
-C</var/tmp/.guestfs-E<lt>UIDE<gt>/initrd>.
+C</var/tmp/.guestfs-E<lt>UIDE<gt>/appliance.d/initrd>.
 
 When the initrd has started you will see messages showing that kernel
 modules are being loaded, similar to this:
@@ -3512,7 +3511,8 @@ modules are being loaded, similar to this:
 
 The appliance is a sparse file containing an ext2 filesystem which
 contains a familiar (although reduced in size) Linux operating system.
-It would normally be called C</var/tmp/.guestfs-E<lt>UIDE<gt>/root>.
+It would normally be called
+C</var/tmp/.guestfs-E<lt>UIDE<gt>/appliance.d/root>.
 
 The regular disks being inspected by libguestfs are the first
 devices exposed by qemu (eg. as C</dev/vda>).
@@ -4706,7 +4706,7 @@ environment which tends to break everything.
 These two environment variables allow the kernel that libguestfs uses
 in the appliance to be selected.  If C<$SUPERMIN_KERNEL> is not
 set, then the most recent host kernel is chosen.  For more information
-about kernel selection, see L<supermin-helper(1)>.  This
+about kernel selection, see L<supermin(1)>.  This
 feature is only available in supermin / febootstrap E<ge> 3.8.
 
 =item TMPDIR
@@ -4758,7 +4758,6 @@ L<guestfs-testing(1)>,
 L<libguestfs-test-tool(1)>,
 L<libguestfs-make-fixed-appliance(1)>,
 L<supermin(1)>,
-L<supermin-helper(1)>,
 L<qemu(1)>,
 L<hivex(3)>,
 L<stap(1)>,
diff --git a/test-tool/libguestfs-test-tool.pod b/test-tool/libguestfs-test-tool.pod
index c428468..2ac299b 100644
--- a/test-tool/libguestfs-test-tool.pod
+++ b/test-tool/libguestfs-test-tool.pod
@@ -87,8 +87,7 @@ variables C<SUPERMIN_KERNEL> and/or C<SUPERMIN_MODULES>
 (C<FEBOOTSTRAP_KERNEL> and C<FEBOOTSTRAP_MODULES> if still using the
 old febootstrap 3.21 program).
 
-Refer to L<supermin-helper(1)/ENVIRONMENT VARIABLES>
-for further information.
+Refer to L<supermin(1)/ENVIRONMENT VARIABLES> for further information.
 
 =head1 TRYING OUT A DIFFERENT VERSION OF LIBVIRT
 
-- 
1.8.5.3




More information about the Libguestfs mailing list