grml-live-db: Replace --db option with --database and use --dpkg for handling dpkg...
[grml-live.git] / grml-live
index a718a41..51e8859 100755 (executable)
--- a/grml-live
+++ b/grml-live
@@ -23,10 +23,9 @@ fi
 set -e
 
 # global variables
-GRML_LIVE_VERSION='0.9.26'
+GRML_LIVE_VERSION='0.9.34'
 PN="$(basename $0)"
 CMDLINE="$0 $@"
-ISO_DATE="$(date +%Y-%m-%d)"
 SOURCES_LIST_FILE='/etc/grml/fai/apt/sources.list'
 ADDONS_LIST_FILE='/boot/isolinux/addons_list.cfg'
 # }}}
@@ -44,6 +43,7 @@ Usage: $PN [options, see as follows]
    -B                      build the ISO without touching the chroot (skips cleanup)
    -c <classe[s]>          classes to be used for building the ISO via FAI
    -C <configfile>         configuration file for grml-live
+   -d <date>               use specified date instead of build time as date of release
    -F                      force execution without prompting
    -g <grml_name>          set the grml flavour name
    -h                      display short usage information and exit
@@ -123,6 +123,8 @@ else
    eerror() { echo "  [!] $*">&2 ;}
    ewarn()  { echo "  [x] $*" ;}
    eend()   { return 0 ;}
+   eindent()  { return 0 ;}
+   eoutdent() { return 0 ;}
 fi
 
 # source main configuration file:
@@ -212,13 +214,14 @@ fi
 # }}}
 
 # command line parsing {{{
-while getopts "a:C:c:g:i:I:o:r:s:t:v:bBFnquVz" opt; do
+while getopts "a:C:c:d:g:i:I:o:r:s:t:v:bBFnquVz" opt; do
   case "$opt" in
     a) ARCH="$OPTARG" ;;
     b) BUILD_ONLY=1 ;;
     B) BUILD_DIRTY=1 ;;
     c) CLASSES="$OPTARG" ;;
     C) CONFIG="$OPTARG" ;;
+    d) DATE="$OPTARG" ;;
     g) GRML_NAME="$OPTARG" ;;
     i) ISO_NAME="$OPTARG" ;;
     I) CHROOT_INSTALL="$OPTARG" ;;
@@ -245,6 +248,7 @@ shift $(($OPTIND - 1))  # set ARGV to the first not parsed commandline parameter
 [ -n "$BUILD_OUTPUT" ]     || BUILD_OUTPUT="$OUTPUT/grml_cd"
 [ -n "$CHROOT_OUTPUT" ]    || CHROOT_OUTPUT="$OUTPUT/grml_chroot"
 [ -n "$CLASSES" ]          || CLASSES="GRMLBASE,GRML_MEDIUM,I386"
+[ -n "$DATE" ]             || DATE="$(date +%Y-%m-%d)"
 [ -n "$DISTRI_INFO" ]      || DISTRI_INFO='Grml - Live Linux for system administrators   '
 [ -n "$DISTRI_NAME" ]      || DISTRI_NAME="grml"
 [ -n "$DISTRI_SPLASH" ]    || DISTRI_SPLASH='grml.png'
@@ -284,22 +288,13 @@ ISO_OUTPUT="$OUTPUT/grml_isos"
 [ -n "$RELEASENAME" ] && export RELEASENAME="$RELEASENAME"
 # }}}
 
-# clean/zero grml-live logfile {{{
+# ZERO_LOGFILE - check for backwards compability reasons {{{
+# this was default behaviour until grml-live 0.9.34:
 if [ -n "$ZERO_LOGFILE" ] ; then
-   echo -n > $LOGFILE
-fi
-# }}}
-
-# clean/zero/remove old FAI directory {{{
-if [ -n "$ZERO_FAI_LOGFILE" ] ; then
-   if [ -d /var/log/fai/"$HOSTNAME" ] ; then
-      rm -rf /var/log/fai/"$HOSTNAME"/"$(readlink /var/log/fai/"$HOSTNAME"/last)"
-      rm -rf /var/log/fai/"$HOSTNAME"/"$(readlink /var/log/fai/"$HOSTNAME"/last-dirinstall)"
-      rm -rf /var/log/fai/"$HOSTNAME"/"$(readlink /var/log/fai/"$HOSTNAME"/last-softupdate)"
-      rm -f /var/log/fai/"$HOSTNAME"/last \
-            /var/log/fai/"$HOSTNAME"/last-dirinstall \
-            /var/log/fai/"$HOSTNAME"/last-softupdate
-   fi
+   PRESERVE_LOGFILE='' # make sure it's cleaned then
+   ewarn "Please consider disabling the \$ZERO_LOGFILE option as grml-live clears..."
+   ewarn "... the logfile $LOGFILE by default (unless \$PRESERVE_LOGFILE is set) nowadays."
+   eend 0
 fi
 # }}}
 
@@ -317,6 +312,7 @@ if [ -z "$FORCE" ] ; then
    [ -n "$ISO_OUTPUT" ]         && echo "  ISO target:        $ISO_OUTPUT"
    [ -n "$GRML_NAME" ]          && echo "  grml name:         $GRML_NAME"
    [ -n "$RELEASENAME" ]        && echo "  release name:      $RELEASENAME"
+   [ -n "$DATE" ]               && echo "  build date:        $DATE"
    [ -n "$VERSION" ]            && echo "  grml version:      $VERSION"
    [ -n "$SUITE" ]              && echo "  Debian suite:      $SUITE"
    [ -n "$ARCH" ]               && echo "  Architecture:      $ARCH"
@@ -341,7 +337,30 @@ if [ -z "$FORCE" ] ; then
    fi
    echo
 fi
+# }}}
+
+# clean/zero/remove logfiles {{{
+
+if [ -n "$PRESERVE_LOGFILE" ] ; then
+   echo "Preserving logfile $LOGFILE as requested via \$PRESERVE_LOGFILE"
+else
+   # make sure it is empty (as it is e.g. appended to grml-live-db)
+   echo -n > $LOGFILE
+fi
+
+if [ -n "$ZERO_FAI_LOGFILE" ] ; then
+   if [ -d /var/log/fai/"$HOSTNAME" ] ; then
+      rm -rf /var/log/fai/"$HOSTNAME"/"$(readlink /var/log/fai/"$HOSTNAME"/last)"
+      rm -rf /var/log/fai/"$HOSTNAME"/"$(readlink /var/log/fai/"$HOSTNAME"/last-dirinstall)"
+      rm -rf /var/log/fai/"$HOSTNAME"/"$(readlink /var/log/fai/"$HOSTNAME"/last-softupdate)"
+      rm -f /var/log/fai/"$HOSTNAME"/last \
+            /var/log/fai/"$HOSTNAME"/last-dirinstall \
+            /var/log/fai/"$HOSTNAME"/last-softupdate
+   fi
+fi
+# }}}
 
+# source config and startup {{{
 if [ -n "$CONFIG" ] ; then
    if ! [ -f "$CONFIG" ] ; then
       log    "Error: $CONFIG could not be read. Exiting. [$(date)]"
@@ -439,7 +458,7 @@ if echo $CLASSES | grep -qi 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?)."
-      eerror "Tip:   Either invoke grml-live with '-i i386' or adjust the architecture class. Exiting."
+      eerror "Tip:   Either invoke grml-live with '-a i386' or adjust the architecture class. Exiting."
       eend 1
       bailout
    fi
@@ -447,7 +466,7 @@ elif echo $CLASSES | grep -qi amd64 ; then
    if ! [[ "$ARCH" == "amd64" ]] ; then
       log    "Error: You specified the AMD64 class but are trying to build something else (I386?)."
       eerror "Error: You specified the AMD64 class but are trying to build something else (I386?)."
-      eerror "Tip:   Either invoke grml-live with '-i amd64' or adjust the architecture class. Exiting."
+      eerror "Tip:   Either invoke grml-live with '-a amd64' or adjust the architecture class. Exiting."
       eend 1
       bailout
    fi
@@ -512,8 +531,8 @@ else
          eerror "Error: critical error while executing fai [exit code ${RC}]. Exiting." ; eend 1
          bailout 1
       else
-         log "Setting /etc/grml_version to $GRML_NAME $VERSION Release Codename $RELEASENAME [$ISO_DATE]"
-         echo "$GRML_NAME $VERSION Release Codename $RELEASENAME [$ISO_DATE]" > $CHROOT_OUTPUT/etc/grml_version
+         log "Setting /etc/grml_version to $GRML_NAME $VERSION Release Codename $RELEASENAME [$DATE]"
+         echo "$GRML_NAME $VERSION Release Codename $RELEASENAME [$DATE]" > $CHROOT_OUTPUT/etc/grml_version
          chmod 644 $CHROOT_OUTPUT/etc/grml_version
          einfo "Rebuilding initramfs"
          # make sure new /etc/grml_version reaches the initramfs:
@@ -569,6 +588,25 @@ else
 fi # BUILD_DIRTY?
 # }}}
 
+# package validator {{{
+CHECKLOG=/var/log/fai/$HOSTNAME/last
+# package validator
+if [ -r "$CHECKLOG/package_errors.log" ] && grep -q '[a-z]' "$CHECKLOG/package_errors.log" ; then
+
+   if [ -n "$EXIT_ON_MISSING_PACKAGES" ] ; then
+      eerror "The following packages were requested for installation but could not be processed:"
+      cat $CHECKLOG/package_errors.log
+      eerror "... exiting as requested via \$EXIT_ON_MISSING_PACKAGES."
+      eend 1
+      bailout 13
+   else
+      ewarn "The following packages were requested for installation but could not be processed:"
+      cat $CHECKLOG/package_errors.log
+      eend 0
+   fi
+fi
+# }}}
+
 # BUILD_OUTPUT - execute arch specific stuff and squashfs {{{
 [ -n "$BUILD_OUTPUT" ] || BUILD_OUTPUT="$OUTPUT/grml_cd"
 mkdir -p "$BUILD_OUTPUT" || bailout 6 "Problem with creating $BUILD_OUTPUT for stage ARCH"
@@ -592,8 +630,8 @@ if [ "$ARCH" = i386 ] || [ "$ARCH" = amd64 ] ; then
             log "Installing /boot/memtest86+.bin"
             cp /boot/memtest86+.bin "$BUILD_OUTPUT"/boot/addons/memtest
          else
-            ewarn "No memtest binary found, skipping."
-            log "No memtest binary found, skipping."
+            ewarn "No memtest binary found (either install package grml-live-addons or memtest86+), skipping."
+            log "No memtest binary found (either install package grml-live-addons or memtest86+), skipping."
             eend 0
          fi
       fi
@@ -635,7 +673,7 @@ if [ "$ARCH" = i386 ] || [ "$ARCH" = amd64 ] ; then
          einfo "Skipping installation boot addons requested via \$NO_ADDONS."
          eend 0
       else
-         if ! [ -d /usr/share/grml-live/templates/boot/addons/bsd4grml ] ; then
+         if ! [ -d "$TEMPLATE_DIRECTORY"/boot/addons/bsd4grml ] ; then
            ewarn "Boot addons not found, skipping therefore. (Consider installing package grml-live-addons)" ; eend 0
          else
            # copy only files so we can handle bsd4grml on its own
@@ -671,7 +709,7 @@ if [ "$ARCH" = i386 ] || [ "$ARCH" = amd64 ] ; then
       RELEASE_INFO="$(extend_string_end 68 "$RELEASE_INFO")"
 
       sed -i "s/%RELEASE_INFO%/$GRML_NAME $VERSION - $RELEASENAME/" "$BUILD_OUTPUT"/GRML/grml-version
-      sed -i "s/%DATE%/$ISO_DATE/"                                  "$BUILD_OUTPUT"/GRML/grml-version
+      sed -i "s/%DATE%/$DATE/"                                      "$BUILD_OUTPUT"/GRML/grml-version
 
       # make sure the squashfs filename is set accordingly:
       SQUASHFS_NAME="$GRML_NAME.squashfs"
@@ -680,7 +718,7 @@ if [ "$ARCH" = i386 ] || [ "$ARCH" = amd64 ] ; then
       for file in "${BUILD_OUTPUT}"/boot/isolinux/*.cfg "${BUILD_OUTPUT}"/boot/isolinux/*.msg \
                   "${BUILD_OUTPUT}"/boot/grub/* ; do
         sed -i "s/%ARCH%/$ARCH/g"                    "${file}"
-        sed -i "s/%DATE%/$ISO_DATE/g"                "${file}"
+        sed -i "s/%DATE%/$DATE/g"                    "${file}"
         sed -i "s/%DISTRI_INFO%/$DISTRI_INFO/g"      "${file}"
         sed -i "s/%DISTRI_NAME%/$DISTRI_NAME/g"      "${file}"
         sed -i "s/%DISTRI_SPLASH%/$DISTRI_SPLASH/g"  "${file}"
@@ -756,12 +794,9 @@ if [ "$ARCH" = i386 ] || [ "$ARCH" = amd64 ] ; then
          fi
       fi
 
-      # jump back to grub from bsd4grml:
-      if [ -e "$BUILD_OUTPUT"/boot/grub/stage2 ]; then
-         GRUB_LEGACY=stage2
-      else
-         GRUB_LEGACY=stage2_eltorito
-      fi
+      # jump back to grub from bsd4grml (/boot/grub/stage2):
+      GRUB_LEGACY=stage2
+
       if [ -e "$BUILD_OUTPUT"/boot/addons/bsd4grml/boot.6 ]; then
          if [ -e "$BUILD_OUTPUT"/boot/grub/core.img ]; then
             GRUB_VERSION=2
@@ -771,8 +806,7 @@ if [ "$ARCH" = i386 ] || [ "$ARCH" = amd64 ] ; then
 
          for file in "$BUILD_OUTPUT"/boot/addons/bsd4grml/boot.6 \
                      "$BUILD_OUTPUT"/boot/addons/bsd4grml/boot.cfg \
-                     "$BUILD_OUTPUT"/boot/isolinux/addons.cfg \
-                     "$BUILD_OUTPUT"/boot/isolinux/syslinux.cfg \
+                     "$BUILD_OUTPUT"/boot/isolinux/*.cfg \
                      "$BUILD_OUTPUT"/boot/grub/grub.cfg \
                      "$BUILD_OUTPUT"/boot/grub/menu.lst ; do
              if [ -e "$file" ] ; then
@@ -948,7 +982,6 @@ else
       eerror "|-> Make sure to install either squashfs-tools and/or squashfs-lzma-tools."
       eerror "\`-> Visit http://grml.org/grml-live/#current_state for further details."
       eend 1
-      package
       bailout
    fi
 
@@ -989,7 +1022,7 @@ find .. -type f -not -name md5sums -not -name isolinux.bin -exec md5sum {} \; >
 if [ "$BOOT_METHOD" = "isolinux" ] ; then
    BOOT_FILE="boot/isolinux/isolinux.bin -c boot/isolinux/boot.cat"
 elif [ "$BOOT_METHOD" = "grub" ] ; then
-   BOOT_FILE="boot/grub/stage2_eltorito"
+   BOOT_FILE="boot/grub/stage2"
 fi
 
 if [ -f "${ISO_OUTPUT}/${ISO_NAME}" -a -z "$UPDATE" -a -z "$BUILD_ONLY" -a -z "$BUILD_DIRTY" -a "$FORCE_ISO_REBUILD" = "false" ]  ; then
@@ -1044,6 +1077,13 @@ else
          else
            log "Creating hybrid ISO file with isohybrid method"
            einfo "Creating hybrid ISO file with isohybrid method"
+           # Notes for consideration:
+           # "-entry 4 -type 1c"
+           # * using 4 as the partition number is supposed to help with BIOSes
+           #   that only support USB-Zip boot
+           # * using 1c (i.e. hidden FAT32 LBA), instead of the default 0x17
+           #   (hidden NTFS, IIRC), as the partition type is sometimes needed
+           #   to get the BIOS even look at the partition created by isohybrid
            isohybrid "${ISO_OUTPUT}/${ISO_NAME}"
            eend $?
          fi
@@ -1054,10 +1094,14 @@ else
          else
            log "Creating hybrid ISO file with manifold method"
            einfo "Creating hybrid ISO file with manifold method"
-           echo 1 63 | \
-               mksh /usr/share/grml-live/scripts/bootgrub.mksh -A -M 1 -p 0x83 -g $cyls:16:32 | \
-               cat - boot/grub/core.img | \
-               dd conv=notrunc of="${ISO_OUTPUT}/${ISO_NAME}" conv=notrunc 2>/dev/null
+           (
+               # 512 bytes: MBR, partition table, load GRUB 2
+               echo 4 63 | mksh /usr/share/grml-live/scripts/bootgrub.mksh -A -M 4:0x96 -g $cyls:16:32
+               # pad to a whole of 2048 bytes (one CD sector)
+               dd if=/dev/zero bs=512 count=3 2>/dev/null
+               # append GRUB 2 (must be <=30720 bytes)
+               cat boot/grub/core.img
+           ) | dd of="${ISO_OUTPUT}/${ISO_NAME}" conv=notrunc 2>/dev/null
            eend $?
          fi
       fi
@@ -1090,10 +1134,60 @@ else
 fi
 # }}}
 
+# log build information to database if grml-live-db is installed and enabled {{{
+dpkg_to_db() {
+if [ -d /usr/share/grml-live-db ] ; then
+
+  # safe defaults
+  DPKG_LIST="/var/log/fai/$HOSTNAME/last/dpkg.list" # the dpkg --list output of the chroot:
+  [ -n "$DPKG_DATABASE" ]  || DPKG_DATABASE=/var/log/grml-live.db
+  [ -n "$DPKG_DBSCRIPT" ]  || DPKG_DBSCRIPT=/usr/share/grml-live-db/scripts/dpkg-to-db
+  [ -n "$DPKG_DBOPTIONS" ] || DPKG_DBOPTIONS="--database $DPKG_DATABASE --logfile $LOGFILE --flavour $GRML_NAME --dpkg $DPKG_LIST"
+
+  if ! [ -x "$DPKG_DBSCRIPT" ] ; then
+    log "Error: $DPKG_DBSCRIPT is not executable, can not log dpkg information."
+    eerror "Error: $DPKG_DBSCRIPT is not executable, can not log dpkg information." ; eend 1
+    bailout 14
+  fi
+
+  # disable by default for now, not sure whether really everyone is using a local db file
+  #if ! touch "$DPKG_DATABASE" ; then
+  #  eerror "Error: can not write to ${DPKG_DATABASE}, can not log dpkg information." ; eend 1
+  #  bailout 14
+  #fi
+
+  if ! [ -r "$DPKG_LIST" ] ; then
+     log "Error reading $DPKG_LIST - can not provide information to $DPKG_DBSCRIPT"
+     eerror "Error reading $DPKG_LIST - can not provide information to $DPKG_DBSCRIPT" ; eend 1
+     bailout 14
+  else
+     einfo "Logging $DPKG_LIST to database $DPKG_DATABASE"
+     log "Logging $DPKG_LIST to database $DPKG_DATABASE"
+     log "Executing $DPKG_DBSCRIPT $DPKG_DBOPTIONS"
+     eindent
+
+     if DB_INFO=$("$DPKG_DBSCRIPT" $DPKG_DBOPTIONS 2>&1) ; then
+       einfo "$DB_INFO"
+       eend 0
+     else
+       eerror "$DB_INFO"
+       eend 1
+     fi
+
+     eoutdent
+  fi
+
+fi
+}
+# }}}
+
 # finalize {{{
 [ -n "$start_seconds" ] && SECONDS="$[$(cut -d . -f 1 /proc/uptime)-$start_seconds]" || SECONDS="unknown"
-einfo "Successfully finished execution of $PN [running ${SECONDS} seconds]" ; eend 0
-log "Successfully finished execution of $PN [running ${SECONDS} seconds]"
+log "Successfully finished execution of $PN [$(date) - running ${SECONDS} seconds]"
+
+dpkg_to_db # make sure we catch the last log line as well, therefore execute between log + einfo
+
+einfo "Successfully finished execution of $PN [$(date) - running ${SECONDS} seconds]" ; eend 0
 bailout 0
 # }}}