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