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