appliance: Use supermin >= 5.

This requires the new version of supermin (5.1.0).
This commit is contained in:
Richard W.M. Jones
2014-02-25 16:08:13 +00:00
parent d353b4a3c8
commit b13c22668d
16 changed files with 284 additions and 876 deletions

6
.gitignore vendored
View File

@@ -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

5
README
View File

@@ -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. |

View File

@@ -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

29
appliance/excludefiles.in Normal file
View File

@@ -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/*

View File

@@ -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

13
appliance/hostfiles.in Normal file
View File

@@ -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

View File

@@ -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"

View File

@@ -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/>.

View File

@@ -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@SUPERMIN_PACKAGER_CONFIG@" != "xno" ]; then
extra="--yum-config @SUPERMIN_PACKAGER_CONFIG@"
extra="--packager-config @SUPERMIN_PACKAGER_CONFIG@"
fi
if [ "x@SUPERMIN_EXTRA_OPTIONS@" != "xno" ]; then
extra="$extra @SUPERMIN_EXTRA_OPTIONS@"
fi
echo @SUPERMIN@ -v -o supermin.d --names $(< packagelist ) $excludes $extra
@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
@SUPERMIN@ --prepare -v -o supermin.d $(< packagelist ) $extra

View File

@@ -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

View File

@@ -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

View File

@@ -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)>,

View File

@@ -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

View File

@@ -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
* $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
*
* 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.
* 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,
if (r == 1)
/* Step (2): build supermin appliance. */
return build_supermin_appliance (g, supermin_path, uid,
kernel, dtb, initrd, appliance);
if (r != 0)
return r == 1 ? 0 : -1;
/* 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,180 +208,6 @@ 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.
*/
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)
{
CLEANUP_FREE char *tmpdir = guestfs_get_cachedir (g);
/* len must be longer than the length of any pathname we can
* generate in this function.
*/
size_t len = strlen (tmpdir) + 128;
char cachedir[len];
snprintf (cachedir, len, "%s/.guestfs-%d", tmpdir, uid);
char filename[len];
snprintf (filename, len, "%s/checksum", cachedir);
ignore_value (mkdir (cachedir, 0755));
ignore_value (chmod (cachedir, 0755)); /* RHBZ#921292 */
/* 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);
return -1;
}
if (!S_ISDIR (statbuf.st_mode)) {
error (g, _("security: cached appliance %s is not a directory (mode %o)"),
filename, statbuf.st_mode);
return -1;
}
if ((statbuf.st_mode & 0022) != 0) {
error (g, _("security: cached appliance %s is writable by group or other (mode %o)"),
cachedir, statbuf.st_mode);
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:
@@ -446,222 +216,87 @@ check_for_cached_appliance (guestfs_h *g,
*/
static int
build_supermin_appliance (guestfs_h *g,
const char *supermin_path, const char *checksum,
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;
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;
char cachedir[len];
snprintf (cachedir, len, "%s/.guestfs-%d", tmpdir, uid);
char lockfile[len];
snprintf (lockfile, len, "%s/lock", cachedir);
char appliancedir[len];
snprintf (appliancedir, len, "%s/appliance.d", cachedir);
/* Build the appliance into a temporary directory. */
char tmpcd[len];
snprintf (tmpcd, len, "%s/guestfs.XXXXXX", tmpdir);
ignore_value (mkdir (cachedir, 0755));
ignore_value (chmod (cachedir, 0755)); /* RHBZ#921292 */
if (mkdtemp (tmpcd) == NULL) {
perrorf (g, "mkdtemp");
/* See if the cache directory exists and passes some simple checks
* to make sure it has not been tampered with.
*/
if (lstat (cachedir, &statbuf) == -1)
return 0;
if (statbuf.st_uid != uid) {
error (g, _("security: cached appliance %s is not owned by UID %d"),
cachedir, uid);
return -1;
}
if (!S_ISDIR (statbuf.st_mode)) {
error (g, _("security: cached appliance %s is not a directory (mode %o)"),
cachedir, statbuf.st_mode);
return -1;
}
if ((statbuf.st_mode & 0022) != 0) {
error (g, _("security: cached appliance %s is writable by group or other (mode %o)"),
cachedir, statbuf.st_mode);
return -1;
}
(void) utimes (cachedir, NULL);
if (g->verbose)
guestfs___print_timestamped_message (g, "run supermin-helper");
guestfs___print_timestamped_message (g, "begin building supermin appliance");
int r = run_supermin_helper (g, supermin_path, tmpcd);
if (r == -1) {
guestfs___recursive_remove_dir (g, tmpcd);
/* Build the appliance if it needs to be built. */
if (g->verbose)
guestfs___print_timestamped_message (g, "run supermin");
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);
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);
/* 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);
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'.
*

View File

@@ -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)>,

View File

@@ -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