Merge branch 'modular-grub' of https://github.com/schierlm/grml-live
[grml-live.git] / grml-live
index ac8f9cf..b629c12 100755 (executable)
--- a/grml-live
+++ b/grml-live
@@ -12,6 +12,9 @@
 export LANG=C
 export LC_ALL=C
 
+# avoid leaking into chroots
+unset TMPDIR
+
 # define function getfilesize before "set -e"
 if stat --help >/dev/null 2>&1; then
   getfilesize='stat -c %s'  # GNU stat
@@ -61,7 +64,7 @@ Usage: $PN [options, see as follows]
    -q                      skip mksquashfs
    -Q                      skip netboot package build
    -r <release_name>       release name
-   -s <suite>              Debian suite; values: etch, lenny, squeeze, sid
+   -s <suite>              Debian suite/release, like: stable, testing, unstable
    -t <template_directory> place of the templates
    -u                      update existing chroot instead of rebuilding it from scratch
    -U <username>           arrange output to be owned by specified username
@@ -74,7 +77,7 @@ Usage examples:
     $PN
     $PN -c GRMLBASE,GRML_FULL,AMD64 -o /dev/shm/grml
     $PN -c GRMLBASE,GRML_FULL,AMD64 -i grml_0.0-1.iso -v 0.0-1
-    $PN -c GRMLBASE,GRML_FULL,AMD64 -s sid -V -r 'grml-live rocks'
+    $PN -c GRMLBASE,GRML_FULL,AMD64 -s stable -V -r 'grml-ftw'
 
 More details: man grml-live + /usr/share/doc/grml-live/grml-live.html
               http://grml.org/grml-live/
@@ -144,7 +147,13 @@ fi
 
 # source main configuration file:
 LIVE_CONF=/etc/grml/grml-live.conf
-. $LIVE_CONF
+if ! [ -r "$LIVE_CONF" ] ; then
+  ewarn "Configuration file $LIVE_CONF can not be read, ignoring"
+else
+  einfo "Sourcing configuration file $LIVE_CONF"
+  . $LIVE_CONF
+  eend $?
+fi
 # }}}
 
 # umount all directories {{{
@@ -157,6 +166,10 @@ umount_all() {
    umount $CHROOT_OUTPUT/dev/pts 2>/dev/null || /bin/true
    umount $CHROOT_OUTPUT/dev 2>/dev/null || /bin/true
 
+   if [ -n "$EXTRACT_ISO_NAME" ] ; then
+     umount "$EXTRACT_ISO_NAME" 2>/dev/null || /bin/true
+   fi
+
    # certain FAI versions sadly leave a ramdisk behind, so better safe than sorry
    if [ -x /usr/lib/fai/mkramdisk ] ; then
      /usr/lib/fai/mkramdisk -u "$(readlink -f ${CHROOT_OUTPUT}/var/lib/dpkg)" >/dev/null 2>&1 || /bin/true
@@ -183,6 +196,11 @@ bailout() {
     [ -n "${CHROOT_OUTPUT}" -a -d "${CHROOT_OUTPUT}" ] && rm -r "${CHROOT_OUTPUT}"
     eend 0
   fi
+
+  # get rid of automatically generated conffiles
+  rm -f ${GRML_FAI_CONFIG}/nfsroot.conf
+  rm -f ${GRML_FAI_CONFIG}/make-fai-nfsroot.conf
+
   if [ -n "$CHOWN_USER" ]; then
     log "Setting ownership"
     einfo "Setting ownership"
@@ -321,10 +339,15 @@ while getopts "a:C:c:d:D:e:g:i:I:o:r:s:t:U:v:AbBFhnNqQuVz" opt; do
     U) CHOWN_USER="$OPTARG" ;;
     V) VERBOSE="-v" ;;
     z) SQUASHFS_ZLIB=1 ;;
-    ?) echo "invalid option -$OPTARG" >&2; bailout 1 ;;
+    ?) echo "invalid option -$OPTARG" >&2; usage; bailout 1 ;;
   esac
 done
 shift $(($OPTIND - 1))  # set ARGV to the first not parsed commandline parameter
+
+if [ -n "$1" ] ; then
+  echo "Error: unknown argument '$1' in options. Exiting to avoid possible data loss." >&2
+  bailout 1
+fi
 # }}}
 
 # read local (non-packaged) configuration {{{
@@ -365,7 +388,6 @@ fi
 [ -n "$GRML_NAME" ]               || GRML_NAME='grml'
 [ -n "$HOSTNAME" ]                || HOSTNAME='grml'
 [ -n "$HYBRID_METHOD" ]           || HYBRID_METHOD='isohybrid'
-[ -n "$NFSROOT_CONF" ]            || NFSROOT_CONF="${GRML_FAI_CONFIG}/make-fai-nfsroot.conf"
 [ -n "$RELEASENAME" ]             || RELEASENAME='grml-live rocks'
 [ -n "$SQUASHFS_EXCLUDES_FILE" ]  || SQUASHFS_EXCLUDES_FILE="${GRML_FAI_CONFIG}/config/grml/squashfs-excludes"
 [ -n "$SUITE" ]                   || SUITE='testing'
@@ -455,7 +477,9 @@ if [ -z "$FORCE" ] ; then
    echo -n "Is this ok for you? [y/N] "
    read a
    if ! [ "$a" = 'y' -o "$a" = 'Y' ] ; then
-      bailout 1 "Exiting as requested."
+      CLEAN_ARTIFACTS=0
+      echo "Exiting as requested."
+      exit 0
    fi
    echo
 fi
@@ -545,7 +569,20 @@ if [ -n "$EXTRACT_ISO_NAME" ]; then
     eend 1
     bailout 1
   fi
-  unsquashfs -d "${CHROOT_OUTPUT}" "${mountpoint}"/live/*/*.squashfs ; rc=$?
+
+  if ls "${mountpoint}"/live/*/*.squashfs 2>/dev/null | grep -q . ; then # ISOs >=2011.12
+    log "Using ${mountpoint}/live/*/*.squashfs for unsquashfs"
+    unsquashfs -d "${CHROOT_OUTPUT}" "${mountpoint}"/live/*/*.squashfs ; rc=$?
+  elif ls "${mountpoint}"/live/*.squashfs 2>/dev/null | grep -q . ; then # ISOs before 2011.12
+    log "Using ${mountpoint}/live/*.squashfs for unsquashfs"
+    unsquashfs -d "${CHROOT_OUTPUT}" "${mountpoint}"/live/*.squashfs ; rc=$?
+  else
+    log "Error: Could not find any *.squashfs files on the ISO"
+    eerror "Error: Could not find any *.squashfs files on the ISO"
+    eend 1
+    bailout 1
+  fi
+
   umount "$mountpoint"
   rmdir "$mountpoint"
   if [ "$rc" != 0 ]; then
@@ -560,9 +597,6 @@ extract_iso
 # }}}
 
 # on-the-fly configuration {{{
-if [ -n "$FAI_DEBOOTSTRAP" ] ; then
-  sed "s#^FAI_DEBOOTSTRAP=.*#FAI_DEBOOTSTRAP=\"$FAI_DEBOOTSTRAP\"#" "$NFSROOT_CONF" | sponge "$NFSROOT_CONF"
-fi
 
 # does this suck? YES!
 # /usr/share/debootstrap/scripts/unstable does not exist, instead use 'sid':
@@ -572,15 +606,9 @@ case $SUITE in
 esac
 export SUITE # make sure it's available in FAI scripts
 
-for file in "$LIVE_CONF" "$LOCAL_CONFIG" "$NFSROOT_CONF" ; do
-    if [ -n "$file" ] ; then
-       sed "s|^FAI_DEBOOTSTRAP=\"[a-z]* |FAI_DEBOOTSTRAP=\"$SUITE |" "$file" | sponge "$file"
-    fi
-done
-
 # validate whether the specified architecture class matches the
 # architecture (option), otherwise installation of kernel will fail
-if echo $CLASSES | grep -qi i386 ; then
+if echo $CLASSES | grep -qw I386 ; then
    if ! [[ "$ARCH" == "i386" ]] ; then
       log    "Error: You specified the I386 class but are trying to build something else (AMD64?)."
       eerror "Error: You specified the I386 class but are trying to build something else (AMD64?)."
@@ -598,11 +626,31 @@ elif echo $CLASSES | grep -qi amd64 ; then
    fi
 fi
 
-if grep -q -- 'FAI_DEBOOTSTRAP_OPTS.*--arch' "$NFSROOT_CONF" ; then
-   sed "s/--arch [a-z0-9]* /--arch $ARCH /" "$NFSROOT_CONF" | sponge "$NFSROOT_CONF"
-else
-   sed "s|^FAI_DEBOOTSTRAP_OPTS=\"\(.*\)|FAI_DEBOOTSTRAP_OPTS=\"--arch $ARCH \1|" "$NFSROOT_CONF" | sponge "$NFSROOT_CONF"
+# generate nfsroot configuration for FAI on the fly
+if [ -z "$FAI_DEBOOTSTRAP" ] ; then
+  FAI_DEBOOTSTRAP="$SUITE http://http.debian.net/debian"
+fi
+
+if [ -z "$FAI_DEBOOTSTRAP_OPTS" ] ; then
+  FAI_DEBOOTSTRAP_OPTS="--exclude=info,tasksel,tasksel-data --arch $ARCH"
+fi
+
+# create backup of old (not yet automatically generated) config file
+if [ -f "${GRML_FAI_CONFIG}/make-fai-nfsroot.conf" ] ; then
+  if ! grep -q 'This is an automatically generated file by grml-live' "${GRML_FAI_CONFIG}/make-fai-nfsroot.conf" ; then
+    ewarn "Found old ${GRML_FAI_CONFIG}/make-fai-nfsroot.conf - moving to ${GRML_FAI_CONFIG}/make-fai-nfsroot.conf.outdated"
+    mv "${GRML_FAI_CONFIG}/make-fai-nfsroot.conf" "${GRML_FAI_CONFIG}/make-fai-nfsroot.conf.outdated"
+    eend $?
+  fi
 fi
+
+echo "# This is an automatically generated file by grml-live.
+# Do NOT edit this file, your changes will be lost.
+FAI_DEBOOTSTRAP=\"$FAI_DEBOOTSTRAP\"
+FAI_DEBOOTSTRAP_OPTS=\"$FAI_DEBOOTSTRAP_OPTS\"
+# EOF " > "${GRML_FAI_CONFIG}/nfsroot.conf"
+# support FAI <=3.4.8, versions >=4.0 use nfsroot.conf
+( cd ${GRML_FAI_CONFIG} && ln -sf nfsroot.conf make-fai-nfsroot.conf )
 # }}}
 
 # CHROOT_OUTPUT - execute FAI {{{
@@ -612,12 +660,6 @@ if [ -n "$BUILD_DIRTY" ]; then
 else
    [ -n "$CHROOT_OUTPUT" ] || CHROOT_OUTPUT="$OUTPUT/grml_chroot"
 
-   # provide inform fai about the ISO we build
-   [ -d "$CHROOT_OUTPUT/etc/" ] || mkdir -p "$CHROOT_OUTPUT/etc/"
-   echo '# This file has been generated by grml-live.' > "$CHROOT_OUTPUT/etc/grml_live_version"
-   [ -n "$GRML_LIVE_VERSION" ] && echo "GRML_LIVE_VERSION=$GRML_LIVE_VERSION" >> "$CHROOT_OUTPUT/etc/grml_live_version"
-   [ -n "$SUITE" ] && echo "SUITE=$SUITE" >> "$CHROOT_OUTPUT/etc/grml_live_version"
-
    if [ -n "$UPDATE" -o -n "$BUILD_ONLY" ] ; then
       FAI_ACTION=softupdate
    else
@@ -647,10 +689,6 @@ else
       mkdir -p "${OUTPUT}/grml_sources/" "${CHROOT_OUTPUT}/grml-live/sources/"
       mount --bind "${OUTPUT}/grml_sources/" "${CHROOT_OUTPUT}/grml-live/sources/"
 
-      # tell dpkg to use "unsafe io" during the build
-      [ -d "$CHROOT_OUTPUT/etc/dpkg/dpkg.cfg.d" ] || mkdir -p "$CHROOT_OUTPUT/etc/dpkg/dpkg.cfg.d"
-      echo force-unsafe-io > "$CHROOT_OUTPUT/etc/dpkg/dpkg.cfg.d/unsafe-io"
-
       log "Executed FAI command line:"
       log "BUILD_ONLY=$BUILD_ONLY BOOTSTRAP_ONLY=$BOOTSTRAP_ONLY GRML_LIVE_CONFIG=$CONFIGDUMP fai $VERBOSE -C $GRML_FAI_CONFIG -s file:///$GRML_FAI_CONFIG/config -c$CLASSES -u $HOSTNAME $FAI_ACTION $CHROOT_OUTPUT $FAI_ARGS"
       BUILD_ONLY="$BUILD_ONLY" BOOTSTRAP_ONLY="$BOOTSTRAP_ONLY" GRML_LIVE_CONFIG="$CONFIGDUMP" fai $VERBOSE \
@@ -658,20 +696,29 @@ else
                   -u "$HOSTNAME" "$FAI_ACTION" "$CHROOT_OUTPUT" $FAI_ARGS | tee -a $LOGFILE
       RC="$PIPESTATUS" # notice: bash-only
 
-      rm -f "$CHROOT_OUTPUT/etc/dpkg/dpkg.cfg.d/unsafe-io"
-
-      FORCE_ISO_REBUILD=true
-
       if [ "$RC" != 0 ] ; then
          log    "Error: critical error while executing fai [exit code ${RC}]. Exiting."
          eerror "Error: critical error while executing fai [exit code ${RC}]. Exiting." ; eend 1
          bailout 1
       fi
 
+      # provide inform fai about the ISO we build, needs to be provided
+      # *after* FAI stage, otherwise FAI skips the debootstrap stage if
+      # there is not BASEFILE (as it checks for presence of /etc) :(
+      echo '# This file has been generated by grml-live.' > "$CHROOT_OUTPUT/etc/grml_live_version"
+      [ -n "$GRML_LIVE_VERSION" ] && echo "GRML_LIVE_VERSION=$GRML_LIVE_VERSION" >> "$CHROOT_OUTPUT/etc/grml_live_version"
+      [ -n "$SUITE" ] && echo "SUITE=$SUITE" >> "$CHROOT_OUTPUT/etc/grml_live_version"
+
+      FORCE_ISO_REBUILD=true
+
       # move fai logs into grml_logs directory
       mkdir -p "$LOG_OUTPUT"/fai/
       cp -r "$CHROOT_OUTPUT"/var/log/fai/"$HOSTNAME"/last/* "$LOG_OUTPUT"/fai/
       rm -rf "$CHROOT_OUTPUT"/var/log/fai
+
+      # store copy of autogenerated configuration file
+      cp ${GRML_FAI_CONFIG}/nfsroot.conf "$LOG_OUTPUT"/fai/
+
       # copy fai package list
       cp "$CHROOT_OUTPUT"/var/log/install_packages.list "$LOG_OUTPUT"/fai/
       # fixup owners
@@ -754,11 +801,12 @@ else
 <testsuite name="grml-live-missing-packages" tests="${package_count}" time="1" failures="${package_errors}" errors="${package_errors}" skipped="0" assertions="0">
 EOF
 
-  for package in $(awk '{print $5}' "${CHECKLOG}/package_errors.log" | sed 's/\.$//') ; do
+  for package in $(awk '{print $1}' "${CHECKLOG}/package_errors.log") ; do
+    failure_reason="$(awk "/$package/ {print \$2}" "${CHECKLOG}/package_errors.log")"
     cat >> "${REPORT_MISSING_PACKAGES}" << EOF
   <testcase name="test_missing_packages_${package}" time="0" assertions="0">
-    <failure type="RuntimeError" message="Package ${package} is missing">
-Package $package is missing in chroot
+    <failure type="${failure_reason}" message="Package ${package} is missing">
+Package $package is missing in chroot (${failure_reason})
   </failure>
   </testcase>
 EOF
@@ -865,7 +913,7 @@ if [ "$ARCH" = i386 ] || [ "$ARCH" = amd64 ] ; then
             copy_addon_file ipxe.lkrn /usr/lib/ipxe addons
             copy_addon_file pci.ids /usr/share/misc addons
             copy_addon_file memtest86+.bin /boot addons
-            for file in memdisk chain.c32 hdt.c32 menu.c32; do
+            for file in memdisk chain.c32 hdt.c32 mboot.c32 menu.c32; do
               copy_addon_file "${file}" /usr/lib/syslinux addons
             done
 
@@ -898,14 +946,30 @@ if [ "$ARCH" = i386 ] || [ "$ARCH" = amd64 ] ; then
        fi
        cp -a ${TEMPLATE_DIRECTORY}/boot/grub/* "$BUILD_OUTPUT"/boot/grub/
 
+       # generate loopback.cfg config file without depending on grub's regexp module
+       # which isn't available in Debian/squeeze
+       echo "## grub2 loopback configuration" > "${BUILD_OUTPUT}"/boot/grub/loopback.cfg
+       echo "source /boot/grub/header.cfg" >> "${BUILD_OUTPUT}"/boot/grub/loopback.cfg
+       for config in "${BUILD_OUTPUT}"/boot/grub/*_default.cfg "${BUILD_OUTPUT}"/boot/grub/*_options.cfg ; do
+         [ -r "$config" ] || continue
+         echo "source ${config##$BUILD_OUTPUT}" >> "${BUILD_OUTPUT}"/boot/grub/loopback.cfg
+       done
+       echo "source /boot/grub/addons.cfg" >> "${BUILD_OUTPUT}"/boot/grub/loopback.cfg
+       echo "source /boot/grub/footer.cfg" >> "${BUILD_OUTPUT}"/boot/grub/loopback.cfg
+
        # copy grub files from target
-       cp -a "${CHROOT_OUTPUT}"/usr/lib/grub/*-pc/*.mod "${BUILD_OUTPUT}"/boot/grub/
-       cp -a "${CHROOT_OUTPUT}"/usr/lib/grub/*-pc/*.o "${BUILD_OUTPUT}"/boot/grub/
-       cp -a "${CHROOT_OUTPUT}"/usr/lib/grub/*-pc/*.lst "${BUILD_OUTPUT}"/boot/grub/
+       mkdir -p "${BUILD_OUTPUT}"/boot/grub/i386-pc/
+       cp -a "${CHROOT_OUTPUT}"/usr/lib/grub/*-pc/*.mod "${BUILD_OUTPUT}"/boot/grub/i386-pc/
+       cp -a "${CHROOT_OUTPUT}"/usr/lib/grub/*-pc/*.o "${BUILD_OUTPUT}"/boot/grub/i386-pc/
+       cp -a "${CHROOT_OUTPUT}"/usr/lib/grub/*-pc/*.lst "${BUILD_OUTPUT}"/boot/grub/i386-pc/
        cp -a "${CHROOT_OUTPUT}"/usr/share/grub/ascii.pf2 "${BUILD_OUTPUT}"/boot/grub/
        cp -a "${CHROOT_OUTPUT}"/boot/grub/core.img "${BUILD_OUTPUT}"/boot/grub/
        cp -a "${CHROOT_OUTPUT}"/boot/grub/grub.img "${BUILD_OUTPUT}"/boot/grub/
 
+       # copy modules for UEFI grub
+       mkdir -p "${BUILD_OUTPUT}"/boot/grub/x86_64-efi/
+       cp -a "${CHROOT_OUTPUT}"/usr/lib/grub/x86_64-efi/*.{mod,lst} "${BUILD_OUTPUT}"/boot/grub/x86_64-efi/
+
        if ! [ -d "${TEMPLATE_DIRECTORY}"/GRML ] ; then
           log    "Error: ${TEMPLATE_DIRECTORY}/GRML does not exist. Exiting."
           eerror "Error: ${TEMPLATE_DIRECTORY}/GRML does not exist. Exiting." ; eend 1
@@ -955,7 +1019,7 @@ if [ "$ARCH" = i386 ] || [ "$ARCH" = amd64 ] ; then
            sed -i "s/%SHORT_NAME%/$SHORT_NAME/g"        "${file}"
            sed -i "s/%VERSION%/$VERSION/g"              "${file}"
 
-           [ -n "$DEFAULT_BOOTOPTIONS" ] && sed -i "s/ boot=live/ boot=live $DEFAULT_BOOTOPTIONS/"  "${file}"
+           [ -n "$DEFAULT_BOOTOPTIONS" ] && sed -i "s; boot=live; boot=live $DEFAULT_BOOTOPTIONS;"  "${file}"
 
            if [ -n "$NO_BOOTID" ] ; then
               sed -i "s/ bootid=%BOOTID%//g" "${file}" # drop bootid bootoption
@@ -1168,7 +1232,7 @@ fi
 # create md5sum file:
 if [ -z "$BOOTSTRAP_ONLY" ] ; then
   ( cd $BUILD_OUTPUT/GRML/"${GRML_NAME}" &&
-  find .. -type f -not -name md5sums -not -name isolinux.bin -exec md5sum {} \; > md5sums )
+  find ../.. -type f -not -name md5sums -not -name isolinux.bin -exec md5sum {} \; > md5sums )
 fi
 # }}}
 
@@ -1314,9 +1378,14 @@ else
           #   (hidden NTFS, IIRC), as the partition type is sometimes needed
           #   to get the BIOS even look at the partition created by isohybrid
           if isohybrid --help | grep -q -- --uefi ; then
-            log   "Detected uefi support for isohybrid, enabling"
-            einfo "Detected uefi support for isohybrid, enabling"
-            ISOHYBRID_OPTIONS=--uefi
+            if echo $CLASSES | grep -qw I386 ; then
+              log   "Detected uefi support for isohybrid but 32bit systems do not support it, ignoring."
+              einfo "Detected uefi support for isohybrid but 32bit systems do not support it, ignoring."
+            else
+              log   "Detected uefi support for isohybrid, enabling"
+              einfo "Detected uefi support for isohybrid, enabling"
+              ISOHYBRID_OPTIONS=--uefi
+            fi
           fi
 
           log "isohybrid $ISOHYBRID_OPTIONS ${ISO_OUTPUT}/${ISO_NAME}"
@@ -1371,7 +1440,7 @@ create_netbootpackage() {
 
   mkdir -p "$NETBOOT"
 
-  if ! [ -r "${CHROOT}/usr/lib/syslinux/pxelinux.0" ] ; then
+  if ! [ -r "${CHROOT_OUTPUT}/usr/lib/syslinux/pxelinux.0" ] ; then
     ewarn "File /usr/lib/syslinux/pxelinux.0 not found in build chroot." ; eend 0
     eindent
     einfo "Install syslinux[-common] package in chroot to get a netboot package."
@@ -1392,7 +1461,12 @@ create_netbootpackage() {
   if [ -r "${BUILD_OUTPUT}/boot/isolinux/netboot.cfg" ] ; then
     cp "${BUILD_OUTPUT}/boot/isolinux/netboot.cfg" "${WORKING_DIR}/pxelinux.cfg/default"
   else
-    ewarn "File ${BUILD_OUTPUT}/boot/isolinux/netboot.cfg not found." ; eend 0
+    log   "File ${BUILD_OUTPUT}/boot/isolinux/netboot.cfg not found."
+    ewarn "File ${BUILD_OUTPUT}/boot/isolinux/netboot.cfg not found."
+    eindent
+    log   "Hint: Are you using custom templates which do not provide netboot.cfg?"
+    ewarn "Hint: Are you using custom templates which do not provide netboot.cfg?" ; eend 0
+    eoutdent
   fi
 
   if tar -C "$OUTPUTDIR" -jcf "${OUTPUT_FILE}" "grml_netboot_package_${GRML_NAME}_${VERSION}" ; then