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