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