Support FAI's newer scripts.log, as used with FAI versions >=6.0
[grml-live.git] / grml-live
1 #!/bin/bash
2 # Filename:      grml-live
3 # Purpose:       build process script for generating a (grml based) Linux Live-ISO
4 # Authors:       grml-team (grml.org),
5 #                (c) Michael Prokop <mika@grml.org>,
6 #                (c) Thorsten Glaser <tg@mirbsd.org>
7 # Bug-Reports:   see http://grml.org/bugs/
8 # License:       This file is licensed under the GPL v2 or any later version.
9 ################################################################################
10
11 # some misc and global stuff {{{
12 export LANG=C
13 export LC_ALL=C
14
15 # avoid leaking into chroots
16 unset TMPDIR
17
18 # define function getfilesize before "set -e"
19 if stat --help >/dev/null 2>&1; then
20   getfilesize='stat -c %s'  # GNU stat
21 else
22   getfilesize='stat -f %z'  # BSD stat
23 fi
24
25 # exit on any error:
26 # disable for now since it seems to cause some problems
27 # set -e
28
29 # The line following this line is patched by debian/rules.
30 GRML_LIVE_VERSION='***UNRELEASED***'
31
32 # global variables
33 PN="$(basename $0)"
34 CMDLINE="$0 $@"
35 ADDONS_LIST_FILE='/boot/isolinux/addons_list.cfg'
36 # }}}
37
38 # usage information {{{
39 usage()
40 {
41   echo "
42 $PN - build process script for generating a (grml based) Linux Live-ISO
43
44 Usage: $PN [options, see as follows]
45
46    -a <architecture>       architecture; available values: i386 and amd64
47    -A                      clean build directories before and after running
48    -b                      build the ISO without updating the chroot via FAI
49    -B                      build the ISO without touching the chroot (skips cleanup)
50    -c <classe[s]>          classes to be used for building the ISO via FAI
51    -C <configfile>         configuration file for grml-live
52    -d <date>               use specified date instead of build time as date of release
53    -D <configdir>          use specified configuration directory instead of /etc/grml/fai
54    -e <iso_name>           extract ISO and squashfs contents from iso_name
55    -F                      force execution without prompting
56    -g <grml_name>          set the grml flavour name
57    -h                      display short usage information and exit
58    -i <iso_name>           name of ISO
59    -I <src_directory>      directory which provides files that should become
60                            part of the chroot/ISO
61    -n                      skip generation of ISO
62    -N                      bootstrap (build chroot) only, do not create files for ISO
63    -o <output_directory>   main output directory of the build process
64    -q                      skip mksquashfs
65    -Q                      skip netboot package build
66    -r <release_name>       release name
67    -s <suite>              Debian suite/release, like: stable, testing, unstable
68    -S <script_directory>   place of scripts (defaults to /usr/share/grml-live/scripts)
69    -t <template_directory> place of the templates
70    -u                      update existing chroot instead of rebuilding it from scratch
71    -U <username>           arrange output to be owned by specified username
72    -v <version_number>     specify version number of the release
73    -V                      increase verbosity in the build process
74    -w <date>               wayback machine, build system using Debian archives
75                            from specified date
76    -z                      use ZLIB instead of LZMA/XZ compression
77
78 Usage examples:
79
80     $PN
81     $PN -c GRMLBASE,GRML_FULL,AMD64 -o /dev/shm/grml
82     $PN -c GRMLBASE,GRML_FULL,AMD64 -i grml_0.0-1.iso -v 0.0-1
83     $PN -c GRMLBASE,GRML_FULL,AMD64 -s stable -V -r 'grml-ftw'
84
85 More details: man grml-live + /usr/share/doc/grml-live/grml-live.html
86               http://grml.org/grml-live/
87
88 Please send your bug reports and feedback to the grml-team: http://grml.org/bugs/
89 "
90    [ "$(id -u 2>/dev/null)" != 0 ] && echo "Please notice that this script requires root permissions."
91 }
92
93 # make sure it's possible to get usage information without being
94 # root or actually executing the script
95 if [ "$1" = '-h' -o "$1" = '--help' ] ; then
96    usage
97    exit 0
98 fi
99 # }}}
100
101 # some runtime checks {{{
102 # we need root permissions for the build-process:
103 if [ "$(id -u 2>/dev/null)" != 0 ] ; then
104    echo "Error: please run this script with uid 0 (root)." >&2
105    exit 1
106 fi
107
108 if [ -r /var/run/fai/FAI_INSTALLATION_IN_PROGRESS ] ; then
109    echo "/usr/sbin/fai already running or was aborted before.">&2
110    echo "You may remove /var/run/fai/FAI_INSTALLATION_IN_PROGRESS and try again.">&2
111    exit 1
112 fi
113
114 # see #449236
115 if [ -r /var/run/fai/fai_softupdate_is_running ] ; then
116    echo "/usr/sbin/fai softupdate already running or was aborted before.">&2
117    echo "You may remove /var/run/fai/fai_softupdate_is_running and try again.">&2
118    exit 1
119 fi
120 # }}}
121
122 # lsb-functions and configuration stuff {{{
123 # make sure they are not set by default
124 VERBOSE=''
125 FORCE=''
126 UPDATE=''
127 BUILD_ONLY=''
128 BUILD_DIRTY=''
129 BOOTSTRAP_ONLY=''
130 HOSTNAME=''
131 USERNAME=''
132 CONFIGDUMP=''
133
134 # don't use colors/escape sequences
135 if [ -r /lib/lsb/init-functions ] ; then
136   . /lib/lsb/init-functions
137   ! log_use_fancy_output && NOCOLORS=true
138 fi
139
140 if [ -r /etc/grml/lsb-functions ] ; then
141    . /etc/grml/lsb-functions
142 else
143    einfo()  { echo "  [*] $*" ;}
144    eerror() { echo "  [!] $*">&2 ;}
145    ewarn()  { echo "  [x] $*" ;}
146    eend()   { return 0 ;}
147    eindent()  { return 0 ;}
148    eoutdent() { return 0 ;}
149 fi
150
151 # source main configuration file:
152 [ -z "$LIVE_CONF" ] && LIVE_CONF='/etc/grml/grml-live.conf'
153 if ! [ -r "$LIVE_CONF" ] ; then
154   ewarn "Configuration file $LIVE_CONF can not be read, ignoring"
155 else
156   einfo "Sourcing configuration file $LIVE_CONF"
157   . $LIVE_CONF
158   eend $?
159 fi
160 # }}}
161
162 # umount all directories {{{
163 umount_all() {
164    # make sure we don't leave any mounts - FAI doesn't remove them always
165    umount $CHROOT_OUTPUT/proc/sys/fs/binfmt_misc 2>/dev/null || /bin/true
166    umount $CHROOT_OUTPUT/proc 2>/dev/null || /bin/true
167    umount $CHROOT_OUTPUT/run/udev 2>/dev/null || /bin/true
168    umount $CHROOT_OUTPUT/run  2>/dev/null || /bin/true
169    umount $CHROOT_OUTPUT/sys  2>/dev/null || /bin/true
170    umount $CHROOT_OUTPUT/dev/pts 2>/dev/null || /bin/true
171    umount $CHROOT_OUTPUT/dev 2>/dev/null || /bin/true
172
173    if [ -n "$EXTRACT_ISO_NAME" ] ; then
174      umount "$EXTRACT_ISO_NAME" 2>/dev/null || /bin/true
175    fi
176
177    # certain FAI versions sadly leave a ramdisk behind, so better safe than sorry
178    if [ -x /usr/lib/fai/mkramdisk ] ; then
179      /usr/lib/fai/mkramdisk -u "$(readlink -f ${CHROOT_OUTPUT}/var/lib/dpkg)" >/dev/null 2>&1 || /bin/true
180    fi
181
182    umount "${CHROOT_OUTPUT}/grml-live/sources/" 2>/dev/null || /bin/true
183    [ -n "$MIRROR_DIRECTORY" ] && umount "${CHROOT_OUTPUT}/${MIRROR_DIRECTORY}"
184 }
185 # }}}
186
187 # store logfiles {{{
188 store_logfiles() {
189   # move fai logs into grml_logs directory
190   mkdir -p "$LOG_OUTPUT"/fai/
191   cp -r "$CHROOT_OUTPUT"/var/log/fai/"$HOSTNAME"/last/* "$LOG_OUTPUT"/fai/
192   rm -rf "$CHROOT_OUTPUT"/var/log/fai
193
194   # store copy of autogenerated configuration file
195   cp ${GRML_FAI_CONFIG}/nfsroot.conf "$LOG_OUTPUT"/fai/
196
197   # copy fai package list
198   cp "$CHROOT_OUTPUT"/var/log/install_packages.list "$LOG_OUTPUT"/fai/
199   # fixup owners
200   chown root:adm "$LOG_OUTPUT"/fai/*
201   chmod 664 "$LOG_OUTPUT"/fai/*
202 }
203 # }}}
204
205 # clean exit {{{
206 bailout() {
207   rm -f /var/run/fai/fai_softupdate_is_running \
208         /var/run/fai/FAI_INSTALLATION_IN_PROGRESS
209   [ -n "$CONFIGDUMP"      ]  && rm -f  "$CONFIGDUMP"
210   [ -n "$SQUASHFS_STDERR" ]  && rm -rf "$SQUASHFS_STDERR"
211   umount_all
212   [ -n "$1" ] && EXIT="$1" || EXIT="1"
213   [ -n "$2" ] && eerror "$2">&2
214   if [ -n "$CLEAN_ARTIFACTS" ]; then
215     log "Cleaning up"
216     einfo "Cleaning up"
217     [ -n "${BUILD_OUTPUT}"  -a -d "${BUILD_OUTPUT}"  ] && rm -r "${BUILD_OUTPUT}"
218     [ -n "${CHROOT_OUTPUT}" -a -d "${CHROOT_OUTPUT}" ] && rm -r "${CHROOT_OUTPUT}"
219     eend 0
220   fi
221
222   # get rid of automatically generated conffiles
223   rm -f ${GRML_FAI_CONFIG}/nfsroot.conf
224
225   if [ -n "$CHOWN_USER" ]; then
226     log "Setting ownership"
227     einfo "Setting ownership"
228     [ -n "${OUTPUT}"         -a -d "${OUTPUT}"         ] && chown -R "${CHOWN_USER}:" "${OUTPUT}"
229     [ -n "${BUILD_OUTPUT}"   -a -d "${BUILD_OUTPUT}"   ] && chown -R "${CHOWN_USER}:" "${BUILD_OUTPUT}"
230     [ -n "${CHROOT_OUTPUT}"  -a -d "${CHROOT_OUTPUT}"  ] && chown -R "${CHOWN_USER}:" "${CHROOT_OUTPUT}"
231     [ -n "${ISO_OUTPUT}"     -a -d "${ISO_OUTPUT}"     ] && chown -R "${CHOWN_USER}:" "${ISO_OUTPUT}"
232     [ -n "${LOG_OUTPUT}"     -a -d "${LOG_OUTPUT}"     ] && chown -R "${CHOWN_USER}:" "${LOG_OUTPUT}"
233     [ -n "${NETBOOT}"        -a -d "${NETBOOT}"        ] && chown -R "${CHOWN_USER}:" "${NETBOOT}"
234     eend 0
235   fi
236   log "------------------------------------------------------------------------------"
237   exit "$EXIT"
238 }
239 trap bailout 1 2 3 3 6 9 14 15
240 trap umount_all EXIT
241 # }}}
242
243 # some important functions {{{
244
245 # log output:
246 # usage: log "string to log"
247 log() { [ -n "$LOGFILE" ] && echo "$*" >> $LOGFILE ; }
248
249 # cut string at character number int = $1
250 # usage: cut_string 5 "1234567890" will output "12345"
251 cut_string() {
252   [ -n "$2" ] || return 1
253   echo "$2" | head -c "$1"; echo -ne "\n"
254 }
255
256 # prepend int = $1 spaces before string = $2
257 # usage: extend_string_begin 5 "123" will output "  123"
258 extend_string_begin() {
259   [ -n "$2" ] || return 1
260   local COUNT="$(echo $2 | wc -c)"
261   local FILL="$(expr $COUNT - $1)"
262   while [ "$FILL" -gt 1 ] ; do
263     echo -n " "
264     local FILL=$(expr $FILL - 1)
265   done
266   while [ "$FILL" -lt 1 ] ; do
267     echo -n " "
268     local FILL=$(expr $FILL + 1)
269   done
270   echo "$2" | head -c "$1"; echo -ne "\n"
271 }
272
273 # append int = $1 spaces to string = $2
274 # usage: extend_string_begin 5 "123" will output "123  "
275 extend_string_end() {
276   [ -n "$2" ] || return 1
277   echo -n "$2" | head -c "$1"
278   local COUNT="$(echo $2 | wc -c)"
279   local FILL="$(expr $COUNT - $1)"
280   while [ "$FILL" -gt 1 ] ; do
281     echo -n " "
282     local FILL=$(expr $FILL - 1)
283   done
284   while [ "$FILL" -lt 1 ] ; do
285     echo -n " "
286     local FILL=$(expr $FILL + 1)
287   done
288   echo -ne "\n"
289 }
290
291 # Copy addonfile $1 from either
292 #   * the chroot (via $2, the system path),
293 #   * or from TEMPLATE_DIRECTORY/compat (if exists),
294 #   * or from the host system (again, using $2),
295 # or warn about the missing file.
296 #
297 # This is because:
298 #   * We assume that the chroot always has a "good" version of
299 #     the file. Also it makes sources handling easier.
300 #   * On unstable, we recommend the Debian packages containing
301 #     these files. The user can override them by putting his
302 #     "better" version into the chroot.
303 #   * With older releases the Debian packages are probably
304 #     not available, so we look in TEMPLATE_DIRECTORY/compat,
305 #     where a (custom) package might install current file versions.
306 copy_addon_file() {
307   DEST="${BUILD_OUTPUT}/boot/$3"
308   if [ ! -d "${DEST}/" ]; then
309     mkdir -p "${DEST}"
310   fi
311   if [ -e "$CHROOT_OUTPUT/$2/$1" ]; then
312     log   "Copying $1 from chroot"
313     cp "$CHROOT_OUTPUT/$2/$1" "${DEST}/"
314     return $?
315   fi
316   if [ -e "${TEMPLATE_DIRECTORY}/compat/$3/$1" ]; then
317     log   "Copying $1 from ${TEMPLATE_DIRECTORY}/compat"
318     cp "${TEMPLATE_DIRECTORY}/compat/$3/$1" "${DEST}/"
319     return $?
320   fi
321   if [ -e "$2/$1" ]; then
322     log   "Copying $1 from system"
323     cp "$2/$1" "${DEST}/"
324     return $?
325   fi
326
327   msg="Missing addon file: \"$1\""
328   ewarn "$msg" ; eend 1
329   log "copy_addon_file: $msg"
330 }
331
332 # replace placeholders in template files with actual information
333 adjust_boot_files() {
334   if [ -z "$1" ] ; then
335     echo "Usage: adjust_boot_files <template_file>" >&2
336     exit 1
337   fi
338
339   local release_info
340   if [ -n "${RELEASE_INFO:-}" ] ; then
341     release_info="${RELEASE_INFO}"
342   else
343     ewarn "Variable RELEASE_INFO is unset, applying fallback for usage in adjust_boot_files." ; eend 1
344     release_info="$GRML_NAME $VERSION - Release Codename $RELEASENAME"
345   fi
346
347   # ensure this has a specific length
348   local fixed_release_info
349   fixed_release_info="$(cut_string 68 "$release_info")"
350   fixed_release_info="$(extend_string_end 68 "$fixed_release_info")"
351
352   for file in "$@" ; do
353     if [ -r "${file}" ] && [ -f "${file}" ] ; then
354       sed -i "s/%ARCH%/$ARCH/g"                    "${file}"
355       sed -i "s/%DATE%/$DATE/g"                    "${file}"
356       sed -i "s/%DISTRI_INFO%/$DISTRI_INFO/g"      "${file}"
357       sed -i "s/%DISTRI_NAME%/$DISTRI_NAME/g"      "${file}"
358       sed -i "s/%DISTRI_SPLASH%/$DISTRI_SPLASH/g"  "${file}"
359       sed -i "s/%GRML_NAME%/$GRML_NAME/g"          "${file}"
360       sed -i "s/%SQUASHFS_NAME%/$SQUASHFS_NAME/g"  "${file}"
361       sed -i "s/%RELEASE_INFO%/$fixed_release_info/g" "${file}"
362       sed -i "s/%SHORT_NAME%/$SHORT_NAME/g"        "${file}"
363       sed -i "s/%VERSION%/$VERSION/g"              "${file}"
364       if [ -n "${BOOT_FILE}" ] ; then
365         sed -i "s;%BOOT_FILE%;$BOOT_FILE;g"        "${file}"
366       fi
367
368       [ -n "$DEFAULT_BOOTOPTIONS" ] && sed -i "s; boot=live; boot=live $DEFAULT_BOOTOPTIONS;"  "${file}"
369
370       if [ -n "$NO_BOOTID" ] ; then
371         sed -i "s/ bootid=%BOOTID%//g" "${file}" # drop bootid bootoption
372       else
373         sed -i "s/%BOOTID%/$BOOTID/g" "${file}" # adjust bootid=... argument
374       fi
375     fi
376   done
377 }
378 # }}}
379
380 # command line parsing {{{
381 while getopts "a:C:c:d:D:e:g:i:I:o:r:s:S:t:U:v:w:AbBFhnNqQuVz" opt; do
382   case "$opt" in
383     a) ARCH="$OPTARG" ;;
384     A) CLEAN_ARTIFACTS=1 ;;
385     b) BUILD_ONLY=1 ;;
386     B) BUILD_DIRTY=1 ;;
387     c) CLASSES="$OPTARG" ;;
388     C) LOCAL_CONFIG="$(readlink -f $OPTARG)" ;;
389     d) DATE="$OPTARG" ;;
390     D) GRML_FAI_CONFIG="$(readlink -f $OPTARG)" ;;
391     e) EXTRACT_ISO_NAME="$(readlink -f $OPTARG)" ;;
392     g) GRML_NAME="$OPTARG" ;;
393     h) usage ; bailout 0 ;;
394     i) ISO_NAME="$OPTARG" ;;
395     I) CHROOT_INSTALL="$OPTARG" ;;
396     n) SKIP_MKISOFS=1 ;;
397     N) BOOTSTRAP_ONLY=1; SKIP_MKISOFS=1; SKIP_MKSQUASHFS=1 ;;
398     o) OUTPUT="$(readlink -f $OPTARG)" ;;
399     q) SKIP_MKSQUASHFS=1 ;;
400     Q) SKIP_NETBOOT=1 ;;
401     r) RELEASENAME="$OPTARG" ;;
402     s) SUITE="$OPTARG" ;;
403     S) SCRIPTS_DIRECTORY="$OPTARG";;
404     t) TEMPLATE_DIRECTORY="$OPTARG";;
405     v) VERSION="$OPTARG" ;;
406     F) FORCE=1 ;;
407     u) UPDATE=1 ;;
408     U) CHOWN_USER="$OPTARG" ;;
409     V) VERBOSE="-v" ;;
410     w) export WAYBACK_DATE="$OPTARG" ;;
411     z) SQUASHFS_ZLIB=1 ;;
412     ?) echo "invalid option -$OPTARG" >&2; usage; bailout 1 ;;
413   esac
414 done
415 shift $(($OPTIND - 1))  # set ARGV to the first not parsed commandline parameter
416
417 if [ -n "$1" ] ; then
418   echo "Error: unknown argument '$1' in options. Exiting to avoid possible data loss." >&2
419   bailout 1
420 fi
421 # }}}
422
423 # read local (non-packaged) configuration {{{
424 if [ -z "$LOCAL_CONFIG" ]; then
425   if [ -r "/etc/grml/grml-live.local" ]; then
426     LOCAL_CONFIG="/etc/grml/grml-live.local"
427   fi
428 fi
429 if [ -n "$LOCAL_CONFIG" ]; then
430   if [ -r "$LOCAL_CONFIG" ]; then
431     . $LOCAL_CONFIG
432   else
433     eerror "Could not read specified local configuration file \"$LOCAL_CONFIG\"."
434     bailout 1
435   fi
436   LOCAL_CONFIG=$(readlink -f "$LOCAL_CONFIG")
437 else
438   LOCAL_CONFIG=''
439 fi
440
441 if [ -n "${GRML_LIVE_SOURCES:-}" ] ; then
442   eerror "Config variable \$GRML_LIVE_SOURCES is set. This variable has been deprecated."
443   ewarn  "Please set up \${GRML_FAI_CONFIG}/config/files/etc/apt/sources.list.d/* instead."
444   bailout 1
445 fi
446 # }}}
447
448 # assume sane defaults (if not set already) {{{
449 [ -n "$ARCH" ]                    || ARCH="$(dpkg --print-architecture)"
450 [ -n "$BOOT_METHOD" ]             || BOOT_METHOD='isolinux'
451 [ -n "$CLASSES" ]                 || CLASSES="GRMLBASE,GRML_FULL,$(echo ${ARCH} | tr 'a-z' 'A-Z')"
452 [ -n "$DATE" ]                    || DATE="$(date +%Y-%m-%d)"
453 [ -n "$DISTRI_INFO" ]             || DISTRI_INFO='Grml - Live Linux for system administrators'
454 [ -n "$DISTRI_NAME" ]             || DISTRI_NAME="grml"
455 [ -n "$DISTRI_SPLASH" ]           || DISTRI_SPLASH='grml.png'
456 [ -n "$FORCE_ISO_REBUILD" ]       || FORCE_ISO_REBUILD="false"
457 [ -n "$GRML_FAI_CONFIG" ]         || GRML_FAI_CONFIG='/etc/grml/fai'
458 [ -n "$GRML_NAME" ]               || GRML_NAME='grml'
459 [ -n "$HOSTNAME" ]                || HOSTNAME='grml'
460 [ -n "$HYBRID_METHOD" ]           || HYBRID_METHOD='isohybrid'
461 [ -n "$RELEASENAME" ]             || RELEASENAME='grml-live rocks'
462 [ -n "$SECURE_BOOT" ]             || SECURE_BOOT='disable'
463 [ -n "$SQUASHFS_BINARY" ]         || SQUASHFS_BINARY='mksquashfs'
464 [ -n "$SQUASHFS_EXCLUDES_FILE" ]  || SQUASHFS_EXCLUDES_FILE="${GRML_FAI_CONFIG}/config/grml/squashfs-excludes"
465 [ -n "$SUITE" ]                   || SUITE='testing'
466 [ -n "$TEMPLATE_DIRECTORY" ]      || TEMPLATE_DIRECTORY='/usr/share/grml-live/templates'
467 [ -n "$SCRIPTS_DIRECTORY" ]       || SCRIPTS_DIRECTORY='/usr/share/grml-live/scripts'
468 [ -n "$USERNAME" ]                || USERNAME='grml'
469 [ -n "$VERSION" ]                 || VERSION='0.0.1'
470
471 # output specific stuff, depends on $OUTPUT (iff not set):
472 [ -n "$OUTPUT" ]           || OUTPUT="$PWD/grml/"
473 [ -n "$BUILD_OUTPUT" ]     || BUILD_OUTPUT="$OUTPUT/grml_cd"
474 [ -n "$CHROOT_OUTPUT" ]    || CHROOT_OUTPUT="$OUTPUT/grml_chroot"
475 [ -n "$ISO_OUTPUT" ]       || ISO_OUTPUT="$OUTPUT/grml_isos"
476 [ -n "$LOG_OUTPUT" ]       || LOG_OUTPUT="$OUTPUT/grml_logs"
477 [ -n "$REPORTS" ]          || REPORTS="${LOG_OUTPUT}/reports/"
478 [ -n "$NETBOOT" ]          || NETBOOT="${OUTPUT}/netboot/"
479 # }}}
480
481 # some misc checks before executing FAI {{{
482 [ -n "$CLASSES" ] || bailout 1 "Error: \$CLASSES unset, please set it in $LIVE_CONF or
483 specify it on the command line using the -c option."
484 [ -n "$OUTPUT" ] || bailout 1 "Error: \$OUTPUT unset, please set it in $LIVE_CONF or
485 specify it on the command line using the -o option."
486
487 # trim characters that are known to cause problems inside $GRML_NAME;
488 # for example isolinux does not like '-' inside the directory name
489 [ -n "$GRML_NAME" ] && export SHORT_NAME="$(echo $GRML_NAME | tr -d ',./;\- ')"
490
491 # export variables to have them available in fai scripts:
492 [ -n "$GRML_NAME" ]   && export GRML_NAME="$GRML_NAME"
493 [ -n "$RELEASENAME" ] && export RELEASENAME="$RELEASENAME"
494 # }}}
495
496 # ZERO_LOGFILE - check for backwards compatibility reasons {{{
497 # this was default behaviour until grml-live 0.9.34:
498 if [ -n "$ZERO_LOGFILE" ] ; then
499    PRESERVE_LOGFILE='' # make sure it's cleaned then
500    ewarn "Please consider disabling the \$ZERO_LOGFILE option as grml-live clears..."
501    ewarn "... the logfile $LOGFILE by default (unless \$PRESERVE_LOGFILE is set) nowadays."
502    eend 0
503 fi
504 # }}}
505
506 # ask user whether the setup is ok {{{
507 if [ -z "$FORCE" ] ; then
508    echo
509    echo "${PN} [${GRML_LIVE_VERSION}]: check your configuration (or use -F to force execution):"
510    echo
511    echo "  FAI classes:       $CLASSES"
512    [ -n "$LOCAL_CONFIG" ]        && echo "  Configuration:     $LOCAL_CONFIG"
513    [ -n "$GRML_FAI_CONFIG" ]     && echo "  Config directory:  $GRML_FAI_CONFIG"
514    echo "  main directory:    $OUTPUT"
515    [ -n "$EXTRACT_ISO_NAME" ]    && echo "  Extract ISO:       $EXTRACT_ISO_NAME"
516    [ -n "$CHROOT_OUTPUT" ]       && echo "  Chroot target:     $CHROOT_OUTPUT"
517    [ -n "$BUILD_OUTPUT" ]        && echo "  Build target:      $BUILD_OUTPUT"
518    [ -n "$ISO_OUTPUT" ]          && echo "  ISO target:        $ISO_OUTPUT"
519    [ -n "$GRML_NAME" ]           && echo "  Grml name:         $GRML_NAME"
520    [ -n "$RELEASENAME" ]         && echo "  Release name:      $RELEASENAME"
521    [ -n "$DATE" ]                && echo "  Build date:        $DATE"
522    [ -n "$VERSION" ]             && echo "  Grml version:      $VERSION"
523    [ -n "$SUITE" ]               && echo "  Debian suite:      $SUITE"
524    [ -n "$ARCH" ]                && echo "  Architecture:      $ARCH"
525    [ -n "$BOOT_METHOD" ]         && echo "  Boot method:       $BOOT_METHOD"
526    [ -n "$HYBRID_METHOD" ]       && echo "  Hybrid method:     $HYBRID_METHOD"
527    [ -n "$SECURE_BOOT" ]         && echo "  Secure Boot:       $SECURE_BOOT"
528    [ -n "$TEMPLATE_DIRECTORY" ]  && echo "  Template files:    $TEMPLATE_DIRECTORY"
529    [ -n "$CHROOT_INSTALL" ]      && echo "  Install files from directory to chroot:  $CHROOT_INSTALL"
530    [ -n "$BOOTID" ]              && echo "  Boot identifier:   $BOOTID"
531    [ -n "$NO_BOOTID" ]           && echo "  Skipping bootid feature."
532    [ -n "$CHOWN_USER" ]          && echo "  Output owner:      $CHOWN_USER"
533    [ -n "$DEFAULT_BOOTOPTIONS" ] && echo "  Adding default bootoptions: \"$DEFAULT_BOOTOPTIONS\""
534    [ -n "$FAI_ARGS" ]            && echo "  Additional arguments for FAI: $FAI_ARGS"
535    [ -n "$LOGFILE" ]             && echo "  Logging to file:   $LOGFILE"
536    [ -n "$SQUASHFS_ZLIB" ]       && echo "  Using ZLIB (instead of LZMA/XZ) compression."
537    [ -n "$SQUASHFS_OPTIONS" ]    && echo "  Using SQUASHFS_OPTIONS ${SQUASHFS_OPTIONS}"
538    [ -n "$VERBOSE" ]             && echo "  Using VERBOSE mode."
539    [ -n "$CLEAN_ARTIFACTS" ]     && echo "  Will clean output before and after running."
540    [ -n "$UPDATE" ]              && echo "  Executing UPDATE instead of fresh installation."
541    if [ -n "$BOOTSTRAP_ONLY" ] ; then
542      echo "  Bootstrapping only and not building (files for) ISO."
543    else
544      [ -n "$SKIP_MKSQUASHFS" ]     && echo "  Skipping creation of SQUASHFS file."
545      [ -n "$SKIP_NETBOOT" ]        && echo "  Skipping creation of NETBOOT package."
546      [ -n "$SKIP_MKISOFS" ]        && echo "  Skipping creation of ISO file."
547      [ -n "$BUILD_ONLY" ]          && echo "  Executing BUILD_ONLY instead of fresh installation or UPDATE."
548      [ -n "$BUILD_DIRTY" ]         && echo "  Executing BUILD_DIRTY to leave chroot untouched."
549    fi
550    echo
551    echo -n "Is this ok for you? [y/N] "
552    read a
553    if ! [ "$a" = 'y' -o "$a" = 'Y' ] ; then
554       CLEAN_ARTIFACTS=0
555       echo "Exiting as requested."
556       exit 0
557    fi
558    echo
559 fi
560 # }}}
561
562 # clean up before start {{{
563 if [ -n "${CLEAN_ARTIFACTS}" ]; then
564   echo "Wiping old artifacts"
565   [ -n "${CHROOT_OUTPUT}"  -a -d "${CHROOT_OUTPUT}"  ] && rm -r "${CHROOT_OUTPUT}"
566   [ -n "${BUILD_OUTPUT}"   -a -d "${BUILD_OUTPUT}"   ] && rm -r "${BUILD_OUTPUT}"
567   [ -n "${ISO_OUTPUT}"     -a -d "${ISO_OUTPUT}"     ] && rm -r "${ISO_OUTPUT}"
568   [ -n "${LOG_OUTPUT}"     -a -d "${LOG_OUTPUT}"     ] && rm -r "${LOG_OUTPUT}"
569   [ -n "${NETBOOT}"        -a -d "${NETBOOT}"        ] && rm -r "${NETBOOT}"
570 fi
571 # }}}
572
573 # create log file {{{
574 [ -n "$LOGFILE" ] || LOGFILE=${LOG_OUTPUT}/grml-live.log
575 mkdir -p $(dirname "${LOGFILE}")
576 touch $LOGFILE
577 chown root:adm $LOGFILE
578 chmod 664 $LOGFILE
579 # }}}
580
581 # clean/zero/remove logfiles {{{
582
583 if [ -n "$PRESERVE_LOGFILE" ] ; then
584    echo "Preserving logfile $LOGFILE as requested via \$PRESERVE_LOGFILE"
585 else
586    # make sure it is empty (as it is e.g. appended to grml-live-db)
587    echo -n > $LOGFILE
588 fi
589
590 if [ -n "$ZERO_FAI_LOGFILE" ] ; then
591    if [ -d /var/log/fai/"$HOSTNAME" ] ; then
592       rm -rf /var/log/fai/"$HOSTNAME"/"$(readlink /var/log/fai/"$HOSTNAME"/last)"
593       rm -rf /var/log/fai/"$HOSTNAME"/"$(readlink /var/log/fai/"$HOSTNAME"/last-dirinstall)"
594       rm -rf /var/log/fai/"$HOSTNAME"/"$(readlink /var/log/fai/"$HOSTNAME"/last-softupdate)"
595       rm -f /var/log/fai/"$HOSTNAME"/last \
596             /var/log/fai/"$HOSTNAME"/last-dirinstall \
597             /var/log/fai/"$HOSTNAME"/last-softupdate
598    fi
599 fi
600 # }}}
601
602 # source config and startup {{{
603 if [ -n "$CONFIG" ] ; then
604    if ! [ -f "$CONFIG" ] ; then
605       log    "Error: $CONFIG could not be read. Exiting. [$(date)]"
606       eerror "Error: $CONFIG could not be read. Exiting." ; eend 1
607       bailout 1
608    else
609       log "Sourcing $CONFIG"
610       . $CONFIG
611    fi
612 fi
613
614 SECONDS=unknown
615 start_seconds="$(date +%s)"
616 log "------------------------------------------------------------------------------"
617 log "Starting grml-live [${GRML_LIVE_VERSION}] run on $(date)"
618 log "Using local config file: $LOCAL_CONFIG"
619 log "Executed grml-live command line:"
620 log "$CMDLINE"
621
622 einfo "Logging actions to logfile $LOGFILE"
623 # }}}
624
625 # dump config variables into file, for script access {{{
626 CONFIGDUMP=$(mktemp)
627 set | grep -E \
628   '^(GRML_NAME|RELEASENAME|DATE|VERSION|SUITE|ARCH|DISTRI_NAME|USERNAME|HOSTNAME|APT_PROXY)=' \
629   > ${CONFIGDUMP}
630 # }}}
631
632 # unpack iso/squashfs {{{
633 extract_iso() {
634 if [ -n "$EXTRACT_ISO_NAME" ]; then
635   log "Unpacking ISO from ${EXTRACT_ISO_NAME}"
636   einfo "Unpacking ISO from ${EXTRACT_ISO_NAME}"
637   local mountpoint=$(mktemp -d)
638   local rc=0
639   mount -o loop "${EXTRACT_ISO_NAME}" "$mountpoint" ; rc=$?
640   if [ "$rc" != 0 ]; then
641     rmdir "$mountpoint"
642     log "mount failed"
643     eerror "mount failed"
644     eend 1
645     bailout 1
646   fi
647
648   if ls "${mountpoint}"/live/*/*.squashfs 2>/dev/null | grep -q . ; then # ISOs >=2011.12
649     log "Using ${mountpoint}/live/*/*.squashfs for unsquashfs"
650     unsquashfs -d "${CHROOT_OUTPUT}" "${mountpoint}"/live/*/*.squashfs ; rc=$?
651   elif ls "${mountpoint}"/live/*.squashfs 2>/dev/null | grep -q . ; then # ISOs before 2011.12
652     log "Using ${mountpoint}/live/*.squashfs for unsquashfs"
653     unsquashfs -d "${CHROOT_OUTPUT}" "${mountpoint}"/live/*.squashfs ; rc=$?
654   else
655     log "Error: Could not find any *.squashfs files on the ISO"
656     eerror "Error: Could not find any *.squashfs files on the ISO"
657     eend 1
658     bailout 1
659   fi
660
661   umount "$mountpoint"
662   rmdir "$mountpoint"
663   if [ "$rc" != 0 ]; then
664     log "unsquashfs failed"
665     eerror "unsquashfs failed"
666     eend 1
667     bailout 1
668   fi
669 fi
670 }
671 extract_iso
672 # }}}
673
674 # on-the-fly configuration {{{
675
676 # does this suck? YES!
677 # /usr/share/debootstrap/scripts/unstable does not exist, instead use 'sid':
678 case $SUITE in
679    unstable) SUITE='sid' ; CLASSES="DEBIAN_UNSTABLE,$CLASSES" ;;
680    *) CLASSES="DEBIAN_$(echo $SUITE | tr 'a-z' 'A-Z'),$CLASSES";;
681 esac
682 export SUITE # make sure it's available in FAI scripts
683
684 # validate whether the specified architecture class matches the
685 # architecture (option), otherwise installation of kernel will fail
686 if echo $CLASSES | grep -qw I386 ; then
687    if ! [[ "$ARCH" == "i386" ]] ; then
688       log    "Error: You specified the I386 class but are trying to build something else (AMD64?)."
689       eerror "Error: You specified the I386 class but are trying to build something else (AMD64?)."
690       eerror "Tip:   Either invoke grml-live with '-a i386' or adjust the architecture class. Exiting."
691       eend 1
692       bailout
693    fi
694 elif echo $CLASSES | grep -qi amd64 ; then
695    if ! [[ "$ARCH" == "amd64" ]] ; then
696       log    "Error: You specified the AMD64 class but are trying to build something else (I386?)."
697       eerror "Error: You specified the AMD64 class but are trying to build something else (I386?)."
698       eerror "Tip:   Either invoke grml-live with '-a amd64' or adjust the architecture class. Exiting."
699       eend 1
700       bailout
701    fi
702 fi
703
704 # generate nfsroot configuration for FAI on the fly
705 if [ -z "$FAI_DEBOOTSTRAP" ] ; then
706   if [ -n "$WAYBACK_DATE" ] ; then
707     FAI_DEBOOTSTRAP="$SUITE http://snapshot.debian.org/archive/debian/$WAYBACK_DATE/"
708   else
709     FAI_DEBOOTSTRAP="$SUITE http://ftp.debian.org/debian"
710   fi
711 fi
712
713 if [ -z "$FAI_DEBOOTSTRAP_OPTS" ] ; then
714   FAI_DEBOOTSTRAP_OPTS="--exclude=info,tasksel,tasksel-data,isc-dhcp-client,isc-dhcp-common --include=aptitude --arch $ARCH"
715 fi
716
717 echo "# This is an automatically generated file by grml-live.
718 # Do NOT edit this file, your changes will be lost.
719 FAI_DEBOOTSTRAP=\"$FAI_DEBOOTSTRAP\"
720 FAI_DEBOOTSTRAP_OPTS=\"$FAI_DEBOOTSTRAP_OPTS\"
721 # EOF " > "${GRML_FAI_CONFIG}/nfsroot.conf"
722 # }}}
723
724 # CHROOT_OUTPUT - execute FAI {{{
725 if [ -n "$BUILD_DIRTY" ]; then
726    log   "Skipping stage 'fai' as requested via option -B"
727    ewarn "Skipping stage 'fai' as requested via option -B" ; eend 0
728 else
729    [ -n "$CHROOT_OUTPUT" ] || CHROOT_OUTPUT="$OUTPUT/grml_chroot"
730
731    if [ -n "$UPDATE" -o -n "$BUILD_ONLY" ] ; then
732       FAI_ACTION=softupdate
733    else
734       FAI_ACTION=dirinstall
735    fi
736
737    if [ -n "$UPDATE" -o -n "$BUILD_ONLY" ] ; then
738       if ! [ -r "$CHROOT_OUTPUT/etc/debian_version" ] ; then
739          log    "Error: does not look like you have a working chroot. Updating/building not possible."
740          eerror "Error: does not look like you have a working chroot. Updating/building not possible. (Drop -u/-b option?)"
741          eend 1
742          bailout 20
743       fi
744    fi
745
746    if [ -d "$CHROOT_OUTPUT/bin" -a -z "$UPDATE" -a -z "$BUILD_ONLY" ] ; then
747       log   "Skipping stage 'fai dirinstall' as $CHROOT_OUTPUT exists already."
748       ewarn "Skipping stage 'fai dirinstall' as $CHROOT_OUTPUT exists already." ; eend 0
749    else
750       mkdir -p "$CHROOT_OUTPUT" || bailout 5 "Problem with creating $CHROOT_OUTPUT for FAI"
751
752       if [ -n "${MIRROR_DIRECTORY}" ] ; then
753          mkdir -p "${CHROOT_OUTPUT}/${MIRROR_DIRECTORY}"
754          mount --bind "${MIRROR_DIRECTORY}" "${CHROOT_OUTPUT}/${MIRROR_DIRECTORY}"
755       fi
756
757       mkdir -p "${OUTPUT}/grml_sources/" "${CHROOT_OUTPUT}/grml-live/sources/"
758       mount --bind "${OUTPUT}/grml_sources/" "${CHROOT_OUTPUT}/grml-live/sources/"
759
760       log "Executed FAI command line:"
761       log "BUILD_ONLY=$BUILD_ONLY BOOTSTRAP_ONLY=$BOOTSTRAP_ONLY GRML_LIVE_CONFIG=$CONFIGDUMP WAYBACK_DATE=$WAYBACK_DATE fai $VERBOSE -C $GRML_FAI_CONFIG -s file:///$GRML_FAI_CONFIG/config -c$CLASSES -u $HOSTNAME $FAI_ACTION $CHROOT_OUTPUT $FAI_ARGS"
762       BUILD_ONLY="$BUILD_ONLY" BOOTSTRAP_ONLY="$BOOTSTRAP_ONLY" GRML_LIVE_CONFIG="$CONFIGDUMP" fai $VERBOSE \
763                   -C "$GRML_FAI_CONFIG" -s "file:///$GRML_FAI_CONFIG/config" -c"$CLASSES" \
764                   -u "$HOSTNAME" "$FAI_ACTION" "$CHROOT_OUTPUT" $FAI_ARGS | tee -a $LOGFILE
765       RC="$PIPESTATUS" # notice: bash-only
766
767       if [ "$RC" != 0 ] ; then
768         store_logfiles  # ensure to have logfiles available even if building failed
769         log    "Error: critical error while executing fai [exit code ${RC}]. Exiting."
770         eerror "Error: critical error while executing fai [exit code ${RC}]. Exiting." ; eend 1
771         bailout 1
772       fi
773
774       # provide inform fai about the ISO we build, needs to be provided
775       # *after* FAI stage, otherwise FAI skips the debootstrap stage if
776       # there is not BASEFILE (as it checks for presence of /etc) :(
777       echo '# This file has been generated by grml-live.' > "$CHROOT_OUTPUT/etc/grml_live_version"
778       [ -n "$GRML_LIVE_VERSION" ] && echo "GRML_LIVE_VERSION=$GRML_LIVE_VERSION" >> "$CHROOT_OUTPUT/etc/grml_live_version"
779       [ -n "$SUITE" ] && echo "SUITE=$SUITE" >> "$CHROOT_OUTPUT/etc/grml_live_version"
780
781       FORCE_ISO_REBUILD=true
782
783       store_logfiles
784
785       umount_all
786
787       # notice: 'fai dirinstall' does not seem to exit appropriate, so:
788       ERROR=''
789       CHECKLOG="$LOG_OUTPUT"/fai/
790       if [ -r "$CHECKLOG/software.log" ] ; then
791          # 1 errors during executing of commands
792          grep 'dpkg: error processing' $CHECKLOG/software.log >> $LOGFILE && ERROR=1
793          grep 'E: Method http has died unexpectedly!' $CHECKLOG/software.log >> $LOGFILE && ERROR=2
794          grep 'ERROR: chroot' $CHECKLOG/software.log >> $LOGFILE && ERROR=3
795          grep 'E: Failed to fetch' $CHECKLOG/software.log >> $LOGFILE && ERROR=4
796          grep 'Unable to write mmap - msync (28 No space left on device)' $CHECKLOG/software.log >> $LOGFILE && ERROR=5
797       fi
798
799       # FAI versions <6.0 used to write to shell.log
800       if [ -r "$CHECKLOG/shell.log" ] ; then
801          grep 'FAILED with exit code' $CHECKLOG/shell.log >> $LOGFILE && ERROR=6
802       fi
803
804       # FAI versions >=6.0 always writes to scripts.log
805       if [ -r "$CHECKLOG/scripts.log" ] ; then
806          grep 'FAILED with exit code' $CHECKLOG/shell.log >> $LOGFILE && ERROR=6
807       fi
808
809       if [ -r "$CHECKLOG/fai.log" ] ; then
810         grep 'updatebase.*FAILED with exit code' "$CHECKLOG/fai.log" >> "$LOGFILE" && ERROR=7
811         grep 'instsoft.*FAILED with exit code'   "$CHECKLOG/fai.log" >> "$LOGFILE" && ERROR=8
812       fi
813
814       if [ -n "$ERROR" ] ; then
815          log    "Error: there was a critical error [${ERROR}] during execution of stage 'fai dirinstall' [$(date)]"
816          eerror "Error: there was a critical error during execution of stage 'fai dirinstall'"
817          eerror "Note:  check out ${CHECKLOG}/ for details. [exit ${ERROR}]"
818          eend 1
819          bailout 1
820       else
821          log "Finished execution of stage 'fai dirinstall' [$(date)]"
822          einfo "Finished execution of stage 'fai dirinstall'"
823       fi
824    fi
825 fi # BUILD_DIRTY?
826 # }}}
827
828 # package validator {{{
829 CHECKLOG=/var/log/fai/$HOSTNAME/last
830 if [ -r "$CHECKLOG/dpkg.selections" ] ; then
831   package_count=$(wc -l "$CHECKLOG/dpkg.selections" | awk '{print $1}')
832 else
833   package_count="unknown"
834 fi
835
836 mkdir -p "$REPORTS"
837 REPORT_MISSING_PACKAGES="${REPORTS}/TEST-MissingPackages.xml"
838
839 # check for missing packages
840 if ! [ -s "$CHECKLOG/package_errors.log" ] ; then
841   einfo "No missing packages found, generating empty junit report."
842
843   cat > "${REPORT_MISSING_PACKAGES}" << EOF
844 <?xml version="1.0" encoding="UTF-8"?>
845 <testsuite name="grml-live-missing-packages" tests="${package_count}" time="1" failures="0" errors="0" skipped="0" assertions="0">
846   <testcase name="test_missing_packages" time="0" assertions="0">
847   </testcase>
848   <system-out>
849   </system-out>
850   <system-err>
851   </system-err>
852 </testsuite>
853 EOF
854   eend 0
855 else
856   einfo "Missing packages found, generating junit report."
857
858   if [ -r "$CHECKLOG/package_errors.log" ] ; then
859     package_errors=$(wc -l "$CHECKLOG/package_errors.log" | awk '{print $1}')
860   else
861     package_errors="unknown"
862   fi
863
864   mkdir -p "$REPORTS"
865   REPORT_MISSING_PACKAGES="${REPORTS}/TEST-MissingPackages.xml"
866
867   cat > "${REPORT_MISSING_PACKAGES}" << EOF
868 <?xml version="1.0" encoding="UTF-8"?>
869 <testsuite name="grml-live-missing-packages" tests="${package_count}" time="1" failures="${package_errors}" errors="${package_errors}" skipped="0" assertions="0">
870 EOF
871
872   for package in $(awk '{print $1}' "${CHECKLOG}/package_errors.log" | sed 's;/;\\/;') ; do
873     failure_reason="$(awk "/$package/ {print \$2}" "${CHECKLOG}/package_errors.log")"
874     cat >> "${REPORT_MISSING_PACKAGES}" << EOF
875   <testcase name="test_missing_packages_${package}" time="0" assertions="0">
876     <failure type="${failure_reason}" message="Package ${package} is missing">
877 Package $package is missing in chroot (${failure_reason})
878   </failure>
879   </testcase>
880 EOF
881   done
882
883   cat >> "${REPORT_MISSING_PACKAGES}" << EOF
884   <system-out>
885   </system-out>
886   <system-err>
887   </system-err>
888 </testsuite>
889 EOF
890   eend 0
891
892   if [ -n "$EXIT_ON_MISSING_PACKAGES" -a -z "$BUILD_DIRTY" ] ; then
893     eerror "The following packages were requested for installation but could not be processed:"
894     cat "$CHECKLOG/package_errors.log"
895     eerror "... exiting as requested via \$EXIT_ON_MISSING_PACKAGES."
896     eend 1
897     bailout 13
898   else
899     ewarn "The following packages were requested for installation but could not be processed:"
900     cat "$CHECKLOG/package_errors.log"
901     eend 0
902   fi
903 fi
904 # }}}
905
906 # grub boot {{{
907 grub_setup() {
908   BOOTX64="/boot/bootx64.efi"
909   BOOTX32="/boot/bootia32.efi"
910   EFI_IMG="/boot/efi.img"
911
912   local efi_size
913   if [[ "${SECURE_BOOT:-}" == "disable" ]] || [[ "${ARCH:-}" == "i386" ]] ; then
914     efi_size='4M'
915   else
916     # e.g. templates/EFI/debian for Secure Boot has >4MB and needs more space
917     efi_size='8M'
918   fi
919
920   if [[ "$ARCH" == "amd64" ]] ; then
921     # important: this depends on execution of ${GRML_FAI_CONFIG}/config/scripts/GRMLBASE/45-grub-images
922     if ! [ -r "${CHROOT_OUTPUT}/${BOOTX64}" ] ; then
923       log    "Can not access GRUB efi image ${CHROOT_OUTPUT}/${BOOTX64}, required for Secure Boot support"
924       eerror "Can not access GRUB efi image ${CHROOT_OUTPUT}/${BOOTX64}, required for Secure Boot support" ; eend 1
925       log    "Possible reason is failure to run ${GRML_FAI_CONFIG}/config/scripts/GRMLBASE/45-grub-images"
926       ewarn  "Possible reason is failure to run ${GRML_FAI_CONFIG}/config/scripts/GRMLBASE/45-grub-images"
927       bailout 50
928     fi
929
930     dd if=/dev/zero of="${CHROOT_OUTPUT}/${EFI_IMG}" bs="${efi_size}" count=1 2>/dev/null || bailout 50
931     mkfs.vfat -n GRML "${CHROOT_OUTPUT}/${EFI_IMG}" >/dev/null || bailout 51
932     mmd -i "${CHROOT_OUTPUT}/${EFI_IMG}" ::EFI || bailout 52
933     mmd -i "${CHROOT_OUTPUT}/${EFI_IMG}" ::EFI/BOOT || bailout 52
934
935     if [ "${SECURE_BOOT:-}" = "disable" ] ; then
936       log   "Secure Boot is disabled."
937       einfo "Secure Boot is disabled." ; eend 0
938
939       # install "$BOOTX64" as ::EFI/BOOT/bootx64.efi inside image file "$EFI_IMG":
940       mcopy -i "${CHROOT_OUTPUT}/${EFI_IMG}" "${CHROOT_OUTPUT}/${BOOTX64}" ::EFI/BOOT/bootx64.efi >/dev/null || bailout 53
941
942       log   "Generated 64-bit EFI image $BOOTX64"
943       einfo "Generated 64-bit EFI image $BOOTX64" ; eend 0
944     else
945       case "${SECURE_BOOT}" in
946         disable*)
947           log   "Secure Boot is disabled [mode: ${SECURE_BOOT}]"
948           einfo "Secure Boot is disabled [mode: ${SECURE_BOOT}]" ; eend 0
949           ;;
950         debian|ubuntu)
951           log   "Secure Boot is enabled [mode: ${SECURE_BOOT}]"
952           einfo "Secure Boot is enabled [mode: ${SECURE_BOOT}]" ; eend 0
953
954           local GRUBCFG_TEMPLATE="${TEMPLATE_DIRECTORY}/secureboot/grub.cfg"
955           local GRUBCFG_TMP=$(mktemp)
956
957           if ! [ -r "${GRUBCFG_TEMPLATE}" ] ; then
958             log    "Secure Boot template for GRUB [${GRUBCFG_TEMPLATE}] not found."
959             eerror "Secure Boot template for GRUB [${GRUBCFG_TEMPLATE}] not found." ; eend 1
960             bailout 54
961           fi
962
963           cp "${GRUBCFG_TEMPLATE}" "${GRUBCFG_TMP}"
964           adjust_boot_files "${GRUBCFG_TMP}"
965
966           mmd -i "${CHROOT_OUTPUT}/${EFI_IMG}" ::boot      || bailout 55
967           mmd -i "${CHROOT_OUTPUT}/${EFI_IMG}" ::boot/grub || bailout 55
968           mcopy -i "${CHROOT_OUTPUT}/${EFI_IMG}" "${GRUBCFG_TMP}" ::boot/grub/grub.cfg || bailout 56
969
970           rm "${GRUBCFG_TMP}"
971
972           if [ -r "${TEMPLATE_DIRECTORY}/EFI/${SECURE_BOOT}/BOOT/grubx64.efi.signed" ] ; then
973             mcopy -i "${CHROOT_OUTPUT}/${EFI_IMG}" "${TEMPLATE_DIRECTORY}/EFI/${SECURE_BOOT}/BOOT/grubx64.efi.signed" ::EFI/BOOT/grubx64.efi >/dev/null || bailout 57
974           else
975             log    "Secure Boot GRUB binary '${TEMPLATE_DIRECTORY}/EFI/${SECURE_BOOT}/BOOT/grubx64.efi.signed' not found."
976             eerror "Secure Boot GRUB binary '${TEMPLATE_DIRECTORY}/EFI/${SECURE_BOOT}/BOOT/grubx64.efi.signed' not found." ; eend 1
977             bailout 57
978           fi
979
980           if [ -r "${TEMPLATE_DIRECTORY}/EFI/${SECURE_BOOT}/BOOT/shimx64.efi.signed" ] ; then
981             mcopy -i "${CHROOT_OUTPUT}/${EFI_IMG}" "${TEMPLATE_DIRECTORY}/EFI/${SECURE_BOOT}/BOOT/shimx64.efi.signed" ::EFI/BOOT/bootx64.efi >/dev/null || bailout 58
982           else
983             log    "Secure Boot GRUB binary '${TEMPLATE_DIRECTORY}/EFI/${SECURE_BOOT}/BOOT/shimx64.efi.signed' not found."
984             eerror "Secure Boot GRUB binary '${TEMPLATE_DIRECTORY}/EFI/${SECURE_BOOT}/BOOT/shimx64.efi.signed' not found." ; eend 1
985             bailout 57
986           fi
987
988           log   "Generated 64-bit Secure Boot (${SECURE_BOOT}) EFI image ${CHROOT_OUTPUT}/${EFI_IMG}"
989           einfo "Generated 64-bit Secure Boot (${SECURE_BOOT}) EFI image ${CHROOT_OUTPUT}/${EFI_IMG}" ; eend 0
990           ;;
991         *)
992           log   "Secure Boot method '${SECURE_BOOT}' is unsupported."
993           eerror "Secure Boot method '${SECURE_BOOT}' is unsupported." ; eend 1
994           bailout 59
995           ;;
996       esac
997     fi
998   fi
999
1000   if [[ "$ARCH" == "i386" ]] ; then
1001     if ! [ -r "${CHROOT_OUTPUT}/${BOOTX32}" ] ; then
1002       log    "Can not access GRUB efi image ${CHROOT_OUTPUT}/${BOOTX32}."
1003       eerror "Can not access GRUB efi image ${CHROOT_OUTPUT}/${BOOTX32}." ; eend 1
1004       log    "Possible reason is failure to run ${GRML_FAI_CONFIG}/config/scripts/GRMLBASE/45-grub-images"
1005       ewarn "Possible reason is failure to run ${GRML_FAI_CONFIG}/config/scripts/GRMLBASE/45-grub-images"
1006       bailout 50
1007     fi
1008
1009     dd if=/dev/zero of="${CHROOT_OUTPUT}/${EFI_IMG}" bs="${efi_size}" count=1 2>/dev/null || bailout 50
1010     mkfs.vfat -n GRML "${CHROOT_OUTPUT}/${EFI_IMG}" >/dev/null || bailout 51
1011     mmd -i "${CHROOT_OUTPUT}/${EFI_IMG}" ::EFI || bailout 52
1012     mmd -i "${CHROOT_OUTPUT}/${EFI_IMG}" ::EFI/BOOT || bailout 52
1013     mcopy -i "${CHROOT_OUTPUT}/${EFI_IMG}" "${CHROOT_OUTPUT}/${BOOTX32}" ::EFI/BOOT/bootia32.efi >/dev/null || bailout 53
1014     log   "Generated 32-bit EFI image $BOOTX32"
1015     einfo "Generated 32-bit EFI image $BOOTX32" ; eend 0
1016   fi
1017 }
1018 # }}}
1019
1020 # BUILD_OUTPUT - execute arch specific stuff and squashfs {{{
1021 [ -n "$BUILD_OUTPUT" ] || BUILD_OUTPUT="$OUTPUT/grml_cd"
1022 mkdir -p "$BUILD_OUTPUT" || bailout 6 "Problem with creating $BUILD_OUTPUT for stage ARCH"
1023
1024 # prepare ISO
1025 if [ "$ARCH" = i386 ] || [ "$ARCH" = amd64 ] ; then
1026   if [ -n "$BOOTSTRAP_ONLY" ] ; then
1027     log   "Skipping stage 'boot' as building with bootstrap only."
1028     ewarn "Skipping stage 'boot' as building with bootstrap only." ; eend 0
1029   else
1030     # booting stuff:
1031     mkdir -p "$BUILD_OUTPUT"/boot/isolinux
1032     mkdir -p "$BUILD_OUTPUT"/boot/"${SHORT_NAME}"
1033
1034     # this is a variable we're using for adjusting boot templates, not only in
1035     # adjust_boot_files though, so set here
1036     RELEASE_INFO="$GRML_NAME $VERSION - Release Codename $RELEASENAME"
1037
1038     # if we don't have an initrd we a) can't boot and b) there was an error
1039     # during build, so check for the file:
1040     INITRD="$(ls $CHROOT_OUTPUT/boot/initrd* 2>/dev/null| grep -v '.bak$' | sort -r | head -1)"
1041     if [ -n "$INITRD" ] ; then
1042       cp $INITRD "$BUILD_OUTPUT"/boot/"${SHORT_NAME}"/initrd.img
1043       find $CHROOT_OUTPUT/boot/ -name initrd\*.bak -exec rm {} \;
1044     else
1045       log    "Error: No initrd found inside $CHROOT_OUTPUT/boot/ - Exiting"
1046       eerror "Error: No initrd found inside $CHROOT_OUTPUT/boot/ - Exiting" ; eend 1
1047       bailout 10
1048     fi
1049
1050     KERNEL_IMAGE="$(ls $CHROOT_OUTPUT/boot/vmlinuz* 2>/dev/null | sort -r | head -1)"
1051     if [ -n "$KERNEL_IMAGE" ] ; then
1052       cp "$KERNEL_IMAGE" "$BUILD_OUTPUT"/boot/"${SHORT_NAME}"/vmlinuz
1053     else
1054       log    "Error: No kernel found inside $CHROOT_OUTPUT/boot/ - Exiting"
1055       eerror "Error: No kernel found inside $CHROOT_OUTPUT/boot/ - Exiting" ; eend 1
1056       bailout 11
1057     fi
1058
1059     # we need to set "$BOOTID" before we invoke adjust_boot_files for the
1060     # first time, being inside grub_setup below
1061     if [ -n "$NO_BOOTID" ] ; then
1062       log   'Skipping bootid feature as requested via $NO_BOOTID.'
1063       einfo 'Skipping bootid feature as requested via $NO_BOOTID.'
1064     else
1065       [ -n "$BOOTID" ] || BOOTID="$(echo ${GRML_NAME}${VERSION} | tr -d ',./;\- ')"
1066       mkdir -p "$BUILD_OUTPUT"/conf
1067       einfo "Generating /conf/bootid.txt with entry ${BOOTID}."
1068       log   "Generating /conf/bootid.txt with entry ${BOOTID}."
1069       echo "$BOOTID" > "$BUILD_OUTPUT"/conf/bootid.txt
1070       eend $?
1071     fi
1072
1073     # every recent Grml ISO ships a /conf/bootid.txt, though GRUB might find
1074     # the /conf/bootid.txt of a different (Grml) ISO than the one that's
1075     # supposed to be running, so within scripts/GRMLBASE/45-grub-images
1076     # we generate a random filename, stored inside /boot/grub/bootfile.txt,
1077     # which we place on the resulting ISO here
1078     if [ -r "${CHROOT_OUTPUT}"/boot/grub/bootfile.txt ] ; then
1079       mkdir -p "${BUILD_OUTPUT}"/conf
1080       rm -f "${BUILD_OUTPUT}"/conf/bootfile*  # ensure we don't leave any old(er) files behind
1081
1082       einfo "Generating "${BUILD_OUTPUT}"/conf/bootfile* files"
1083       log   "Generating "${BUILD_OUTPUT}"/conf/bootfile* files"
1084
1085       BOOT_FILE="/conf/bootfile_$(cat "${CHROOT_OUTPUT}"/boot/grub/bootfile.txt)"
1086       echo "# This file is relevant for GRUB boot with the Grml ISO." > "${BUILD_OUTPUT}/${BOOT_FILE}"
1087       # save information about the random filename inside /conf/bootfile.txt
1088       echo "${BOOT_FILE}" > "${BUILD_OUTPUT}"/conf/bootfile.txt
1089       eend $?
1090     fi
1091
1092     grub_setup
1093
1094     # EFI boot files
1095     if [ -r "${CHROOT_OUTPUT}/boot/efi.img" -a -r "${CHROOT_OUTPUT}/boot/bootx64.efi" ] ; then
1096       einfo "Copying 64-bit EFI boot files into ISO path."
1097       log   "Copying 64-bit EFI boot files into ISO path."
1098       RC=$0
1099       cp "${CHROOT_OUTPUT}/boot/efi.img" "${BUILD_OUTPUT}/boot/" || RC=$?
1100       mkdir -p "${BUILD_OUTPUT}/EFI/BOOT/" || RC=$?
1101       cp "${CHROOT_OUTPUT}/boot/bootx64.efi" "${BUILD_OUTPUT}/EFI/BOOT/bootx64.efi" || RC=$?
1102       eend $?
1103     elif [ -r "${CHROOT_OUTPUT}/boot/efi.img" -a -r "${CHROOT_OUTPUT}/boot/bootia32.efi" ] ; then
1104       einfo "Copying 32-bit EFI boot files into ISO path."
1105       log "Copying 32-bit EFI boot files into ISO path."
1106       RC=$0
1107       cp "${CHROOT_OUTPUT}/boot/efi.img" "${BUILD_OUTPUT}/boot/" || RC=$?
1108       mkdir -p "${BUILD_OUTPUT}/EFI/BOOT/" || RC=$?
1109       cp "${CHROOT_OUTPUT}/boot/bootia32.efi" "${BUILD_OUTPUT}/EFI/BOOT/bootia32.efi" || RC=$?
1110       eend $?
1111     else
1112       ewarn "No EFI boot files found, skipping." ; eend 0
1113     fi
1114
1115     [ -n "$TEMPLATE_DIRECTORY" ] || TEMPLATE_DIRECTORY='/usr/share/grml-live/templates'
1116     if ! [ -d "${TEMPLATE_DIRECTORY}"/boot ] ; then
1117       log    "Error: ${TEMPLATE_DIRECTORY}/boot does not exist. Exiting."
1118       eerror "Error: ${TEMPLATE_DIRECTORY}/boot does not exist. Exiting." ; eend 1
1119       bailout 8
1120     fi
1121
1122     # copy _required_ isolinux files
1123     if [ -d "${CHROOT_OUTPUT}/usr/lib/ISOLINUX" ] ; then
1124       copy_addon_file isolinux.bin /usr/lib/ISOLINUX isolinux
1125       for file in ${CHROOT_OUTPUT}/usr/lib/syslinux/modules/bios/*.c32 ; do
1126         copy_addon_file "$(basename "$file")"  /usr/lib/syslinux/modules/bios/ isolinux
1127       done
1128     else # syslinux versions <= 3:4.05+dfsg-6+deb8u1
1129       copy_addon_file isolinux.bin /usr/lib/syslinux isolinux
1130       copy_addon_file ifcpu64.c32  /usr/lib/syslinux isolinux
1131       copy_addon_file vesamenu.c32 /usr/lib/syslinux isolinux
1132     fi
1133
1134     # *always* copy files to output directory so the variables
1135     # get adjusted according to the build.
1136     cp ${TEMPLATE_DIRECTORY}/boot/isolinux/*  "$BUILD_OUTPUT"/boot/isolinux/
1137
1138     mkdir -p "${BUILD_OUTPUT}/boot/grub"
1139     cp -a ${TEMPLATE_DIRECTORY}/boot/grub/* "$BUILD_OUTPUT"/boot/grub/
1140
1141     if [ -n "$NO_ADDONS" ] ; then
1142       rm -f "$BUILD_OUTPUT"/boot/grub/addons.cfg
1143       log   "Skipping installation of boot addons as requested via \$NO_ADDONS."
1144       einfo "Skipping installation of boot addons as requested via \$NO_ADDONS."; eend 0
1145     else
1146       if ! [ -r "$TEMPLATE_DIRECTORY"/boot/addons ] ; then
1147         log   "Boot addons not found, skipping therefore. (Consider installing package grml-live-addons)"
1148         ewarn "Boot addons not found, skipping therefore. (Consider installing package grml-live-addons)" ; eend 0
1149       else
1150         log   "Installing boot addons."
1151         einfo "Installing boot addons."
1152
1153         # copy addons from system packages or grml-live-addons
1154         copy_addon_file ipxe.lkrn /usr/lib/ipxe addons
1155         copy_addon_file ipxe.efi /usr/lib/ipxe addons
1156         copy_addon_file pci.ids /usr/share/misc addons
1157
1158         # memtest86+ <=5.01-3.1
1159         copy_addon_file memtest86+.bin /boot addons
1160         # make memtest filename FAT16/8.3 compatible
1161         if [ -r "${BUILD_OUTPUT}/boot/addons/memtest86+.bin" ] ; then
1162           mv "${BUILD_OUTPUT}/boot/addons/memtest86+.bin" \
1163              "${BUILD_OUTPUT}/boot/addons/memtest"
1164         fi
1165
1166         # memtest86+ >=6.00-1
1167         copy_addon_file memtest86+x32.bin /boot addons
1168         copy_addon_file memtest86+x32.efi /boot addons
1169         copy_addon_file memtest86+x64.bin /boot addons
1170         copy_addon_file memtest86+x64.efi /boot addons
1171
1172         # provide memtest86+ >=6.00-1 files as "memtest" file
1173         # for BIOS boot in isolinux/syslinux
1174         if ! [ -r "${BUILD_OUTPUT}/boot/addons/memtest" ] ; then
1175           if [[ "$ARCH" == "amd64" ]] ; then
1176             copy_addon_file memtest86+x64.bin /boot addons
1177             mv "${BUILD_OUTPUT}/boot/addons/memtest86+x64.bin" \
1178                "${BUILD_OUTPUT}/boot/addons/memtest"
1179           elif [[ "$ARCH" == "i386" ]] ; then
1180             copy_addon_file memtest86+x32.bin /boot addons
1181             mv "${BUILD_OUTPUT}/boot/addons/memtest86+x32.bin" \
1182                "${BUILD_OUTPUT}/boot/addons/memtest"
1183           fi
1184         fi
1185
1186         # since syslinux(-common) v3:6.03~pre1+dfsg-4 the files are in a
1187         # different directory :(
1188         if [ -d "${CHROOT_OUTPUT}/usr/lib/syslinux/modules/bios/" ] ; then
1189           syslinux_modules_dir=/usr/lib/syslinux/modules/bios/
1190         else
1191           syslinux_modules_dir=/usr/lib/syslinux
1192         fi
1193         for file in chain.c32 hdt.c32 mboot.c32 menu.c32; do
1194           copy_addon_file "${file}" "${syslinux_modules_dir}" addons
1195         done
1196
1197         copy_addon_file memdisk /usr/lib/syslinux addons
1198
1199         # copy only files so we can handle bsd4grml on its own
1200         for file in ${TEMPLATE_DIRECTORY}/boot/addons/* ; do
1201           test -f $file && cp $file "$BUILD_OUTPUT"/boot/addons/
1202         done
1203
1204         eend 0
1205
1206         if [ -n "$NO_ADDONS_BSD4GRML" ] ; then
1207           log   "Skipping installation of bsd4grml as requested via \$NO_ADDONS_BSD4GRML."
1208           einfo "Skipping installation of bsd4grml as requested via \$NO_ADDONS_BSD4GRML."; eend 0
1209         else
1210           if [ -d "$TEMPLATE_DIRECTORY"/boot/addons/bsd4grml ] ; then
1211             cp -a ${TEMPLATE_DIRECTORY}/boot/addons/bsd4grml "$BUILD_OUTPUT"/boot/addons/
1212           else
1213             log   "Missing addon file: bsd4grml"
1214             ewarn "Missing addon file: bsd4grml" ; eend 0
1215           fi
1216         fi
1217
1218       fi # no "$TEMPLATE_DIRECTORY"/boot/addons
1219     fi # NO_ADDONS
1220
1221     # generate loopback.cfg config file without depending on grub's regexp module
1222     # which isn't available in Debian/squeeze
1223     echo "## grub2 loopback configuration" > "${BUILD_OUTPUT}"/boot/grub/loopback.cfg
1224     echo "source /boot/grub/header.cfg" >> "${BUILD_OUTPUT}"/boot/grub/loopback.cfg
1225     for config in "${BUILD_OUTPUT}"/boot/grub/*_default.cfg "${BUILD_OUTPUT}"/boot/grub/*_options.cfg ; do
1226       [ -r "$config" ] || continue
1227       echo "source ${config##$BUILD_OUTPUT}" >> "${BUILD_OUTPUT}"/boot/grub/loopback.cfg
1228     done
1229     if [ -z "$NO_ADDONS" ] ; then
1230       echo "source /boot/grub/addons.cfg" >> "${BUILD_OUTPUT}"/boot/grub/loopback.cfg
1231     fi
1232     echo "source /boot/grub/footer.cfg" >> "${BUILD_OUTPUT}"/boot/grub/loopback.cfg
1233
1234     # copy grub files from target
1235     mkdir -p "${BUILD_OUTPUT}"/boot/grub/i386-pc/
1236     cp -a "${CHROOT_OUTPUT}"/usr/lib/grub/*-pc/*.mod "${BUILD_OUTPUT}"/boot/grub/i386-pc/
1237     cp -a "${CHROOT_OUTPUT}"/usr/lib/grub/*-pc/*.o "${BUILD_OUTPUT}"/boot/grub/i386-pc/
1238     cp -a "${CHROOT_OUTPUT}"/usr/lib/grub/*-pc/*.lst "${BUILD_OUTPUT}"/boot/grub/i386-pc/
1239     cp -a "${CHROOT_OUTPUT}"/usr/share/grub/ascii.pf2 "${BUILD_OUTPUT}"/boot/grub/
1240     cp -a "${CHROOT_OUTPUT}"/boot/grub/core.img "${BUILD_OUTPUT}"/boot/grub/
1241     cp -a "${CHROOT_OUTPUT}"/boot/grub/grub.img "${BUILD_OUTPUT}"/boot/grub/
1242
1243     # copy modules for UEFI grub, 64-bit
1244     mkdir -p "${BUILD_OUTPUT}"/boot/grub/x86_64-efi/
1245     cp -a "${CHROOT_OUTPUT}"/usr/lib/grub/x86_64-efi/*.{mod,lst} "${BUILD_OUTPUT}"/boot/grub/x86_64-efi/
1246
1247     # copy modules for UEFI grub, 32-bit
1248     mkdir -p "${BUILD_OUTPUT}"/boot/grub/i386-efi/
1249     cp -a "${CHROOT_OUTPUT}"/usr/lib/grub/i386-efi/*.{mod,lst} "${BUILD_OUTPUT}"/boot/grub/i386-efi/
1250
1251     if ! [ -d "${TEMPLATE_DIRECTORY}"/GRML ] ; then
1252       log    "Error: ${TEMPLATE_DIRECTORY}/GRML does not exist. Exiting."
1253       eerror "Error: ${TEMPLATE_DIRECTORY}/GRML does not exist. Exiting." ; eend 1
1254       bailout 9
1255     fi
1256
1257     mkdir -p "$BUILD_OUTPUT"/GRML/"${GRML_NAME}"/
1258     cp -a ${TEMPLATE_DIRECTORY}/GRML/* "$BUILD_OUTPUT"/GRML/"${GRML_NAME}"/
1259
1260     if [ -r "$BUILD_OUTPUT"/GRML/"${GRML_NAME}"/grml-version ] ; then
1261       sed -i "s/%RELEASE_INFO%/$RELEASE_INFO/" "$BUILD_OUTPUT"/GRML/"${GRML_NAME}"/grml-version
1262       sed -i "s/%DATE%/$DATE/"                 "$BUILD_OUTPUT"/GRML/"${GRML_NAME}"/grml-version
1263     fi
1264
1265     # make sure the squashfs filename is set accordingly:
1266     SQUASHFS_NAME="$GRML_NAME.squashfs"
1267     # adjust bootsplash accordingly but make sure the string has the according length
1268     fixed_squashfs_name="$(cut_string 20 "$SQUASHFS_NAME")"
1269     fixed_squashfs_name="$(extend_string_end 20 "$fixed_squashfs_name")"
1270     for file in f4 f5 ; do
1271       if [ -r "${BUILD_OUTPUT}/boot/isolinux/${file}" ] ; then
1272         sed -i "s/%SQUASHFS_NAME%/${fixed_squashfs_name}/" "${BUILD_OUTPUT}/boot/isolinux/${file}"
1273         sed -i "s/%SQUASHFS_NAME%/${fixed_squashfs_name}/" "${BUILD_OUTPUT}/boot/isolinux/${file}"
1274       fi
1275     done
1276
1277     # adjust all variables in the templates with the according distribution information
1278     adjust_boot_files "${BUILD_OUTPUT}"/boot/isolinux/*.cfg \
1279       "${BUILD_OUTPUT}"/boot/isolinux/*.msg \
1280       "${BUILD_OUTPUT}"/boot/grub/*
1281
1282     for param in ARCH DATE DISTRI_INFO DISTRI_NAME DISTRI_SPLASH GRML_NAME SQUASHFS_NAME \
1283       RELEASE_INFO SHORT_NAME VERSION ; do
1284       for file in $(find "${BUILD_OUTPUT}" -name "*%$param%*") ; do
1285         value="$(eval echo '$'"$param")"
1286         mv ${file} ${file/\%${param}\%/$value}
1287       done
1288     done
1289
1290     # generate addon list
1291     rm -f "${BUILD_OUTPUT}/${ADDONS_LIST_FILE}"
1292     for name in "${BUILD_OUTPUT}"/boot/isolinux/addon_*.cfg ; do
1293       include_name=$(basename "$name")
1294       echo "include $include_name"  >> "${BUILD_OUTPUT}/${ADDONS_LIST_FILE}"
1295     done
1296
1297     if ! [ -r "${BUILD_OUTPUT}/boot/isolinux/${DISTRI_NAME}.cfg" ] || [ "$DISTRI_NAME" = "grml" ] ; then
1298       log "including grmlmain.cfg in ${BUILD_OUTPUT}/boot/isolinux/distri.cfg"
1299       echo "include grmlmain.cfg"    >  "${BUILD_OUTPUT}/boot/isolinux/distri.cfg"
1300       echo "include default.cfg"     >  "${BUILD_OUTPUT}/boot/isolinux/grmlmain.cfg"
1301       echo "include menuoptions.cfg" >> "${BUILD_OUTPUT}/boot/isolinux/grmlmain.cfg"
1302       echo "include grml.cfg"        >> "${BUILD_OUTPUT}/boot/isolinux/grmlmain.cfg"
1303
1304       for f in "${BUILD_OUTPUT}"/boot/isolinux/submenu*.cfg ; do
1305         echo "include $(basename $f)"     >> "${BUILD_OUTPUT}/boot/isolinux/grmlmain.cfg"
1306       done
1307
1308       echo "include options.cfg"     >> "${BUILD_OUTPUT}/boot/isolinux/grmlmain.cfg"
1309       if [ -z "$NO_ADDONS" ] ; then
1310         echo "include addons.cfg"    >> "${BUILD_OUTPUT}/boot/isolinux/grmlmain.cfg"
1311       fi
1312       echo "include isoprompt.cfg"   >> "${BUILD_OUTPUT}/boot/isolinux/grmlmain.cfg"
1313       echo "include hd.cfg"          >> "${BUILD_OUTPUT}/boot/isolinux/grmlmain.cfg"
1314       echo "include hidden.cfg"      >> "${BUILD_OUTPUT}/boot/isolinux/grmlmain.cfg"
1315     else # assume we are building a custom distribution:
1316       log "File ${BUILD_OUTPUT}/boot/isolinux/${DISTRI_NAME}.cfg found, using it."
1317       einfo "File ${BUILD_OUTPUT}/boot/isolinux/${DISTRI_NAME}.cfg found, using it."
1318       if grep -q "^include ${DISTRI_NAME}.cfg" "${BUILD_OUTPUT}/boot/isolinux/distri.cfg" ; then
1319         log "include for ${DISTRI_NAME}.cfg already present, nothing to do."
1320         eindent
1321         einfo "include for ${DISTRI_NAME}.cfg already present, nothing to do."
1322         eoutdent
1323         eend $?
1324       else
1325         log "including ${DISTRI_NAME}.cfg in ${BUILD_OUTPUT}/boot/isolinux/distri.cfg"
1326         echo "include ${DISTRI_NAME}.cfg" > "${BUILD_OUTPUT}/boot/isolinux/distri.cfg"
1327         if [ -z "$NO_ADDONS" ] ; then
1328           echo "include addons.cfg" >> "${BUILD_OUTPUT}/boot/isolinux/distri.cfg"
1329         fi
1330       fi
1331     fi
1332
1333     # use old style console based isolinux method only if requested:
1334     if [[ "${ISOLINUX_METHOD}" == "console" ]] ; then
1335       log 'Using console based isolinux method as requested via $ISOLINUX_METHOD.'
1336       einfo 'Using console based isolinux method as requested via $ISOLINUX_METHOD.'
1337       if grep -q '^include console.cfg' "${BUILD_OUTPUT}/boot/isolinux/distri.cfg" ; then
1338         einfo "include for console.cfg already found, nothing to do."
1339         eend 0
1340       else
1341         log "including console.cfg in ${BUILD_OUTPUT}/boot/isolinux/isolinux.cfg"
1342         einfo "including console.cfg in ${BUILD_OUTPUT}/boot/isolinux/isolinux.cfg"
1343         echo "include console.cfg" >> "${BUILD_OUTPUT}/boot/isolinux/isolinux.cfg"
1344         eend $?
1345       fi
1346     else
1347       log 'Using graphical boot menu.'
1348       if grep -q '^include vesamenu.cfg' "${BUILD_OUTPUT}/boot/isolinux/isolinux.cfg" ; then
1349         log "include for vesamenu.cfg already found, nothing to do."
1350       else
1351         log "including vesamenu.cfg in ${BUILD_OUTPUT}/boot/isolinux/isolinux.cfg"
1352         echo "include vesamenu.cfg" >> "${BUILD_OUTPUT}/boot/isolinux/isolinux.cfg"
1353       fi
1354     fi
1355
1356     if [ -e "$BUILD_OUTPUT"/boot/addons/bsd4grml/boot.6 ]; then
1357       sed -i "s/%RELEASE_INFO%/$RELEASE_INFO/" "$BUILD_OUTPUT"/boot/addons/bsd4grml/boot.6
1358     fi
1359
1360     DPKG_LIST="/var/log/fai/$HOSTNAME/last/dpkg.list" # the dpkg --list output of the chroot
1361     if ! [ -r "$DPKG_LIST" ] ; then
1362       ewarn "$DPKG_LIST could not be read, ignoring to store package information on ISO therefore."
1363     else
1364       einfo "Storing package list information as /GRML/${GRML_NAME}/packages.txt on ISO."
1365       cp "$DPKG_LIST" "${BUILD_OUTPUT}"/GRML/"${GRML_NAME}"/packages.txt
1366       eend $?
1367     fi
1368
1369     # autostart for Windows:
1370     if [ -d "${TEMPLATE_DIRECTORY}/windows/autostart/" ] ; then
1371       cp ${TEMPLATE_DIRECTORY}/windows/autostart/* "$BUILD_OUTPUT"/
1372     fi
1373
1374     FORCE_ISO_REBUILD=true
1375     einfo "Finished execution of stage 'boot'" ; eend 0
1376   fi # BOOTSTRAP_ONLY
1377 else
1378   log    'Error: Unsupported ARCH, sorry. Want to support it? Contribute!'
1379   eerror 'Error: Unsupported ARCH, sorry. Want to support it? Contribute!' ; eend 1
1380   bailout
1381 fi
1382
1383 # support installation of local files into the chroot/ISO
1384 if [ -n "$CHROOT_INSTALL" ] ; then
1385   if ! [ -d "$CHROOT_INSTALL" ] ; then
1386      log "Configuration variable \$CHROOT_INSTALL is set but not a directory; ignoring"
1387      ewarn "Configuration variable \$CHROOT_INSTALL is set but not a directory; ignoring"
1388   else
1389      log "Copying local files to chroot as requested via \$CHROOT_INSTALL"
1390      einfo "Copying local files to chroot as requested via \$CHROOT_INSTALL"
1391      rsync -avz --inplace "$CHROOT_INSTALL"/ "$CHROOT_OUTPUT/"
1392      eend $?
1393      einfo "Make sure to run squashfs stage, otherwise your local files won't be part of the ISO."
1394      FORCE_ISO_REBUILD=true
1395   fi
1396 fi
1397
1398 if [ -f "$BUILD_OUTPUT"/live/${GRML_NAME}.squashfs -a -z "$UPDATE" -a -z "$BUILD_ONLY" -a -z "$BUILD_DIRTY" ] ; then
1399    log   "Skipping stage 'squashfs' as $BUILD_OUTPUT/live exists already."
1400    ewarn "Skipping stage 'squashfs' as $BUILD_OUTPUT/live exists already." ; eend 0
1401 elif [ -n "$SKIP_MKSQUASHFS" ] ; then
1402    log   "Skipping stage 'squashfs' as requested via option -q or -N"
1403    ewarn "Skipping stage 'squashfs' as requested via option -q or -N" ; eend 0
1404 else
1405    mkdir -p "$BUILD_OUTPUT"/live/"${GRML_NAME}"/
1406    # make sure we don't leave (even an empty) base.tgz:
1407    [ -f "$CHROOT_OUTPUT/base.tgz" ] && rm -f "$CHROOT_OUTPUT/base.tgz"
1408
1409    if which "$SQUASHFS_BINARY" >/dev/null 2>&1 ; then
1410       log    "Using mksquashfs binary ${SQUASHFS_BINARY}"
1411       einfo  "Using mksquashfs binary ${SQUASHFS_BINARY}" ; eend 0
1412    else
1413       log    "Error: mksquashfs binary ($SQUASHFS_BINARY) not found. Exiting."
1414       eerror "Error: mksquashfs binary ($SQUASHFS_BINARY) not found. Exiting." ; eend 1
1415       bailout
1416    fi
1417
1418    # use sane defaults if $SQUASHFS_OPTIONS isn't set
1419    if [ -z "$SQUASHFS_OPTIONS" ] ; then
1420      # use block size 1m as this gives good result with regards to time + compression
1421      SQUASHFS_OPTIONS="-b 1m"
1422
1423      # set lzma/xz compression by default, unless -z option has been specified on command line
1424      if [ -z "$SQUASHFS_ZLIB" ] ; then
1425         SQUASHFS_OPTIONS="$SQUASHFS_OPTIONS -comp xz"
1426      else
1427         SQUASHFS_OPTIONS="$SQUASHFS_OPTIONS -comp gzip"
1428      fi
1429    fi
1430
1431    # support exclusion of files via exclude-file:
1432    if [ -n "$SQUASHFS_EXCLUDES_FILE" -a "$SQUASHFS_EXCLUDES_FILE" ] ; then
1433       SQUASHFS_OPTIONS="$SQUASHFS_OPTIONS -ef $SQUASHFS_EXCLUDES_FILE -wildcards"
1434    fi
1435
1436    # get rid of unnecessary files when building grml-small for final release:
1437    if echo "$CLASSES" | grep -q GRML_SMALL ; then
1438       SQUASHFS_OPTIONS="$SQUASHFS_OPTIONS -e initrd.img* vmlinuz*"
1439    fi
1440
1441    # log stuff
1442    SQUASHFS_STDERR="$(mktemp -t grml-live.XXXXXX)"
1443
1444    # informational stuff
1445    [ -n "$SQUASHFS_OPTIONS" ]  && SQUASHFS_INFO_MSG="$SQUASHFS_OPTIONS"
1446    [ -n "$SQUASHFS_INFO_MSG" ] && SQUASHFS_INFO_MSG="using options: $SQUASHFS_INFO_MSG"
1447    einfo "Squashfs build information: running binary $SQUASHFS_BINARY $SQUASHFS_INFO_MSG"
1448
1449    log "$SQUASHFS_BINARY $CHROOT_OUTPUT/ $BUILD_OUTPUT/live/${GRML_NAME}/${GRML_NAME}.squashfs -noappend $SQUASHFS_OPTIONS"
1450
1451    if $SQUASHFS_BINARY $CHROOT_OUTPUT/ $BUILD_OUTPUT/live/"${GRML_NAME}"/"${GRML_NAME}".squashfs \
1452       -noappend $SQUASHFS_OPTIONS 2>"${SQUASHFS_STDERR}" ; then
1453       echo "${GRML_NAME}.squashfs" > $BUILD_OUTPUT/live/"${GRML_NAME}"/filesystem.module
1454       log "Finished execution of stage 'squashfs' [$(date)]"
1455       einfo "Finished execution of stage 'squashfs'" ; eend 0
1456    else
1457       log    "Error: there was a critical error executing stage 'squashfs' [$(date)]:"
1458       log    "$(cat $SQUASHFS_STDERR)"
1459       eerror "Error: there was a critical error executing stage 'squashfs':"
1460       cat    "${SQUASHFS_STDERR}"
1461       eend 1
1462       bailout
1463    fi
1464
1465    FORCE_ISO_REBUILD=true
1466 fi
1467
1468 # create md5sum file:
1469 if [ -z "$BOOTSTRAP_ONLY" ] ; then
1470   ( cd $BUILD_OUTPUT/GRML/"${GRML_NAME}" &&
1471   find ../.. -type f -not -name md5sums -not -name isolinux.bin -exec md5sum {} \; > md5sums )
1472 fi
1473 # }}}
1474
1475 # information how the ISO was generated {{{
1476 # shellcheck disable=SC2034
1477 generate_build_info() {
1478   jo -p \
1479     boot_method="${BOOT_METHOD}" \
1480     bootstrap_only="${BOOTSTRAP_ONLY}" \
1481     build_date="${DATE}" \
1482     build_dirty="${BUILD_DIRTY}" \
1483     build_only="${BUILD_ONLY}" \
1484     chroot_install="${CHROOT_INSTALL}" \
1485     classes="${CLASSES}" \
1486     clean_artifacts="${CLEAN_ARTIFACTS}" \
1487     default_bootoptions="${DEFAULT_BOOTOPTIONS}" \
1488     distri_info="${DISTRI_INFO}" \
1489     distri_name="${DISTRI_NAME}" \
1490     extract_iso_name="${EXTRACT_ISO_NAME}" \
1491     fai_cmdline="BUILD_ONLY=${BUILD_ONLY} BOOTSTRAP_ONLY=${BOOTSTRAP_ONLY} GRML_LIVE_CONFIG=${CONFIGDUMP} WAYBACK_DATE=${WAYBACK_DATE} fai ${VERBOSE} -C ${GRML_FAI_CONFIG} -s file:///${GRML_FAI_CONFIG}/config -c${CLASSES} -u ${HOSTNAME} ${FAI_ACTION} ${CHROOT_OUTPUT} ${FAI_ARGS}" \
1492     fai_version="$(fai --help 2>/dev/null | head -1 | awk '{print $2}' | sed 's/\.$//' || true)" \
1493     grml_architecture="${ARCH}" \
1494     grml_bootid="${BOOTID}" \
1495     grml_build_output="${BUILD_OUTPUT}" \
1496     grml_chroot_output="${CHROOT_OUTPUT}" \
1497     grml_debian_version="${SUITE}" \
1498     grml_iso_name="${ISO_NAME}" \
1499     grml_iso_output="${ISO_OUTPUT}" \
1500     grml_live_cmdline="${CMDLINE}" \
1501     grml_live_config_file="${LIVE_CONF}" \
1502     grml_live_scripts_directory="${SCRIPTS_DIRECTORY}" \
1503     grml_live_template_directory="${TEMPLATE_DIRECTORY}" \
1504     grml_live_version="${GRML_LIVE_VERSION}" \
1505     grml_local_config="${LOCAL_CONFIG}" \
1506     grml_name="${GRML_NAME}" \
1507     grml_short_name="${SHORT_NAME}" \
1508     grml_username="${USERNAME}" \
1509     grml_version="${VERSION}" \
1510     host_architecture="$(dpkg --print-architecture || true)" \
1511     host_debian_version="$(cat /etc/debian_version 2>/dev/null || true)" \
1512     host_kernel_version="$(uname -a)" \
1513     hybrid_method="${HYBRID_METHOD}" \
1514     mkisofs_cmdline="${MKISOFS} -V ${GRML_NAME} ${VERSION} -publisher 'grml-live | grml.org' -l -r -J ${BOOT_ARGS} ${EFI_ARGS} -no-pad -o ${ISO_OUTPUT}/${ISO_NAME}" \
1515     mkisofs_version="$(${MKISOFS} --version 2>/dev/null | head -1 || true)" \
1516     mksquashfs_cmdline="${SQUASHFS_BINARY} ${CHROOT_OUTPUT}/ ${BUILD_OUTPUT}/live/${GRML_NAME}/${GRML_NAME}.squashfs -noappend ${SQUASHFS_OPTIONS}" \
1517     mksquashfs_version="$(${SQUASHFS_BINARY} -version | head -1 || true)" \
1518     output_owner="${CHOWN_USER}" \
1519     release_info="${RELEASE_INFO}" \
1520     release_name="${RELEASENAME}" \
1521     secure_boot="${SECURE_BOOT}" \
1522     skip_mkisofs="${SKIP_MKISOFS}" \
1523     skip_mksquashfs_="${SKIP_MKSQUASHFS}" \
1524     skip_netboot="${SKIP_NETBOOT}" \
1525     squashfs_name="${SQUASHFS_NAME}" \
1526     template_directory="${TEMPLATE_DIRECTORY}" \
1527     timestamp="$(TZ=UTC date +%s)" \
1528     update_only="${UPDATE}" \
1529     wayback_date="${WAYBACK_DATE}" \
1530   --
1531 }
1532 # }}}
1533
1534 # ISO_OUTPUT - mkisofs {{{
1535 [ -n "$ISO_OUTPUT" ] || ISO_OUTPUT="$OUTPUT/grml_isos"
1536 [ -n "$ISO_NAME" ] || ISO_NAME="${GRML_NAME}_${VERSION}.iso"
1537
1538 if [ "$BOOT_METHOD" = "isolinux" ] ; then
1539    BOOT_ARGS="-no-emul-boot -boot-load-size 4 -boot-info-table -b boot/isolinux/isolinux.bin -c boot/isolinux/boot.cat"
1540    if [ "$HYBRID_METHOD" = "isohybrid" ] ; then
1541      EFI_ARGS="-isohybrid-mbr /usr/lib/ISOLINUX/isohdpfx.bin -eltorito-alt-boot -e boot/efi.img -no-emul-boot -isohybrid-gpt-basdat"
1542    fi
1543 elif [ "$BOOT_METHOD" = "grub2" ] ; then
1544    BOOT_ARGS="-no-emul-boot -boot-load-size 4 -b boot/grub/toriboot.bin"
1545 fi
1546
1547 # Work around http://bts.grml.org/grml/issue945
1548 if [[ $BOOT_METHOD != isolinux && ($HYBRID_METHOD = isohybrid || $HYBRID_METHOD = manifold) ]]; then
1549   log   "Setting HYBRID_METHOD to grub2 as hybrid mode does not work with isohybrid yet."
1550   ewarn "Setting HYBRID_METHOD to grub2 as hybrid mode does not work with isohybrid yet."
1551   HYBRID_METHOD='grub2'
1552   eend 0
1553 fi
1554
1555 if [ -f "${ISO_OUTPUT}/${ISO_NAME}" -a -z "$UPDATE" -a -z "$BUILD_ONLY" -a -z "$BUILD_DIRTY" -a "$FORCE_ISO_REBUILD" = "false" ]  ; then
1556    log   "Skipping stage 'iso build' as $ISO_OUTPUT/${ISO_NAME} exists already."
1557    ewarn "Skipping stage 'iso build' as $ISO_OUTPUT/${ISO_NAME} exists already." ; eend 0
1558 elif [ -n "$SKIP_MKISOFS" ] ; then
1559    log   "Skipping stage 'iso build' as requested via option -n or -N"
1560    ewarn "Skipping stage 'iso build' as requested via option -n or -N" ; eend 0
1561 else
1562    mkdir -p "$ISO_OUTPUT" || bailout 6 "Problem with creating $ISO_OUTPUT for stage 'iso build'"
1563
1564    if $FORCE_ISO_REBUILD && ! [ -f "${ISO_OUTPUT}/${ISO_NAME}" ] ; then
1565       log   "Forcing rebuild of ISO because files on ISO have been modified."
1566       einfo "Forcing rebuild of ISO because files on ISO have been modified."
1567    fi
1568
1569    # support xorriso as well mkisofs and genisoimage
1570    if which xorriso >/dev/null 2>&1 ; then
1571       MKISOFS='xorriso -as mkisofs'
1572     elif which mkisofs >/dev/null 2>&1; then
1573       MKISOFS='mkisofs'
1574    elif which genisoimage >/dev/null 2>&1; then
1575       MKISOFS='genisoimage'
1576    else
1577       log    "Error: neither xorriso nor mkisofs nor genisoimage available - can not create ISO."
1578       eerror "Error: neither xorriso nor mkisofs nor genisoimage available - can not create ISO." ; eend 1
1579       bailout
1580    fi
1581
1582    einfo "Using ${MKISOFS} to build ISO." ;  eend 0
1583    case "${ARCH}-${MKISOFS}" in
1584      # using -eltorito-alt-boot is limited to xorriso for now
1585      amd64-xorriso*)
1586        eindent
1587
1588        if ! dpkg --compare-versions $(dpkg-query -W -f='${Version}\n' xorriso 2>/dev/null) gt-nl 1.1.6-1 ; then
1589          log   "Disabling (U)EFI boot support because xorriso version is too old."
1590          ewarn "Disabling (U)EFI boot support because xorriso version is too old." ; eend 0
1591        else
1592          if [ -r "${BUILD_OUTPUT}"/boot/efi.img ] ; then
1593            einfo "Enabling (U)EFI boot."
1594            log   "Enabling (U)EFI boot."
1595            BOOT_ARGS="$BOOT_ARGS -boot-info-table -eltorito-alt-boot -e boot/efi.img -no-emul-boot"
1596            eend $?
1597          else
1598            log   "Disabling (U)EFI boot support because /boot/efi.img is missing."
1599            ewarn "Disabling (U)EFI boot support because /boot/efi.img is missing." ; eend 0
1600          fi
1601        fi
1602
1603        eoutdent
1604        ;;
1605    esac
1606
1607    CURRENT_DIR=$(pwd)
1608    if cd "$BUILD_OUTPUT" ; then
1609       if [ "$BOOT_METHOD" = "grub2" ]; then
1610          # make a 2048-byte bootsector for El Torito
1611          dd if=/dev/zero of=boot/grub/toriboot.bin bs=512 count=4 2>/dev/null
1612          # those are in 2048-byte sectors, so 1 16 matches 4 63 below
1613          echo 1 16 | mksh "${SCRIPTS_DIRECTORY}/bootgrub.mksh" -B 11 | \
1614             dd of=boot/grub/toriboot.bin conv=notrunc 2>/dev/null
1615       fi
1616
1617       log   "Generating build information in conf/buildinfo.json"
1618       einfo "Generating build information in conf/buildinfo.json"
1619       mkdir -p conf/
1620       generate_build_info > conf/buildinfo.json
1621       eend $?
1622
1623       log "$MKISOFS -V '${GRML_NAME} ${VERSION}' -publisher 'grml-live | grml.org' -l -r -J $BOOT_ARGS $EFI_ARGS -no-pad -o ${ISO_OUTPUT}/${ISO_NAME} ."
1624       einfo "Generating ISO file..."
1625       $MKISOFS -V "${GRML_NAME} ${VERSION}" -publisher 'grml-live | grml.org' \
1626               -l -r -J $BOOT_ARGS $EFI_ARGS -no-pad \
1627               -o "${ISO_OUTPUT}/${ISO_NAME}" . ; RC=$?
1628       eend $RC
1629
1630       # do not continue on errors, otherwise we might generate/overwrite the ISO with dd if=... stuff
1631       if [ "$RC" != 0 ] ; then
1632         log    "Error: critical error while generating ISO [exit code ${RC}]. Exiting."
1633         eerror "Error: critical error while generating ISO [exit code ${RC}]. Exiting." ; eend 1
1634         bailout $RC
1635       fi
1636
1637       # both of these need core.img there, so it’s easier to write it here
1638       if [ "$BOOT_METHOD" = "grub2" ] || [ "$HYBRID_METHOD" = "grub2" ]; then
1639          # must be <= 30720 bytes
1640          dd if=boot/grub/core.img of="${ISO_OUTPUT}/${ISO_NAME}" \
1641            conv=notrunc bs=512 seek=4 2>/dev/null
1642       fi
1643
1644       # pad the output ISO to multiples of 256 KiB for partition table support
1645       siz=$($getfilesize "${ISO_OUTPUT}/${ISO_NAME}")
1646       cyls=$((siz / 512 / 32 / 16 + 1))   # C=$cyls H=16 S=32
1647       siz=$((cyls * 16 * 32 * 512))   # size after padding
1648       dd if=/dev/zero bs=1 count=1 seek=$((siz - 1)) \
1649          of="${ISO_OUTPUT}/${ISO_NAME}" 2>/dev/null
1650
1651       # support disabling hybrid ISO image
1652       if [ "$HYBRID_METHOD" = "disable" ] ; then
1653         log   "Skipping creation of hybrid ISO file as requested via HYBRID_METHOD=disable"
1654         einfo "Skipping creation of hybrid ISO file as requested via HYBRID_METHOD=disable"
1655         eend 0
1656       elif [ "$HYBRID_METHOD" = "manifold" ] || [ "$HYBRID_METHOD" = "grub2" ] ; then
1657         # isoinfo is part of both mkisofs and genisoimage so we're good
1658         bootoff=$(isoinfo -l -i "${ISO_OUTPUT}/${ISO_NAME}" | \
1659           sed -n '/^.*\[ *\([0-9]*\)[] ].* ISOLINUX.BIN[;1]* *$/s//\1/p')
1660
1661         if ! [ -r boot/grub/core.img ] ; then
1662           log   "boot/grub/core.img not found, not creating manifold boot ISO file"
1663           ewarn "boot/grub/core.img not found, not creating manifold boot ISO file"
1664         elif [ "${bootoff:-0}" -lt 1 ] ; then
1665           log   "isolinux.bin not found on the ISO file, disabling manifold boot"
1666           ewarn "isolinux.bin not found on the ISO file, disabling manifold boot"
1667         else
1668           if [ "$HYBRID_METHOD" = "grub2" ] ; then
1669             log   "Creating hybrid ISO file with manifold/grub2 method"
1670             einfo "Creating hybrid ISO file with manifold/grub2 method"
1671             # 512 bytes: MBR, partition table, load GRUB 2
1672             echo 4 63 | mksh "${SCRIPTS_DIRECTORY}/bootgrub.mksh" -A -M 4:0x96 -g $cyls:16:32
1673           else
1674             log   "Creating hybrid ISO file with manifold method"
1675             einfo "Creating hybrid ISO file with manifold method"
1676             # read only one but 2048-byte sized (scale: << 2) sector
1677             echo $bootoff $bootoff | \
1678               mksh ${SCRIPTS_DIRECTORY}/bootilnx.mksh -A -M 4:0x96 -g $cyls:16:32 -S 2
1679           fi | dd of="${ISO_OUTPUT}/${ISO_NAME}" conv=notrunc 2>/dev/null
1680           eend $?
1681         fi
1682       elif [ "$HYBRID_METHOD" = "isohybrid" ] ; then
1683         : # nothing to do, handled via $MKISOFS $EFI_ARGS already
1684       else
1685         bailout 12 "Unknown HYBRID_METHOD [${HYBRID_METHOD}]. Supported values: disable, isohybrid, grub2, manifold"
1686       fi
1687
1688       # generate ISO checksums if we are using class 'RELEASE':
1689       case $CLASSES in *RELEASE*)
1690          [ "$RC" = 0 ] && \
1691          (
1692            if cd $ISO_OUTPUT ; then
1693              sha256sum ${ISO_NAME} > ${ISO_NAME}.sha256 && \
1694              touch -r ${ISO_NAME} ${ISO_NAME}.sha256
1695            fi
1696          )
1697          ;;
1698       esac
1699
1700       cd "$CURRENT_DIR"
1701    fi
1702
1703    if [ "$RC" = 0 ] ; then
1704       log   "Finished execution of stage 'iso build' [$(date)]"
1705       einfo "Finished execution of stage 'iso build'" ; eend 0
1706    else
1707       log    "Error: there was a critical error ($RC) executing stage 'iso build' [$(date)]"
1708       eerror "Error: there was a critical error executing stage 'iso build'" ; eend 1
1709       bailout $RC
1710    fi
1711 fi
1712 # }}}
1713
1714 # netboot package {{{
1715 create_netbootpackage() {
1716   local OUTPUT_FILE="${NETBOOT}/grml_netboot_package_${GRML_NAME}_${VERSION}.tar"
1717
1718   if [ -f "${OUTPUT_FILE}" -a -z "$UPDATE" -a -z "$BUILD_ONLY" -a -z "$BUILD_DIRTY" ] ; then
1719     log   "Skipping stage 'netboot' as $OUTPUT_FILE exists already."
1720     ewarn "Skipping stage 'netboot' as $OUTPUT_FILE exists already." ; eend 0
1721     return 0
1722   elif [ -n "$SKIP_NETBOOT" ] ; then
1723     log   "Skipping stage 'netboot' as requested via option -Q"
1724     ewarn "Skipping stage 'netboot' as requested via option -Q" ; eend 0
1725     return 0
1726   fi
1727
1728   mkdir -p "$NETBOOT"
1729
1730   # since syslinux v3:6.03~pre1+dfsg-4 the pxelinux.0 has been split into a
1731   # separate pxelinux package
1732   if [ -d "${CHROOT_OUTPUT}/usr/lib/PXELINUX/" ] ; then
1733     local pxelinux_dir=/usr/lib/PXELINUX
1734   else
1735     local pxelinux_dir=/usr/lib/syslinux
1736   fi
1737
1738   if ! [ -r "${CHROOT_OUTPUT}/${pxelinux_dir}/pxelinux.0" ] ; then
1739     ewarn "File ${pxelinux_dir}/pxelinux.0 not found in build chroot." ; eend 0
1740     eindent
1741     einfo "Install syslinux[-common]/pxelinux package in chroot to get a netboot package."
1742     eoutdent
1743     return 0
1744   fi
1745
1746   local OUTPUTDIR="${NETBOOT}/build_tmp"
1747   local WORKING_DIR="${OUTPUTDIR}/grml_netboot_package_${GRML_NAME}_${VERSION}/tftpboot/"
1748
1749   mkdir -p "$WORKING_DIR"
1750
1751   cp "${CHROOT_OUTPUT}"/boot/vmlinuz-*    "$WORKING_DIR"/vmlinuz
1752   cp "${CHROOT_OUTPUT}"/boot/initrd.img-* "$WORKING_DIR"/initrd.img
1753   cp "${CHROOT_OUTPUT}/${pxelinux_dir}/pxelinux.0" "${WORKING_DIR}/pxelinux.0"
1754
1755   if [ -r "${CHROOT_OUTPUT}"/usr/lib/syslinux/modules/bios/ldlinux.c32 ] ; then
1756     cp "${CHROOT_OUTPUT}"/usr/lib/syslinux/modules/bios/ldlinux.c32 "${WORKING_DIR}"/
1757   fi
1758
1759   mkdir -p "${WORKING_DIR}/pxelinux.cfg"
1760   if [ -r "${BUILD_OUTPUT}/boot/isolinux/netboot.cfg" ] ; then
1761     cp "${BUILD_OUTPUT}/boot/isolinux/netboot.cfg" "${WORKING_DIR}/pxelinux.cfg/default"
1762   else
1763     log   "File ${BUILD_OUTPUT}/boot/isolinux/netboot.cfg not found."
1764     ewarn "File ${BUILD_OUTPUT}/boot/isolinux/netboot.cfg not found."
1765     eindent
1766     log   "Hint: Are you using custom templates which do not provide netboot.cfg?"
1767     ewarn "Hint: Are you using custom templates which do not provide netboot.cfg?" ; eend 0
1768     eoutdent
1769   fi
1770
1771   # don't include shim + grubnetx64 + grub files in i386 netboot packages,
1772   # as those don't make much sense there
1773   if [ "$ARCH" = amd64 ] ; then
1774     if ! [ -r "${BUILD_OUTPUT}/boot/grub/netboot.cfg" ] ; then
1775       log   "File ${BUILD_OUTPUT}/boot/grub/netboot.cfg not found."
1776       ewarn "File ${BUILD_OUTPUT}/boot/grub/netboot.cfg not found."
1777       eindent
1778       log   "Hint: Are you using custom templates which do not provide grub.cfg?"
1779       ewarn "Hint: Are you using custom templates which do not provide grub.cfg?" ; eend 0
1780       eoutdent
1781     else
1782       cp "${BUILD_OUTPUT}/boot/grub/netboot.cfg" "${WORKING_DIR}/grub.cfg"
1783       adjust_boot_files "${WORKING_DIR}/grub.cfg"
1784
1785       if [ -r "${CHROOT_OUTPUT}"/usr/lib/shim/shimx64.efi.signed ] ; then
1786         log "Installing ${CHROOT_OUTPUT}/usr/lib/shim/shimx64.efi.signed as shim.efi in netboot package"
1787         cp "${CHROOT_OUTPUT}"/usr/lib/shim/shimx64.efi.signed "${WORKING_DIR}"/shim.efi
1788       elif [ -r "${CHROOT_OUTPUT}"/usr/lib/shim/shimx64.efi ] ; then
1789         log "Installing ${CHROOT_OUTPUT}/usr/lib/shim/shimx64.efi as shim.efi in netboot package"
1790         cp "${CHROOT_OUTPUT}"/usr/lib/shim/shimx64.efi "${WORKING_DIR}"/shim.efi
1791       else
1792         log   "No shimx64.efi for usage with PXE boot found (shim-signed not present?)"
1793         ewarn "No shimx64.efi for usage with PXE boot found (shim-signed not present?)" ; eend 0
1794       fi
1795
1796       if [ -r "${CHROOT_OUTPUT}"/usr/lib/grub/x86_64-efi-signed/grubnetx64.efi.signed ] ; then
1797         log "Installing /usr/lib/grub/x86_64-efi-signed/grubnetx64.efi.signed as grubx64.efi in netboot package"
1798         cp "${CHROOT_OUTPUT}"/usr/lib/grub/x86_64-efi-signed/grubnetx64.efi.signed "${WORKING_DIR}"/grubx64.efi
1799       elif [ -r "${CHROOT_OUTPUT}"/usr/lib/grub/x86_64-efi/monolithic/grubnetx64.efi ] ; then
1800         log "Installing /usr/lib/grub/x86_64-efi/monolithic/grubnetx64.efi as grubx64.efi in netboot package"
1801         cp "${CHROOT_OUTPUT}"/usr/lib/grub/x86_64-efi/monolithic/grubnetx64.efi "${WORKING_DIR}"/grubx64.efi
1802       else
1803         log   "No grubnetx64.efi for usage with PXE boot found (grub-efi-amd64-signed not present?)"
1804         ewarn "No grubnetx64.efi for usage with PXE boot found (grub-efi-amd64-signed not present?)." ; eend 0
1805       fi
1806
1807       if [ -r "${CHROOT_OUTPUT}"/usr/share/grub/unicode.pf2 ] ; then
1808         log "Installing ${CHROOT_OUTPUT}/usr/share/grub/unicode.pf2 as grub/fonts/unicode.pf2 in netboot package"
1809         mkdir -p "${WORKING_DIR}"/grub/fonts/
1810         cp "${CHROOT_OUTPUT}"/usr/share/grub/unicode.pf2 "${WORKING_DIR}"/grub/fonts/
1811       else
1812         log   "No unicode.pf2 for usage with PXE boot found (grub-common not present?)"
1813         ewarn "No unicode.pf2 for usage with PXE boot found (grub-common not present?)" ; eend 0
1814       fi
1815     fi
1816   fi
1817
1818   if tar -C "$OUTPUTDIR" -cf "${OUTPUT_FILE}" "grml_netboot_package_${GRML_NAME}_${VERSION}" ; then
1819     (
1820       cd $(dirname "${OUTPUT_FILE}")
1821       sha256sum $(basename "${OUTPUT_FILE}") > "${OUTPUT_FILE}.sha256"
1822     )
1823     einfo "Generated netboot package ${OUTPUT_FILE}" ; eend 0
1824     rm -rf "${OUTPUTDIR}"
1825   else
1826     rm -rf "${OUTPUTDIR}"
1827     eerror "Could not generate netboot package ${OUTPUT_FILE}" ; eend 1
1828     bailout 21
1829   fi
1830 }
1831
1832 create_netbootpackage
1833 # }}}
1834
1835 # log build information to database if grml-live-db is installed and enabled {{{
1836 dpkg_to_db() {
1837 if [ -d /usr/share/grml-live-db ] ; then
1838
1839   # safe defaults
1840   DPKG_LIST="/var/log/fai/$HOSTNAME/last/dpkg.list" # the dpkg --list output of the chroot:
1841   [ -n "$DPKG_DATABASE" ]  || DPKG_DATABASE=/var/log/grml-live.db
1842   [ -n "$DPKG_DBSCRIPT" ]  || DPKG_DBSCRIPT=/usr/share/grml-live-db/scripts/dpkg-to-db
1843   [ -n "$DPKG_DBOPTIONS" ] || DPKG_DBOPTIONS="--database $DPKG_DATABASE --logfile $LOGFILE --flavour $GRML_NAME --dpkg $DPKG_LIST"
1844
1845   if ! [ -x "$DPKG_DBSCRIPT" ] ; then
1846     log "Error: $DPKG_DBSCRIPT is not executable, can not log dpkg information."
1847     eerror "Error: $DPKG_DBSCRIPT is not executable, can not log dpkg information." ; eend 1
1848     bailout 14
1849   fi
1850
1851   # disable by default for now, not sure whether really everyone is using a local db file
1852   #if ! touch "$DPKG_DATABASE" ; then
1853   #  eerror "Error: can not write to ${DPKG_DATABASE}, can not log dpkg information." ; eend 1
1854   #  bailout 14
1855   #fi
1856
1857   if ! [ -r "$DPKG_LIST" ] ; then
1858      log   "Warning: can not read $DPKG_LIST - can not provide information to $DPKG_DBSCRIPT (dirty build?)"
1859      ewarn "Warning: can not read $DPKG_LIST - can not provide information to $DPKG_DBSCRIPT (dirty build?)" ; eend 0
1860   else
1861      einfo "Logging $DPKG_LIST to database $DPKG_DATABASE"
1862      log "Logging $DPKG_LIST to database $DPKG_DATABASE"
1863      log "Executing $DPKG_DBSCRIPT $DPKG_DBOPTIONS"
1864      eindent
1865
1866      if DB_INFO=$("$DPKG_DBSCRIPT" $DPKG_DBOPTIONS 2>&1) ; then
1867        einfo "$DB_INFO"
1868        eend 0
1869      else
1870        eerror "$DB_INFO"
1871        eend 1
1872      fi
1873
1874      eoutdent
1875   fi
1876
1877 fi
1878 }
1879 # }}}
1880
1881 # finalize {{{
1882 if [ -n "${start_seconds}" ] ; then
1883   end_seconds="$(date +%s)"
1884   SECONDS="$(( end_seconds - start_seconds ))"
1885 fi
1886 log "Successfully finished execution of $PN [$(date) - running ${SECONDS} seconds]"
1887
1888 dpkg_to_db # make sure we catch the last log line as well, therefore execute between log + einfo
1889
1890 einfo "Successfully finished execution of $PN [$(date) - running ${SECONDS} seconds]" ; eend 0
1891 bailout 0
1892 # }}}
1893
1894 ## END OF FILE #################################################################
1895 # vim:foldmethod=marker ts=2 ft=sh ai expandtab tw=80 sw=2