From: Richard Jones Date: Thu, 13 May 2010 16:07:17 +0000 (+0100) Subject: Unify supermin appliance building using febootstrap 2.7 X-Git-Tag: 1.3.12~1 X-Git-Url: http://git.annexia.org/?a=commitdiff_plain;h=8700a55d52f25f60f01ef1b67cff6d5c0071700b;p=libguestfs.git Unify supermin appliance building using febootstrap 2.7 --- diff --git a/.gitignore b/.gitignore index e92f04b..9f487c6 100644 --- a/.gitignore +++ b/.gitignore @@ -9,16 +9,12 @@ appliance/debian/root/ appliance/debian/vmlinuz-* appliance/debian/debirf.conf appliance/initramfs.*.img -appliance/initramfs.*.supermin.hostfiles appliance/kmod.whitelist -appliance/libguestfs-supermin-helper -appliance/libguestfs-supermin-helper.old appliance/make.sh appliance/packagelist appliance/stamp-debirf-modules -appliance/supermin.incfiles -appliance/supermin-make.sh -appliance/supermin-split.sh +appliance/stamp-supermin +appliance/supermin.d appliance/update.sh appliance/vmlinuz.* autom4te.cache diff --git a/README b/README index 4919f62..ea1da1f 100644 --- a/README +++ b/README @@ -40,7 +40,7 @@ Requirements - recent QEMU >= 0.10 with vmchannel support http://lists.gnu.org/archive/html/qemu-devel/2009-02/msg01042.html -- febootstrap >= 2.3 +- febootstrap >= 2.7 - fakeroot diff --git a/appliance/Makefile.am b/appliance/Makefile.am index 27a23d3..157c875 100644 --- a/appliance/Makefile.am +++ b/appliance/Makefile.am @@ -32,35 +32,24 @@ EXTRA_DIST = \ # $(libdir) is best. When we build cross-architecture filesystems we # should probably move them to $(datadir). fsdir = $(libdir)/guestfs -fs_DATA = $(APPLIANCE_FILES) +superminfsdir = $(libdir)/guestfs/supermin.d # These are the resulting output files from the whole process: # VMLINUZ kernel for the full appliance # INITRAMFSIMG initramfs (ie. root fs) for the full appliance -# For details of the supermin appliance, read the README file: -# SUPERMINIMG initramfs (ie. partial root fs) for the supermin appliance -# SUPERMINFILES list of missing files (the ones we will pull out of the -# host filesystem at runtime) in the supermin appliance -APPLIANCE_FILES = $(INITRAMFSIMG) $(VMLINUZ) +# For details of the supermin appliance, read the README file. +fs_DATA = $(INITRAMFSIMG) $(VMLINUZ) if SUPERMIN -APPLIANCE_FILES += $(SUPERMINIMG) $(SUPERMINFILES) kmod.whitelist -bin_PROGRAMS = libguestfs-supermin-helper -libguestfs_supermin_helper_SOURCES = \ - libguestfs-supermin-helper.c ../gnulib/lib/xalloc-die.c -libguestfs_supermin_helper_CFLAGS = \ - -I$(srcdir)/../gnulib/lib -I../gnulib/lib \ - $(WARN_CFLAGS) $(WERROR_CFLAGS) -libguestfs_supermin_helper_LDADD = \ - ../gnulib/lib/libgnu.la +fs_DATA += kmod.whitelist +superminfs_DATA = \ + supermin.d/base.img \ + supermin.d/daemon.img \ + supermin.d/hostfiles endif # Don't change these names - they must be the same as in '*.sh' scripts. INITRAMFSIMG = initramfs.$(REPO).$(host_cpu).img VMLINUZ = vmlinuz.$(REPO).$(host_cpu) -if SUPERMIN -SUPERMINIMG = initramfs.$(REPO).$(host_cpu).supermin.img -SUPERMINFILES = initramfs.$(REPO).$(host_cpu).supermin.hostfiles -endif # This is for building the normal appliance: $(INITRAMFSIMG) $(VMLINUZ): $(top_builddir)/initramfs/fakeroot.log @@ -97,19 +86,19 @@ packagelist: packagelist.in if SUPERMIN -# First we need to decide which files go in and out of the supermin -# appliance. This decision is made by 'supermin-split.sh'. -$(SUPERMINFILES): supermin.incfiles -supermin.incfiles: $(top_builddir)/initramfs/fakeroot.log $(top_builddir)/daemon/guestfsd supermin-split.sh - rm -f supermin.incfiles $(SUPERMINFILES) - bash supermin-split.sh - -# Second we need to create a supermin appliance with just the included -# files (leaving out the host files, which we'll add back at runtime). -$(SUPERMINIMG): supermin.incfiles supermin-make.sh - rm -f $@ - bash supermin-make.sh +supermin.d/base.img supermin.d/hostfiles: stamp-supermin +stamp-supermin: $(top_builddir)/initramfs/fakeroot.log $(top_builddir)/initramfs/init + mkdir -p supermin.d + rm -f $@ supermin.d/base.img supermin.d/hostfiles + febootstrap-to-supermin $(top_builddir)/initramfs supermin.d/base.img supermin.d/hostfiles + touch $@ +supermin.d/daemon.img: $(top_builddir)/initramfs/sbin/guestfsd + mkdir -p supermin.d + rm -f $@ $@-t + (cd $(top_builddir)/initramfs && \ + echo -e "sbin\nsbin/guestfsd" | cpio --quiet -o -H newc ) > $@-t + mv $@-t $@ endif # Extra symlinks needed by the Debian appliance. @@ -160,7 +149,8 @@ test-boot-realistic: emptydisk # Make clean. -CLEANFILES = $(APPLIANCE_FILES) packagelist kmod.whitelist supermin.incfiles +CLEANFILES = packagelist kmod.whitelist clean-local: + rm -f supermin.d/* rm -rf $(top_builddir)/initramfs diff --git a/appliance/init b/appliance/init index 98538de..9ecf007 100755 --- a/appliance/init +++ b/appliance/init @@ -5,6 +5,8 @@ echo Starting /init script ... PATH=/sbin:/usr/sbin:/bin:/usr/bin export PATH +mkdir -p /sysroot + mount -t proc /proc /proc mount -t sysfs /sys /sys diff --git a/appliance/libguestfs-supermin-helper.c b/appliance/libguestfs-supermin-helper.c deleted file mode 100644 index 127b5c0..0000000 --- a/appliance/libguestfs-supermin-helper.c +++ /dev/null @@ -1,919 +0,0 @@ -/* libguestfs-supermin-helper reimplementation in C. - * Copyright (C) 2009-2010 Red Hat Inc. - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. - */ - -/* This script builds the supermin appliance on the fly each - * time the appliance runs. - * - * *NOTE*: This program is designed to be very short-lived, and so we - * don't normally bother to free up any memory that we allocate. - * That's not completely true - we free up stuff if it's obvious and - * easy to free up, and ignore the rest. - */ - -#include - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include "error.h" -#include "filevercmp.h" -#include "fts_.h" -#include "full-write.h" -#include "hash.h" -#include "hash-pjw.h" -#include "xalloc.h" -#include "xvasprintf.h" - -/* Directory containing candidate kernels. We could make this - * configurable at some point. - */ -#define KERNELDIR "/boot" -#define MODULESDIR "/lib/modules" - -/* Buffer size used in copy operations throughout. Large for - * greatest efficiency. - */ -#define BUFFER_SIZE 65536 - -static struct timeval start_t; -static int verbose = 0; - -static void print_timestamped_message (const char *fs, ...); -static const char *create_kernel (const char *hostcpu, const char *kernel); -static void create_appliance (const char *sourcedir, const char *hostcpu, const char *repo, const char *modpath, const char *initrd); - -enum { HELP_OPTION = CHAR_MAX + 1 }; - -static const char *options = "vV"; -static const struct option long_options[] = { - { "help", 0, 0, HELP_OPTION }, - { "verbose", 0, 0, 'v' }, - { "version", 0, 0, 'V' }, - { 0, 0, 0, 0 } -}; - -static void -usage (const char *progname) -{ - printf ("%s: build the supermin appliance on the fly\n" - "\n" - "Usage:\n" - " %s [-options] sourcedir host_cpu repo kernel initrd\n" - " %s --help\n" - " %s --version\n" - "\n" - "This script is used by libguestfs to build the supermin appliance\n" - "(kernel and initrd output files). You should NOT need to run this\n" - "program directly except if you are debugging tricky supermin\n" - "appliance problems.\n" - "\n" - "NB: The kernel and initrd parameters are OUTPUT parameters. If\n" - "those files exist, they are overwritten by the output.\n" - "\n" - "Options:\n" - " --help\n" - " Display this help text and exit.\n" - " --verbose | -v\n" - " Enable verbose messages (give multiple times for more verbosity).\n" - " --version | -V\n" - " Display version number and exit.\n" - "\n" - "Typical usage when debugging supermin appliance problems:\n" - " %s -v /usr/lib*/guestfs x86_64 fedora-12 /tmp/kernel /tmp/initrd\n" - "Note: This will OVERWRITE any existing files called /tmp/kernel\n" - "and /tmp/initrd.\n", - progname, progname, progname, progname, progname); -} - -int -main (int argc, char *argv[]) -{ - /* First thing: start the clock. */ - gettimeofday (&start_t, NULL); - - /* Command line arguments. */ - for (;;) { - int c = getopt_long (argc, argv, options, long_options, NULL); - if (c == -1) break; - - switch (c) { - case HELP_OPTION: - usage (argv[0]); - exit (EXIT_SUCCESS); - - case 'v': - verbose++; - break; - - case 'V': - printf (PACKAGE_NAME " " PACKAGE_VERSION "\n"); - exit (EXIT_SUCCESS); - - default: - usage (argv[0]); - exit (EXIT_FAILURE); - } - } - - if (argc - optind != 5) { - usage (argv[0]); - exit (EXIT_FAILURE); - } - - const char *sourcedir = argv[optind]; - - /* Host CPU and repo constants passed from the library (see: - * https://bugzilla.redhat.com/show_bug.cgi?id=558593). - */ - const char *hostcpu = argv[optind+1]; - const char *repo = argv[optind+2]; - - /* Output files. */ - const char *kernel = argv[optind+3]; - const char *initrd = argv[optind+4]; - - if (verbose) - print_timestamped_message ("sourcedir = %s, " - "host_cpu = %s, " - "repo = %s, " - "kernel = %s, " - "initrd = %s", - sourcedir, hostcpu, repo, kernel, initrd); - - /* Remove the output files if they exist. */ - unlink (kernel); - unlink (initrd); - - /* Create kernel output file. */ - const char *modpath; - modpath = create_kernel (hostcpu, kernel); - - if (verbose) - print_timestamped_message ("finished creating kernel"); - - /* Create the appliance. */ - create_appliance (sourcedir, hostcpu, repo, modpath, initrd); - - if (verbose) - print_timestamped_message ("finished creating appliance"); - - exit (EXIT_SUCCESS); -} - -/* Compute Y - X and return the result in milliseconds. - * Approximately the same as this code: - * http://www.mpp.mpg.de/~huber/util/timevaldiff.c - */ -static int64_t -timeval_diff (const struct timeval *x, const struct timeval *y) -{ - int64_t msec; - - msec = (y->tv_sec - x->tv_sec) * 1000; - msec += (y->tv_usec - x->tv_usec) / 1000; - return msec; -} - -static void -print_timestamped_message (const char *fs, ...) -{ - struct timeval tv; - gettimeofday (&tv, NULL); - - va_list args; - char *msg; - int err; - - va_start (args, fs); - err = vasprintf (&msg, fs, args); - va_end (args); - - if (err < 0) return; - - fprintf (stderr, "supermin helper [%05" PRIi64 "ms] %s\n", - timeval_diff (&start_t, &tv), msg); - - free (msg); -} - -static char **read_dir (const char *dir); -static char **filter_fnmatch (char **strings, const char *patt, int flags); -static char **filter_notmatching_substring (char **strings, const char *sub); -static void sort (char **strings, int (*compare) (const void *, const void *)); -static int isdir (const char *path); - -static int -reverse_filevercmp (const void *p1, const void *p2) -{ - const char *s1 = * (char * const *) p1; - const char *s2 = * (char * const *) p2; - - /* Note, arguments are reversed to achieve a reverse sort. */ - return filevercmp (s2, s1); -} - -/* Create the kernel. This chooses an appropriate kernel and makes a - * symlink to it. - * - * Look for the most recent kernel named vmlinuz-*.* which has a - * corresponding directory in /lib/modules/. If the architecture is - * x86, look for any x86 kernel. - * - * RHEL 5 didn't append the arch to the kernel name, so look for - * kernels without arch second. - * - * If no suitable kernel can be found, exit with an error. - * - * This function returns the module path (ie. /lib/modules/). - */ -static const char * -create_kernel (const char *hostcpu, const char *kernel) -{ - char **all_files = read_dir (KERNELDIR); - - /* In original: ls -1dvr /boot/vmlinuz-*.$arch* 2>/dev/null | grep -v xen */ - const char *patt; - if (hostcpu[0] == 'i' && hostcpu[2] == '8' && hostcpu[3] == '6' && - hostcpu[4] == '\0') - patt = "vmlinuz-*.i?86*"; - else - patt = xasprintf ("vmlinuz-*.%s*", hostcpu); - - char **candidates; - candidates = filter_fnmatch (all_files, patt, FNM_NOESCAPE); - candidates = filter_notmatching_substring (candidates, "xen"); - - if (candidates[0] == NULL) { - /* In original: ls -1dvr /boot/vmlinuz-* 2>/dev/null | grep -v xen */ - patt = "vmlinuz-*"; - candidates = filter_fnmatch (all_files, patt, FNM_NOESCAPE); - candidates = filter_notmatching_substring (candidates, "xen"); - - if (candidates[0] == NULL) - goto no_kernels; - } - - sort (candidates, reverse_filevercmp); - - /* Choose the first candidate which has a corresponding /lib/modules - * directory. - */ - int i; - for (i = 0; candidates[i] != NULL; ++i) { - if (verbose >= 2) - fprintf (stderr, "candidate kernel: " KERNELDIR "/%s\n", candidates[i]); - - /* Ignore "vmlinuz-" at the beginning of the kernel name. */ - const char *version = &candidates[i][8]; - - /* /lib/modules/ */ - char *modpath = xasprintf (MODULESDIR "/%s", version); - - if (verbose >= 2) - fprintf (stderr, "checking modpath %s is a directory\n", modpath); - - if (isdir (modpath)) { - if (verbose >= 2) - fprintf (stderr, "picked %s because modpath %s exists\n", - candidates[i], modpath); - - char *tmp = xasprintf (KERNELDIR "/%s", candidates[i]); - - if (verbose >= 2) - fprintf (stderr, "creating symlink %s -> %s\n", kernel, tmp); - - if (symlink (tmp, kernel) == -1) - error (EXIT_FAILURE, errno, "symlink kernel"); - - free (tmp); - - return modpath; - } - } - - /* Print more diagnostics here than the old script did. */ - no_kernels: - fprintf (stderr, - "libguestfs-supermin-helper: failed to find a suitable kernel.\n" - "I looked for kernels in " KERNELDIR " and modules in " MODULESDIR - ".\n" - "If this is a Xen guest, and you only have Xen domU kernels\n" - "installed, try installing a fullvirt kernel (only for\n" - "libguestfs use, you shouldn't boot the Xen guest with it).\n"); - exit (EXIT_FAILURE); -} - -static void write_kernel_modules (const char *sourcedir, const char *modpath); -static void write_hostfiles (const char *sourcedir, const char *hostcpu, const char *repo); -static void write_to_fd (const void *buffer, size_t len); -static void write_file_to_fd (const char *filename); -static void write_file_len_to_fd (const char *filename, size_t len); -static void write_padding (size_t len); -static char **load_file (const char *filename); -static void cpio_append_fts_entry (FTSENT *entry); -static void cpio_append_stat (const char *filename, struct stat *); -static void cpio_append (const char *filename); -static void cpio_append_trailer (void); - -static int out_fd = -1; -static off_t out_offset = 0; - -/* Create the appliance. - * - * The initrd consists of these components concatenated together: - * - * (1) The base skeleton appliance that we constructed at build time. - * name = initramfs.$repo.$host_cpu.supermin.img - * format = plain cpio - * (2) The modules from modpath which are on the module whitelist. - * format = plain cpio - * (3) The host files which match wildcards in *.supermin.hostfiles. - * format = plain cpio - * - * The original shell scripted used the external cpio program to - * create parts (2) and (3), but we have decided it's going to be - * faster if we just write out the data outselves. The reasons are - * that external cpio is slow (particularly when used with SELinux - * because it does 512 byte reads), and the format that we're writing - * is narrow and well understood, because we only care that the Linux - * kernel can read it. - */ -static void -create_appliance (const char *sourcedir, - const char *hostcpu, const char *repo, - const char *modpath, - const char *initrd) -{ - out_fd = open (initrd, O_WRONLY | O_CREAT | O_TRUNC | O_NOCTTY, 0644); - if (out_fd == -1) - error (EXIT_FAILURE, errno, "open: %s", initrd); - out_offset = 0; - - /* Copy the base skeleton appliance (1). */ - char *tmp = xasprintf ("%s/initramfs.%s.%s.supermin.img", - sourcedir, repo, hostcpu); - write_file_to_fd (tmp); - free (tmp); - - /* Kernel modules (2). */ - write_kernel_modules (sourcedir, modpath); - - /* Copy hostfiles (3). */ - write_hostfiles (sourcedir, hostcpu, repo); - - cpio_append_trailer (); - - /* Finish off and close output file. */ - if (close (out_fd) == -1) - error (EXIT_FAILURE, errno, "close: %s", initrd); -} - -/* Copy kernel modules. - * - * Find every file under modpath. - * - * Exclude all *.ko files, *except* ones which match names in - * the whitelist (which may contain wildcards). Include all - * other files. - * - * Add chosen files to the output. - */ -static void -write_kernel_modules (const char *sourcedir, const char *modpath) -{ - char *tmp = xasprintf ("%s/kmod.whitelist", sourcedir); - char **whitelist = load_file (tmp); - free (tmp); - - char *paths[2] = { (char *) modpath, NULL }; - FTS *fts = fts_open (paths, FTS_COMFOLLOW|FTS_PHYSICAL, NULL); - if (fts == NULL) - error (EXIT_FAILURE, errno, "write_kernel_modules: fts_open: %s", modpath); - - for (;;) { - errno = 0; - FTSENT *entry = fts_read (fts); - if (entry == NULL && errno != 0) - error (EXIT_FAILURE, errno, "write_kernel_modules: fts_read: %s", modpath); - if (entry == NULL) - break; - - /* Ignore directories being visited in post-order. */ - if (entry->fts_info & FTS_DP) - continue; - - /* Is it a *.ko file? */ - if (entry->fts_namelen >= 3 && - entry->fts_name[entry->fts_namelen-3] == '.' && - entry->fts_name[entry->fts_namelen-2] == 'k' && - entry->fts_name[entry->fts_namelen-1] == 'o') { - /* Is it a *.ko file which is on the whitelist? */ - size_t j; - for (j = 0; whitelist[j] != NULL; ++j) { - int r; - r = fnmatch (whitelist[j], entry->fts_name, 0); - if (r == 0) { - /* It's on the whitelist, so include it. */ - if (verbose >= 2) - fprintf (stderr, "including kernel module %s (matches whitelist entry %s)\n", - entry->fts_name, whitelist[j]); - cpio_append_fts_entry (entry); - break; - } else if (r != FNM_NOMATCH) - error (EXIT_FAILURE, 0, "internal error: fnmatch ('%s', '%s', %d) returned unexpected non-zero value %d\n", - whitelist[j], entry->fts_name, 0, r); - } /* for (j) */ - } else - /* It's some other sort of file, or a directory, always include. */ - cpio_append_fts_entry (entry); - } - - if (fts_close (fts) == -1) - error (EXIT_FAILURE, errno, "write_kernel_modules: fts_close: %s", modpath); -} - -/* Copy the host files. - * - * Read the list of entries in *.supermin.hostfiles (which may contain - * wildcards). Look them up in the filesystem, and add those files - * that exist. Ignore any files that don't exist or are not readable. - */ -static void -write_hostfiles (const char *sourcedir, const char *hostcpu, const char *repo) -{ - char *tmp = xasprintf ("%s/initramfs.%s.%s.supermin.hostfiles", - sourcedir, repo, hostcpu); - char **hostfiles = load_file (tmp); - free (tmp); - - /* Hostfiles list can contain "." before each path - ignore it. - * It also contains each directory name before we enter it. But - * we don't read that until we see a wildcard for that directory. - */ - size_t i, j; - for (i = 0; hostfiles[i] != NULL; ++i) { - char *hostfile = hostfiles[i]; - if (hostfile[0] == '.') - hostfile++; - - struct stat statbuf; - - /* Is it a wildcard? */ - if (strchr (hostfile, '*') || strchr (hostfile, '?')) { - char *dirname = xstrdup (hostfile); - char *patt = strrchr (dirname, '/'); - assert (patt); - *patt++ = '\0'; - - char **files = read_dir (dirname); - files = filter_fnmatch (files, patt, FNM_NOESCAPE); - - /* Add matching files. */ - for (j = 0; files[j] != NULL; ++j) { - tmp = xasprintf ("%s/%s", dirname, files[j]); - - if (verbose >= 2) - fprintf (stderr, "including host file %s (matches %s)\n", tmp, patt); - - cpio_append (tmp); - - free (tmp); - } - } - /* Else does this file/directory/whatever exist? */ - else if (lstat (hostfile, &statbuf) == 0) { - if (verbose >= 2) - fprintf (stderr, "including host file %s (directly referenced)\n", - hostfile); - - cpio_append_stat (hostfile, &statbuf); - } /* Ignore files that don't exist. */ - } -} - -/*----------*/ -/* Helper functions. */ - -static void -add_string (char ***argv, size_t *n_used, size_t *n_alloc, const char *str) -{ - char **new_argv; - char *new_str; - - if (*n_used >= *n_alloc) - *argv = x2nrealloc (*argv, n_alloc, sizeof (char *)); - - if (str) - new_str = xstrdup (str); - else - new_str = NULL; - - (*argv)[*n_used] = new_str; - - (*n_used)++; -} - -static size_t -count_strings (char *const *argv) -{ - size_t argc; - - for (argc = 0; argv[argc] != NULL; ++argc) - ; - return argc; -} - -struct dir_cache { - char *path; - char **files; -}; - -static size_t -dir_cache_hash (void const *x, size_t table_size) -{ - struct dir_cache const *p = x; - return hash_pjw (p->path, table_size); -} - -static bool -dir_cache_compare (void const *x, void const *y) -{ - struct dir_cache const *p = x; - struct dir_cache const *q = y; - return strcmp (p->path, q->path) == 0; -} - -/* Read a directory into a list of strings. - * - * Previously looked up directories are cached and returned quickly, - * saving some considerable amount of time compared to reading the - * directory over again. However this means you really must not - * alter the array of strings that are returned. - * - * Returns an empty list if the directory cannot be opened. - */ -static char ** -read_dir (const char *name) -{ - static Hash_table *ht = NULL; - - if (!ht) - ht = hash_initialize (1024, NULL, dir_cache_hash, dir_cache_compare, NULL); - - struct dir_cache key = { .path = (char *) name }; - struct dir_cache *p = hash_lookup (ht, &key); - if (p) - return p->files; - - char **files = NULL; - size_t n_used = 0, n_alloc = 0; - - DIR *dir = opendir (name); - if (!dir) { - /* If it fails to open, that's OK, skip to the end. */ - /*perror (name);*/ - goto done; - } - - for (;;) { - errno = 0; - struct dirent *d = readdir (dir); - if (d == NULL) { - if (errno != 0) - /* But if it fails here, after opening and potentially reading - * part of the directory, that's a proper failure - inform the - * user and exit. - */ - error (EXIT_FAILURE, errno, "%s", name); - break; - } - - add_string (&files, &n_used, &n_alloc, d->d_name); - } - - if (closedir (dir) == -1) - error (EXIT_FAILURE, errno, "closedir: %s", name); - - done: - /* NULL-terminate the array. */ - add_string (&files, &n_used, &n_alloc, NULL); - - /* Add it to the hash for next time. */ - p = xmalloc (sizeof *p); - p->path = (char *) name; - p->files = files; - p = hash_insert (ht, p); - assert (p != NULL); - - return files; -} - -/* Filter a list of strings and return only those matching the wildcard. */ -static char ** -filter_fnmatch (char **strings, const char *patt, int flags) -{ - char **out = NULL; - size_t n_used = 0, n_alloc = 0; - - int i, r; - for (i = 0; strings[i] != NULL; ++i) { - r = fnmatch (patt, strings[i], flags); - if (r == 0) - add_string (&out, &n_used, &n_alloc, strings[i]); - else if (r != FNM_NOMATCH) - error (EXIT_FAILURE, 0, "internal error: fnmatch ('%s', '%s', %d) returned unexpected non-zero value %d\n", - patt, strings[i], flags, r); - } - - add_string (&out, &n_used, &n_alloc, NULL); - return out; -} - -/* Filter a list of strings and return only those which DON'T contain sub. */ -static char ** -filter_notmatching_substring (char **strings, const char *sub) -{ - char **out = NULL; - size_t n_used = 0, n_alloc = 0; - - int i; - for (i = 0; strings[i] != NULL; ++i) { - if (strstr (strings[i], sub) == NULL) - add_string (&out, &n_used, &n_alloc, strings[i]); - } - - add_string (&out, &n_used, &n_alloc, NULL); - return out; -} - -/* Sort a list of strings, in place, with the comparison function supplied. */ -static void -sort (char **strings, int (*compare) (const void *, const void *)) -{ - qsort (strings, count_strings (strings), sizeof (char *), compare); -} - -/* Return true iff path exists and is a directory. This version - * follows symlinks. - */ -static int -isdir (const char *path) -{ - struct stat statbuf; - - if (stat (path, &statbuf) == -1) - return 0; - - return S_ISDIR (statbuf.st_mode); -} - -/* Copy contents of buffer to out_fd and keep out_offset correct. */ -static void -write_to_fd (const void *buffer, size_t len) -{ - if (full_write (out_fd, buffer, len) != len) - error (EXIT_FAILURE, errno, "write"); - out_offset += len; -} - -/* Copy contents of file to out_fd. */ -static void -write_file_to_fd (const char *filename) -{ - char buffer[BUFFER_SIZE]; - int fd2; - ssize_t r; - - if (verbose >= 2) - fprintf (stderr, "write_file_to_fd %s -> %d\n", filename, out_fd); - - fd2 = open (filename, O_RDONLY); - if (fd2 == -1) - error (EXIT_FAILURE, errno, "open: %s", filename); - for (;;) { - r = read (fd2, buffer, sizeof buffer); - if (r == 0) - break; - if (r == -1) { - if (errno == EAGAIN || errno == EWOULDBLOCK || errno == EINTR) - continue; - error (EXIT_FAILURE, errno, "read: %s", filename); - } - write_to_fd (buffer, r); - } - - if (close (fd2) == -1) - error (EXIT_FAILURE, errno, "close: %s", filename); -} - -/* Copy file of given length to output, and fail if the file has - * changed size. - */ -static void -write_file_len_to_fd (const char *filename, size_t len) -{ - char buffer[BUFFER_SIZE]; - size_t count = 0; - - if (verbose >= 2) - fprintf (stderr, "write_file_to_fd %s -> %d\n", filename, out_fd); - - int fd2 = open (filename, O_RDONLY); - if (fd2 == -1) - error (EXIT_FAILURE, errno, "open: %s", filename); - for (;;) { - ssize_t r = read (fd2, buffer, sizeof buffer); - if (r == 0) - break; - if (r == -1) { - if (errno == EAGAIN || errno == EWOULDBLOCK || errno == EINTR) - continue; - error (EXIT_FAILURE, errno, "read: %s", filename); - } - write_to_fd (buffer, r); - count += r; - if (count > len) - error (EXIT_FAILURE, 0, "write_file_len_to_fd: %s: file has increased in size\n", filename); - } - - if (close (fd2) == -1) - error (EXIT_FAILURE, errno, "close: %s", filename); - - if (count != len) - error (EXIT_FAILURE, 0, "libguestfs-supermin-helper: write_file_len_to_fd: %s: file has changed size\n", filename); -} - -/* Load in a file, returning a list of lines. */ -static char ** -load_file (const char *filename) -{ - char **lines = 0; - size_t n_used = 0, n_alloc = 0; - - FILE *fp; - fp = fopen (filename, "r"); - if (fp == NULL) - error (EXIT_FAILURE, errno, "fopen: %s", filename); - - char line[4096]; - while (fgets (line, sizeof line, fp)) { - size_t len = strlen (line); - if (len > 0 && line[len-1] == '\n') - line[len-1] = '\0'; - add_string (&lines, &n_used, &n_alloc, line); - } - - add_string (&lines, &n_used, &n_alloc, NULL); - return lines; -} - -/* Append the file pointed to by FTSENT to the cpio output. */ -static void -cpio_append_fts_entry (FTSENT *entry) -{ - if (entry->fts_info & FTS_NS || entry->fts_info & FTS_NSOK) - cpio_append (entry->fts_path); - else - cpio_append_stat (entry->fts_path, entry->fts_statp); -} - -/* Append the file named 'filename' to the cpio output. */ -static void -cpio_append (const char *filename) -{ - struct stat statbuf; - - if (lstat (filename, &statbuf) == -1) - error (EXIT_FAILURE, errno, "lstat: %s", filename); - cpio_append_stat (filename, &statbuf); -} - -/* Append the file to the cpio output. */ -#define PADDING(len) ((((len) + 3) & ~3) - (len)) - -#define CPIO_HEADER_LEN (6 + 13*8) - -static void -cpio_append_stat (const char *filename, struct stat *statbuf) -{ - const char *orig_filename = filename; - - if (*filename == '/') - filename++; - if (*filename == '\0') - filename = "."; - - if (verbose >= 2) - fprintf (stderr, "cpio_append_stat %s 0%o -> %d\n", - orig_filename, statbuf->st_mode, out_fd); - - /* Regular files and symlinks are the only ones that have a "body" - * in this cpio entry. - */ - int has_body = S_ISREG (statbuf->st_mode) || S_ISLNK (statbuf->st_mode); - - size_t len = strlen (filename) + 1; - - char header[CPIO_HEADER_LEN + 1]; - snprintf (header, sizeof header, - "070701" /* magic */ - "%08X" /* inode */ - "%08X" /* mode */ - "%08X" "%08X" /* uid, gid */ - "%08X" /* nlink */ - "%08X" /* mtime */ - "%08X" /* file length */ - "%08X" "%08X" /* device holding file major, minor */ - "%08X" "%08X" /* for specials, device major, minor */ - "%08X" /* name length (including \0 byte) */ - "%08X", /* checksum (not used by the kernel) */ - (unsigned) statbuf->st_ino, statbuf->st_mode, - statbuf->st_uid, statbuf->st_gid, - (unsigned) statbuf->st_nlink, (unsigned) statbuf->st_mtime, - has_body ? (unsigned) statbuf->st_size : 0, - major (statbuf->st_dev), minor (statbuf->st_dev), - major (statbuf->st_rdev), minor (statbuf->st_rdev), - (unsigned) len, 0); - - /* Write the header. */ - write_to_fd (header, CPIO_HEADER_LEN); - - /* Follow with the filename, and pad it. */ - write_to_fd (filename, len); - size_t padding_len = PADDING (CPIO_HEADER_LEN + len); - write_padding (padding_len); - - /* Follow with the file or symlink content, and pad it. */ - if (has_body) { - if (S_ISREG (statbuf->st_mode)) - write_file_len_to_fd (orig_filename, statbuf->st_size); - else if (S_ISLNK (statbuf->st_mode)) { - char tmp[PATH_MAX]; - if (readlink (orig_filename, tmp, sizeof tmp) == -1) - error (EXIT_FAILURE, errno, "readlink: %s", orig_filename); - write_to_fd (tmp, statbuf->st_size); - } - - padding_len = PADDING (statbuf->st_size); - write_padding (padding_len); - } -} - -/* CPIO voodoo. */ -static void -cpio_append_trailer (void) -{ - struct stat statbuf; - memset (&statbuf, 0, sizeof statbuf); - statbuf.st_nlink = 1; - cpio_append_stat ("TRAILER!!!", &statbuf); - - /* CPIO seems to pad up to the next block boundary, ie. up to - * the next 512 bytes. - */ - write_padding (((out_offset + 511) & ~511) - out_offset); - assert ((out_offset & 511) == 0); -} - -/* Write 'len' bytes of zeroes out. */ -static void -write_padding (size_t len) -{ - static const char buffer[512] = { 0 }; - - while (len > 0) { - size_t n = len < sizeof buffer ? len : sizeof buffer; - write_to_fd (buffer, n); - len -= n; - } -} diff --git a/appliance/supermin-make.sh.in b/appliance/supermin-make.sh.in deleted file mode 100755 index c40dec9..0000000 --- a/appliance/supermin-make.sh.in +++ /dev/null @@ -1,36 +0,0 @@ -#!/bin/bash - -# @configure_input@ -# Copyright (C) 2009 Red Hat Inc. -# -# This program is free software; you can redistribute it and/or modify -# it under the terms of the GNU General Public License as published by -# the Free Software Foundation; either version 2 of the License, or -# (at your option) any later version. -# -# This program is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU General Public License for more details. -# -# You should have received a copy of the GNU General Public License -# along with this program; if not, write to the Free Software -# Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. - -# Build the supermin appliance. -# Read the README file! - -unset CDPATH - -set -e - -cd @top_builddir@ - -output=appliance/initramfs.@REPO@.@host_cpu@.supermin.img - -# Generate final image. -@FEBOOTSTRAP_TO_INITRAMFS@ \ - --nocompress \ - --files=$(pwd)/appliance/supermin.incfiles \ - initramfs > $output-t -mv $output-t $output -ls -lh $output diff --git a/appliance/supermin-split.sh.in b/appliance/supermin-split.sh.in deleted file mode 100755 index 44cfe21..0000000 --- a/appliance/supermin-split.sh.in +++ /dev/null @@ -1,133 +0,0 @@ -#!/bin/bash - -# @configure_input@ -# Copyright (C) 2009 Red Hat Inc. -# -# This program is free software; you can redistribute it and/or modify -# it under the terms of the GNU General Public License as published by -# the Free Software Foundation; either version 2 of the License, or -# (at your option) any later version. -# -# This program is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU General Public License for more details. -# -# You should have received a copy of the GNU General Public License -# along with this program; if not, write to the Free Software -# Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. - -# Decide which files will stay in the supermin appliance and which -# files will be pulled out of the host at runtime. -# -# Read the README file! -# -# The basic idea is that we create two output files, one containing -# the files that will stay, and the other listing the files that -# will be pulled from the host (ie. not go into the appliance now). -# -# The list of files that stay ('supermin.incfiles') is just a straight -# list of files and directories. -# -# The list of files that come from the host ('*.supermin.hostfiles') -# can include wildcards, to allow libraries to be upgraded on the -# host. - -unset CDPATH - -set -e - -cd @top_builddir@/initramfs - -incfiles=../appliance/supermin.incfiles -hostfiles=../appliance/initramfs.@REPO@.@host_cpu@.supermin.hostfiles - -exec 5>$incfiles -exec 6>$hostfiles - -# Note currently the initramfs contains ~2500 files, and none have -# "funny characters" in the names. So this is reasonable just to -# simplify the script. -for path in $(find -not -name fakeroot.log); do - dir=$(dirname "$path") - file=$(basename "$path") - - # For quoting problems with the bash =~ operator, see bash FAQ - # question E14 here http://tiswww.case.edu/php/chet/bash/FAQ and - # http://bugs.debian.org/cgi-bin/bugreport.cgi?bug=487387#25 - # (RHBZ#566511). - p_etc='^\./etc' - p_dev='^\./dev' - p_var='^\./var' - p_lib_modules='^\./lib/modules/' - p_builddir='^\./builddir' - p_ld_so='^ld-[.0-9]+\.so$' - p_libbfd='^libbfd-.*\.so$' - p_libgcc='^libgcc_s-.*\.so\.([0-9]+)$' - p_libntfs3g='^libntfs-3g\.so\..*$' - p_lib123so='^lib(.*)-[-.0-9]+\.so$' - p_lib123so123='^lib(.*)-[-.0-9]+\.so\.([0-9]+)\.' - p_libso123='^lib(.*)\.so\.([0-9]+)\.' - - # All we're going to keep are the special files /init, the daemon, - # configuration files (/etc), devices and modifiable stuff (/var). - if [ "$path" = "./init" -o "$file" = "guestfsd" ]; then - echo "$path" >&5 - - # Get timezone configuration from local system. - elif [ "$path" = "./etc/localtime" ]; then - echo "$path" >&6 - - elif [[ "$path" =~ $p_etc || "$path" =~ $p_dev || "$path" =~ $p_var ]] - then - echo "$path" >&5 - - # Kernel modules are always copied in from the host, including all - # the dependency files. - elif [[ "$path" =~ $p_lib_modules ]]; then - : - - # On mock/Koji, exclude bogus /builddir directory which for some - # reason contains some yum temporary files (RHBZ#566512). - elif [[ "$path" =~ $p_builddir ]]; then - : - - elif [ -d "$path" ]; then - # Always write directory names to both output files. - echo "$path" >&5 - echo "$path" >&6 - - # Some libraries need fixed version numbers replaced by wildcards. - - elif [[ "$file" =~ $p_ld_so ]]; then - echo "$dir/ld-*.so" >&6 - - # Special case for libbfd - elif [[ "$file" =~ $p_libbfd ]]; then - echo "$dir/libbfd-*.so" >&6 - - # Special case for libgcc_s--.so.N - elif [[ "$file" =~ $p_libgcc ]]; then - echo "$dir/libgcc_s-*.so.${BASH_REMATCH[1]}" >&6 - - # Special case for libntfs-3g.so.* - elif [[ "$file" =~ $p_libntfs3g ]]; then - [ -n "$libntfs3g_once" ] || echo "$dir/libntfs-3g.so.*" >&6 - libntfs3g_once=1 - - # libfoo-1.2.3.so - elif [[ "$file" =~ $p_lib123so ]]; then - echo "$dir/lib${BASH_REMATCH[1]}-*.so" >&6 - - # libfoo-1.2.3.so.1.2.3 (but NOT '*.so.N') - elif [[ "$file" =~ $p_lib123so123 ]]; then - echo "$dir/lib${BASH_REMATCH[1]}-*.so.${BASH_REMATCH[2]}.*" >&6 - - # libfoo.so.1.2.3 (but NOT '*.so.N') - elif [[ "$file" =~ $p_libso123 ]]; then - echo "$dir/lib${BASH_REMATCH[1]}.so.${BASH_REMATCH[2]}.*" >&6 - - else - # Anything else comes from the host directly. - echo "$path" >&6 - fi -done diff --git a/configure.ac b/configure.ac index 0eb83c0..d660738 100644 --- a/configure.ac +++ b/configure.ac @@ -305,6 +305,10 @@ if test "x$enable_appliance" = "xyes"; then [febootstrap-to-initramfs],[febootstrap-to-initramfs],[no]) test "x$FEBOOTSTRAP_TO_INITRAMFS" = "xno" && \ AC_MSG_ERROR([febootstrap-to-initramfs must be installed]) + AC_CHECK_PROG([FEBOOTSTRAP_TO_SUPERMIN], + [febootstrap-to-supermin],[febootstrap-to-supermin],[no]) + test "x$FEBOOTSTRAP_TO_SUPERMIN" = "xno" && \ + AC_MSG_ERROR([febootstrap-to-supermin must be installed]) dnl Check we have fakechroot >= 2.9 (it's an indirect requirement dnl of febootstrap, but old versions will fail with yum). @@ -391,40 +395,6 @@ AC_ARG_ENABLE([supermin], [enable_supermin=no]) AM_CONDITIONAL([SUPERMIN],[test "x$enable_supermin" = "xyes"]) -if test "x$enable_supermin" = "xyes"; then - dnl Check febootstrap-to-initramfs accepts the --files option - dnl (febootstrap >= 2.2). - AC_MSG_CHECKING([for --files support in $FEBOOTSTRAP_TO_INITRAMFS]) - out=`$FEBOOTSTRAP_TO_INITRAMFS 2>&1 ||:` - echo "febootstrap_to_initramfs test command output: $out" >&AS_MESSAGE_LOG_FD - if ! echo "$out" | grep -sq -e --files ; then - AC_MSG_RESULT([no]) - AC_MSG_FAILURE( -[febootstrap-to-initramfs does not support the --files option. - -To build the supermin appliance, you need to upgrade to the latest -version of febootstrap. -]) - fi - AC_MSG_RESULT([yes]) - - dnl Check febootstrap-to-initramfs accepts the --nocompress option - dnl (febootstrap >= 2.3). - AC_MSG_CHECKING([for --nocompress support in $FEBOOTSTRAP_TO_INITRAMFS]) - out=`$FEBOOTSTRAP_TO_INITRAMFS 2>&1 ||:` - echo "febootstrap_to_initramfs test command output: $out" >&AS_MESSAGE_LOG_FD - if ! echo "$out" | grep -sq -e --nocompress ; then - AC_MSG_RESULT([no]) - AC_MSG_FAILURE( -[febootstrap-to-initramfs does not support the --nocompress option. - -To build the supermin appliance, you need to upgrade to the latest -version of febootstrap. -]) - fi - AC_MSG_RESULT([yes]) -fi - dnl Enable packet dumps when in verbose mode. This generates lots dnl of debug info, only useful for people debugging the RPC mechanism. AC_ARG_ENABLE([packet-dump], @@ -761,10 +731,6 @@ AC_CONFIG_HEADERS([config.h]) dnl http://www.mail-archive.com/automake@gnu.org/msg10204.html AC_CONFIG_FILES([appliance/update.sh], [chmod +x appliance/update.sh]) -AC_CONFIG_FILES([appliance/supermin-split.sh], - [chmod +x appliance/supermin-split.sh]) -AC_CONFIG_FILES([appliance/supermin-make.sh], - [chmod +x appliance/supermin-make.sh]) AC_CONFIG_FILES([Makefile src/Makefile fish/Makefile po/Makefile.in examples/Makefile appliance/Makefile diff --git a/po/POTFILES.in b/po/POTFILES.in index b2f24d0..8086ab2 100644 --- a/po/POTFILES.in +++ b/po/POTFILES.in @@ -1,4 +1,3 @@ -appliance/libguestfs-supermin-helper.c daemon/augeas.c daemon/available.c daemon/base64.c diff --git a/src/guestfs.c b/src/guestfs.c index 027e08c..0e4cb73 100644 --- a/src/guestfs.c +++ b/src/guestfs.c @@ -928,10 +928,6 @@ static void print_cmdline (guestfs_h *g); static const char *kernel_name = "vmlinuz." REPO "." host_cpu; static const char *initrd_name = "initramfs." REPO "." host_cpu ".img"; -static const char *supermin_name = - "initramfs." REPO "." host_cpu ".supermin.img"; -static const char *supermin_hostfiles_name = - "initramfs." REPO "." host_cpu ".supermin.hostfiles"; int guestfs__launch (guestfs_h *g) @@ -998,8 +994,7 @@ guestfs__launch (guestfs_h *g) fprintf (stderr, "looking for supermin appliance in current directory\n"); if (dir_contains_files (".", - supermin_name, supermin_hostfiles_name, - "kmod.whitelist", NULL)) { + "supermin.d", "kmod.whitelist", NULL)) { if (build_supermin_appliance (g, ".", &kernel, &initrd) == -1) return -1; break; @@ -1011,8 +1006,7 @@ guestfs__launch (guestfs_h *g) fprintf (stderr, "looking for supermin appliance in %s\n", pelem); if (dir_contains_files (pelem, - supermin_name, supermin_hostfiles_name, - "kmod.whitelist", NULL)) { + "supermin.d", "kmod.whitelist", NULL)) { if (build_supermin_appliance (g, pelem, &kernel, &initrd) == -1) return -1; break; @@ -1591,11 +1585,15 @@ build_supermin_appliance (guestfs_h *g, const char *path, snprintf (*initrd, len+8, "%s/initrd", g->tmpdir); snprintf (cmd, sizeof cmd, - "PATH='%s':$PATH " - "libguestfs-supermin-helper%s '%s' " host_cpu " " REPO " %s %s", - path, + "febootstrap-supermin-helper%s " + "-k '%s/kmod.whitelist' " + "'%s/supermin.d' " + host_cpu " " + "%s %s", g->verbose ? " --verbose" : "", - path, *kernel, *initrd); + path, + path, + *kernel, *initrd); if (g->verbose) print_timestamped_message (g, "%s", cmd);