Drop trailing dot in 'Boot from next boot device'
[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; values: etch, lenny, squeeze, sid
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 sid -V -r 'grml-live rocks'
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://cdn.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       # provide inform fai about the ISO we build, needs to be provided
700       # *after* FAI stage, otherwise FAI skips the debootstrap stage if
701       # there is not BASEFILE (as it checks for presence of /etc) :(
702       echo '# This file has been generated by grml-live.' > "$CHROOT_OUTPUT/etc/grml_live_version"
703       [ -n "$GRML_LIVE_VERSION" ] && echo "GRML_LIVE_VERSION=$GRML_LIVE_VERSION" >> "$CHROOT_OUTPUT/etc/grml_live_version"
704       [ -n "$SUITE" ] && echo "SUITE=$SUITE" >> "$CHROOT_OUTPUT/etc/grml_live_version"
705
706       FORCE_ISO_REBUILD=true
707
708       if [ "$RC" != 0 ] ; then
709          log    "Error: critical error while executing fai [exit code ${RC}]. Exiting."
710          eerror "Error: critical error while executing fai [exit code ${RC}]. Exiting." ; eend 1
711          bailout 1
712       fi
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        cp -a "${CHROOT_OUTPUT}"/usr/lib/grub/*-pc/*.mod "${BUILD_OUTPUT}"/boot/grub/
962        cp -a "${CHROOT_OUTPUT}"/usr/lib/grub/*-pc/*.o "${BUILD_OUTPUT}"/boot/grub/
963        cp -a "${CHROOT_OUTPUT}"/usr/lib/grub/*-pc/*.lst "${BUILD_OUTPUT}"/boot/grub/
964        cp -a "${CHROOT_OUTPUT}"/usr/share/grub/ascii.pf2 "${BUILD_OUTPUT}"/boot/grub/
965        cp -a "${CHROOT_OUTPUT}"/boot/grub/core.img "${BUILD_OUTPUT}"/boot/grub/
966        cp -a "${CHROOT_OUTPUT}"/boot/grub/grub.img "${BUILD_OUTPUT}"/boot/grub/
967
968        if ! [ -d "${TEMPLATE_DIRECTORY}"/GRML ] ; then
969           log    "Error: ${TEMPLATE_DIRECTORY}/GRML does not exist. Exiting."
970           eerror "Error: ${TEMPLATE_DIRECTORY}/GRML does not exist. Exiting." ; eend 1
971           bailout 9
972        fi
973
974        mkdir -p "$BUILD_OUTPUT"/GRML/"${GRML_NAME}"/
975        cp -a ${TEMPLATE_DIRECTORY}/GRML/* "$BUILD_OUTPUT"/GRML/"${GRML_NAME}"/
976
977        # adjust boot splash information:
978        RELEASE_INFO="$GRML_NAME $VERSION - Release Codename $RELEASENAME"
979        RELEASE_INFO="$(cut_string 68 "$RELEASE_INFO")"
980        RELEASE_INFO="$(extend_string_end 68 "$RELEASE_INFO")"
981
982        if [ -r "$BUILD_OUTPUT"/GRML/"${GRML_NAME}"/grml-version ] ; then
983           sed -i "s/%RELEASE_INFO%/$GRML_NAME $VERSION - $RELEASENAME/" "$BUILD_OUTPUT"/GRML/"${GRML_NAME}"/grml-version
984           sed -i "s/%DATE%/$DATE/"                                      "$BUILD_OUTPUT"/GRML/"${GRML_NAME}"/grml-version
985        fi
986
987        # make sure the squashfs filename is set accordingly:
988        SQUASHFS_NAME="$GRML_NAME.squashfs"
989
990        if [ -n "$NO_BOOTID" ] ; then
991           log   'Skipping bootid feature as requested via $NO_BOOTID.'
992           einfo 'Skipping bootid feature as requested via $NO_BOOTID.'
993        else
994           [ -n "$BOOTID" ] || BOOTID="$(echo ${GRML_NAME}${VERSION} | tr -d ',./;\- ')"
995           [ -d "$BUILD_OUTPUT"/conf ] || mkdir "$BUILD_OUTPUT"/conf
996           einfo "Generating /conf/bootid.txt with entry ${BOOTID}."
997           log   "Generating /conf/bootid.txt with entry ${BOOTID}."
998           echo "$BOOTID" > "$BUILD_OUTPUT"/conf/bootid.txt
999           eend $?
1000        fi
1001
1002        # adjust all variables in the templates with the according distribution information
1003        for file in "${BUILD_OUTPUT}"/boot/isolinux/*.cfg "${BUILD_OUTPUT}"/boot/isolinux/*.msg \
1004                    "${BUILD_OUTPUT}"/boot/grub/* ; do
1005          if [ -r "${file}" ] && [ -f "${file}" ] ; then
1006            sed -i "s/%ARCH%/$ARCH/g"                    "${file}"
1007            sed -i "s/%DATE%/$DATE/g"                    "${file}"
1008            sed -i "s/%DISTRI_INFO%/$DISTRI_INFO/g"      "${file}"
1009            sed -i "s/%DISTRI_NAME%/$DISTRI_NAME/g"      "${file}"
1010            sed -i "s/%DISTRI_SPLASH%/$DISTRI_SPLASH/g"  "${file}"
1011            sed -i "s/%GRML_NAME%/$GRML_NAME/g"          "${file}"
1012            sed -i "s/%SQUASHFS_NAME%/$SQUASHFS_NAME/g"  "${file}"
1013            sed -i "s/%RELEASE_INFO%/$RELEASE_INFO/g"    "${file}"
1014            sed -i "s/%SHORT_NAME%/$SHORT_NAME/g"        "${file}"
1015            sed -i "s/%VERSION%/$VERSION/g"              "${file}"
1016
1017            [ -n "$DEFAULT_BOOTOPTIONS" ] && sed -i "s; boot=live; boot=live $DEFAULT_BOOTOPTIONS;"  "${file}"
1018
1019            if [ -n "$NO_BOOTID" ] ; then
1020               sed -i "s/ bootid=%BOOTID%//g" "${file}" # drop bootid bootoption
1021            else
1022               sed -i "s/%BOOTID%/$BOOTID/g" "${file}" # adjust bootid=... argument
1023            fi
1024          fi
1025        done
1026
1027        for param in ARCH DATE DISTRI_INFO DISTRI_NAME DISTRI_SPLASH GRML_NAME SQUASHFS_NAME \
1028            RELEASE_INFO SHORT_NAME VERSION ; do
1029            for file in $(find "${BUILD_OUTPUT}" -name "*%$param%*") ; do
1030                value="$(eval echo '$'"$param")"
1031                mv ${file} ${file/\%${param}\%/$value}
1032            done
1033        done
1034
1035        # adjust bootsplash accordingly but make sure the string has the according lenght
1036        SQUASHFS_NAME="$(cut_string 20 "$SQUASHFS_NAME")"
1037        SQUASHFS_NAME="$(extend_string_end 20 "$SQUASHFS_NAME")"
1038        for file in f4 f5 ; do
1039           if [ -r "${BUILD_OUTPUT}/boot/isolinux/${file}" ] ; then
1040              sed -i "s/%SQUASHFS_NAME%/$SQUASHFS_NAME/" "${BUILD_OUTPUT}/boot/isolinux/${file}"
1041              sed -i "s/%SQUASHFS_NAME%/$SQUASHFS_NAME/" "${BUILD_OUTPUT}/boot/isolinux/${file}"
1042           fi
1043        done
1044
1045        # generate addon list
1046        rm -f "${BUILD_OUTPUT}/${ADDONS_LIST_FILE}"
1047        for name in "${BUILD_OUTPUT}"/boot/isolinux/addon_*.cfg ; do
1048          include_name=$(basename "$name")
1049          echo "include $include_name"  >> "${BUILD_OUTPUT}/${ADDONS_LIST_FILE}"
1050        done
1051
1052        if ! [ -r "${BUILD_OUTPUT}/boot/isolinux/${DISTRI_NAME}.cfg" ] || [ "$DISTRI_NAME" = "grml" ] ; then
1053           log "including grmlmain.cfg in ${BUILD_OUTPUT}/boot/isolinux/distri.cfg"
1054           echo "include grmlmain.cfg"    >  "${BUILD_OUTPUT}/boot/isolinux/distri.cfg"
1055           echo "include default.cfg"     >  "${BUILD_OUTPUT}/boot/isolinux/grmlmain.cfg"
1056           echo "include menuoptions.cfg" >> "${BUILD_OUTPUT}/boot/isolinux/grmlmain.cfg"
1057           echo "include grml.cfg"        >> "${BUILD_OUTPUT}/boot/isolinux/grmlmain.cfg"
1058
1059           for f in "${BUILD_OUTPUT}"/boot/isolinux/submenu*.cfg ; do
1060             echo "include $(basename $f)"     >> "${BUILD_OUTPUT}/boot/isolinux/grmlmain.cfg"
1061           done
1062
1063           echo "include options.cfg"     >> "${BUILD_OUTPUT}/boot/isolinux/grmlmain.cfg"
1064           if [ ! -n "$NO_ADDONS" ] ; then
1065             echo "include addons.cfg"    >> "${BUILD_OUTPUT}/boot/isolinux/grmlmain.cfg"
1066           fi
1067           echo "include isoprompt.cfg"   >> "${BUILD_OUTPUT}/boot/isolinux/grmlmain.cfg"
1068           echo "include hd.cfg"          >> "${BUILD_OUTPUT}/boot/isolinux/grmlmain.cfg"
1069           echo "include hidden.cfg"      >> "${BUILD_OUTPUT}/boot/isolinux/grmlmain.cfg"
1070        else # assume we are building a custom distribution:
1071           log "File ${BUILD_OUTPUT}/boot/isolinux/${DISTRI_NAME}.cfg found, using it."
1072           einfo "File ${BUILD_OUTPUT}/boot/isolinux/${DISTRI_NAME}.cfg found, using it."
1073           if grep -q "^include ${DISTRI_NAME}.cfg" "${BUILD_OUTPUT}/boot/isolinux/distri.cfg" ; then
1074             log "include for ${DISTRI_NAME}.cfg already present, nothing to do."
1075             eindent
1076             einfo "include for ${DISTRI_NAME}.cfg already present, nothing to do."
1077             eoutdent
1078             eend $?
1079          else
1080             log "including ${DISTRI_NAME}.cfg in ${BUILD_OUTPUT}/boot/isolinux/distri.cfg"
1081             echo "include ${DISTRI_NAME}.cfg" > "${BUILD_OUTPUT}/boot/isolinux/distri.cfg"
1082             [ -n "$NO_ADDONS" ] || echo "include addons.cfg" >> "${BUILD_OUTPUT}/boot/isolinux/distri.cfg"
1083           fi
1084        fi
1085
1086        # use old style console based isolinux method only if requested:
1087        if [[ "${ISOLINUX_METHOD}" == "console" ]] ; then
1088           log 'Using console based isolinux method as requested via $ISOLINUX_METHOD.'
1089           einfo 'Using console based isolinux method as requested via $ISOLINUX_METHOD.'
1090           if grep -q '^include console.cfg' "${BUILD_OUTPUT}/boot/isolinux/distri.cfg" ; then
1091             einfo "include for console.cfg already found, nothing to do."
1092             eend 0
1093           else
1094             log "including console.cfg in ${BUILD_OUTPUT}/boot/isolinux/isolinux.cfg"
1095             einfo "including console.cfg in ${BUILD_OUTPUT}/boot/isolinux/isolinux.cfg"
1096             echo "include console.cfg" >> "${BUILD_OUTPUT}/boot/isolinux/isolinux.cfg"
1097             eend $?
1098           fi
1099        else
1100           log 'Using graphical boot menu.'
1101           if grep -q '^include vesamenu.cfg' "${BUILD_OUTPUT}/boot/isolinux/isolinux.cfg" ; then
1102             log "include for vesamenu.cfg already found, nothing to do."
1103           else
1104             log "including vesamenu.cfg in ${BUILD_OUTPUT}/boot/isolinux/isolinux.cfg"
1105             echo "include vesamenu.cfg" >> "${BUILD_OUTPUT}/boot/isolinux/isolinux.cfg"
1106           fi
1107        fi
1108
1109        if [ -e "$BUILD_OUTPUT"/boot/addons/bsd4grml/boot.6 ]; then
1110           sed -i "s/%RELEASE_INFO%/$GRML_NAME $VERSION - $RELEASENAME/" "$BUILD_OUTPUT"/boot/addons/bsd4grml/boot.6
1111        fi
1112
1113        DPKG_LIST="/var/log/fai/$HOSTNAME/last/dpkg.list" # the dpkg --list output of the chroot
1114        if ! [ -r "$DPKG_LIST" ] ; then
1115           ewarn "$DPKG_LIST could not be read, ignoring to store package information on ISO therefore."
1116        else
1117           einfo "Storing package list information as /GRML/${GRML_NAME}/packages.txt on ISO."
1118           cp "$DPKG_LIST" "${BUILD_OUTPUT}"/GRML/"${GRML_NAME}"/packages.txt
1119           eend $?
1120        fi
1121
1122        # autostart for Windows:
1123        if [ -d "${TEMPLATE_DIRECTORY}/windows/autostart/" ] ; then
1124           cp ${TEMPLATE_DIRECTORY}/windows/autostart/* "$BUILD_OUTPUT"/
1125        fi
1126
1127     FORCE_ISO_REBUILD=true
1128     einfo "Finished execution of stage 'boot'" ; eend 0
1129     fi
1130   fi # BOOTSTRAP_ONLY
1131 else
1132    log    'Error: Unsupported ARCH, sorry. Want to support it? Contribute!'
1133    eerror 'Error: Unsupported ARCH, sorry. Want to support it? Contribute!' ; eend 1
1134    bailout
1135 fi
1136
1137 # support installation of local files into the chroot/ISO
1138 if [ -n "$CHROOT_INSTALL" ] ; then
1139   if ! [ -d "$CHROOT_INSTALL" ] ; then
1140      log "Configuration variable \$CHROOT_INSTALL is set but not a directory; ignoring"
1141      ewarn "Configuration variable \$CHROOT_INSTALL is set but not a directory; ignoring"
1142   else
1143      log "Copying local files to chroot as requested via \$CHROOT_INSTALL"
1144      einfo "Copying local files to chroot as requested via \$CHROOT_INSTALL"
1145      rsync -avz --inplace "$CHROOT_INSTALL"/ "$CHROOT_OUTPUT/"
1146      eend $?
1147      einfo "Make sure to run squashfs stage, otherwise your local files won't be part of the ISO."
1148      FORCE_ISO_REBUILD=true
1149   fi
1150 fi
1151
1152 if [ -f "$BUILD_OUTPUT"/live/${GRML_NAME}.squashfs -a -z "$UPDATE" -a -z "$BUILD_ONLY" -a -z "$BUILD_DIRTY" ] ; then
1153    log   "Skipping stage 'squashfs' as $BUILD_OUTPUT/live exists already."
1154    ewarn "Skipping stage 'squashfs' as $BUILD_OUTPUT/live exists already." ; eend 0
1155 elif [ -n "$SKIP_MKSQUASHFS" ] ; then
1156    log   "Skipping stage 'squashfs' as requested via option -q or -N"
1157    ewarn "Skipping stage 'squashfs' as requested via option -q or -N" ; eend 0
1158 else
1159    mkdir -p "$BUILD_OUTPUT"/live/"${GRML_NAME}"/
1160    # make sure we don't leave (even an empty) base.tgz:
1161    [ -f "$CHROOT_OUTPUT/base.tgz" ] && rm -f "$CHROOT_OUTPUT/base.tgz"
1162
1163    # if unconfigured default to squashfs-tools' mksquashfs binary
1164    if [ -z "$SQUASHFS_BINARY" ] ; then
1165       SQUASHFS_BINARY='mksquashfs'
1166    fi
1167
1168    if which "$SQUASHFS_BINARY" >/dev/null 2>&1 ; then
1169       log    "Using mksquashfs binary ${SQUASHFS_BINARY}"
1170       einfo  "Using mksquashfs binary ${SQUASHFS_BINARY}" ; eend 0
1171    else
1172       log    "Error: mksquashfs binary ($SQUASHFS_BINARY) not found. Exiting."
1173       eerror "Error: mksquashfs binary ($SQUASHFS_BINARY) not found. Exiting." ; eend 1
1174       bailout
1175    fi
1176
1177    # use sane defaults if $SQUASHFS_OPTIONS isn't set
1178    if [ -z "$SQUASHFS_OPTIONS" ] ; then
1179      # use blocksize 256k as this gives best result with regards to time + compression
1180      SQUASHFS_OPTIONS="-b 256k"
1181
1182      # set lzma/xz compression by default, unless -z option has been specified on command line
1183      if [ -z "$SQUASHFS_ZLIB" ] ; then
1184         SQUASHFS_OPTIONS="$SQUASHFS_OPTIONS -comp xz"
1185      else
1186         SQUASHFS_OPTIONS="$SQUASHFS_OPTIONS -comp gzip"
1187      fi
1188    fi
1189
1190    # support exclusion of files via exclude-file:
1191    if [ -n "$SQUASHFS_EXCLUDES_FILE" -a "$SQUASHFS_EXCLUDES_FILE" ] ; then
1192       SQUASHFS_OPTIONS="$SQUASHFS_OPTIONS -ef $SQUASHFS_EXCLUDES_FILE -wildcards"
1193    fi
1194
1195    # get rid of unnecessary files when building grml-small for final release:
1196    if echo "$CLASSES" | grep -q GRML_SMALL ; then
1197       SQUASHFS_OPTIONS="$SQUASHFS_OPTIONS -e initrd.img* vmlinuz*"
1198    fi
1199
1200    # log stuff
1201    SQUASHFS_STDERR="$(mktemp -t grml-live.XXXXXX)"
1202
1203    # informational stuff
1204    [ -n "$SQUASHFS_OPTIONS" ]  && SQUASHFS_INFO_MSG="$SQUASHFS_OPTIONS"
1205    [ -n "$SQUASHFS_INFO_MSG" ] && SQUASHFS_INFO_MSG="using options: $SQUASHFS_INFO_MSG"
1206    einfo "Squashfs build information: running binary $SQUASHFS_BINARY $SQUASHFS_INFO_MSG"
1207
1208    log "$SQUASHFS_BINARY $CHROOT_OUTPUT/ $BUILD_OUTPUT/live/${GRML_NAME}/${GRML_NAME}.squashfs -noappend $SQUASHFS_OPTIONS"
1209
1210    if $SQUASHFS_BINARY $CHROOT_OUTPUT/ $BUILD_OUTPUT/live/"${GRML_NAME}"/"${GRML_NAME}".squashfs \
1211       -noappend $SQUASHFS_OPTIONS 2>"${SQUASHFS_STDERR}" ; then
1212       echo "${GRML_NAME}.squashfs" > $BUILD_OUTPUT/live/"${GRML_NAME}"/filesystem.module
1213       log "Finished execution of stage 'squashfs' [$(date)]"
1214       einfo "Finished execution of stage 'squashfs'" ; eend 0
1215    else
1216       log    "Error: there was a critical error executing stage 'squashfs' [$(date)]:"
1217       log    "$(cat $SQUASHFS_STDERR)"
1218       eerror "Error: there was a critical error executing stage 'squashfs':"
1219       cat    "${SQUASHFS_STDERR}"
1220       eend 1
1221       bailout
1222    fi
1223
1224    FORCE_ISO_REBUILD=true
1225 fi
1226
1227 # create md5sum file:
1228 if [ -z "$BOOTSTRAP_ONLY" ] ; then
1229   ( cd $BUILD_OUTPUT/GRML/"${GRML_NAME}" &&
1230   find ../.. -type f -not -name md5sums -not -name isolinux.bin -exec md5sum {} \; > md5sums )
1231 fi
1232 # }}}
1233
1234 # ISO_OUTPUT - mkisofs {{{
1235 [ -n "$ISO_OUTPUT" ] || ISO_OUTPUT="$OUTPUT/grml_isos"
1236 [ -n "$ISO_NAME" ] || ISO_NAME="${GRML_NAME}_${VERSION}.iso"
1237
1238 if [ "$BOOT_METHOD" = "isolinux" ] ; then
1239    BOOT_ARGS="-no-emul-boot -boot-load-size 4 -boot-info-table -b boot/isolinux/isolinux.bin -c boot/isolinux/boot.cat"
1240 elif [ "$BOOT_METHOD" = "grub2" ] ; then
1241    BOOT_ARGS="-no-emul-boot -boot-load-size 4 -b boot/grub/toriboot.bin"
1242 fi
1243
1244 # Work around http://bts.grml.org/grml/issue945
1245 if [[ $BOOT_METHOD != isolinux && ($HYBRID_METHOD = isohybrid || $HYBRID_METHOD = manifold) ]]; then
1246   log   "Setting HYBRID_METHOD to grub2 as hybrid mode does not work with isohybrid yet."
1247   ewarn "Setting HYBRID_METHOD to grub2 as hybrid mode does not work with isohybrid yet."
1248   HYBRID_METHOD='grub2'
1249   eend 0
1250 fi
1251
1252 if [ -f "${ISO_OUTPUT}/${ISO_NAME}" -a -z "$UPDATE" -a -z "$BUILD_ONLY" -a -z "$BUILD_DIRTY" -a "$FORCE_ISO_REBUILD" = "false" ]  ; then
1253    log   "Skipping stage 'iso build' as $ISO_OUTPUT/${ISO_NAME} exists already."
1254    ewarn "Skipping stage 'iso build' as $ISO_OUTPUT/${ISO_NAME} exists already." ; eend 0
1255 elif [ -n "$SKIP_MKISOFS" ] ; then
1256    log   "Skipping stage 'iso build' as requested via option -n or -N"
1257    ewarn "Skipping stage 'iso build' as requested via option -n or -N" ; eend 0
1258 else
1259    mkdir -p "$ISO_OUTPUT" || bailout 6 "Problem with creating $ISO_OUTPUT for stage 'iso build'"
1260
1261    if $FORCE_ISO_REBUILD && ! [ -f "${ISO_OUTPUT}/${ISO_NAME}" ] ; then
1262       log   "Forcing rebuild of ISO because files on ISO have been modified."
1263       einfo "Forcing rebuild of ISO because files on ISO have been modified."
1264    fi
1265
1266    # support xorriso as well mkisofs and genisoimage
1267    if which xorriso >/dev/null 2>&1 ; then
1268       MKISOFS='xorriso -as mkisofs'
1269     elif which mkisofs >/dev/null 2>&1; then
1270       MKISOFS='mkisofs'
1271    elif which genisoimage >/dev/null 2>&1; then
1272       MKISOFS='genisoimage'
1273    else
1274       log    "Error: neither xorriso nor mkisofs nor genisoimage available - can not create ISO."
1275       eerror "Error: neither xorriso nor mkisofs nor genisoimage available - can not create ISO." ; eend 1
1276       bailout
1277    fi
1278
1279    einfo "Using ${MKISOFS} to build ISO." ;  eend 0
1280    case "${ARCH}-${MKISOFS}" in
1281      # using -eltorito-alt-boot is limited to xorriso for now
1282      amd64-xorriso*)
1283        eindent
1284
1285        if ! dpkg --compare-versions $(dpkg-query -W -f='${Version}\n' xorriso 2>/dev/null) gt-nl 1.1.6-1 ; then
1286          log   "Disabling (U)EFI boot support because xorriso version is too old."
1287          ewarn "Disabling (U)EFI boot support because xorriso version is too old." ; eend 0
1288        else
1289          if [ -r "${BUILD_OUTPUT}"/boot/efi.img ] ; then
1290            einfo "Enabling (U)EFI boot."
1291            log   "Enabling (U)EFI boot."
1292            BOOT_ARGS="$BOOT_ARGS -boot-info-table -eltorito-alt-boot -e boot/efi.img -no-emul-boot"
1293            eend $?
1294          else
1295            log   "Disabling (U)EFI boot support because /boot/efi.img is missing."
1296            ewarn "Disabling (U)EFI boot support because /boot/efi.img is missing." ; eend 0
1297          fi
1298        fi
1299
1300        eoutdent
1301        ;;
1302    esac
1303
1304    CURRENT_DIR=$(pwd)
1305    if cd "$BUILD_OUTPUT" ; then
1306       if [ "$BOOT_METHOD" = "grub2" ]; then
1307          # make a 2048-byte bootsector for El Torito
1308          dd if=/dev/zero of=boot/grub/toriboot.bin bs=512 count=4 2>/dev/null
1309          # those are in 2048-byte sectors, so 1 16 matches 4 63 below
1310          echo 1 16 | mksh /usr/share/grml-live/scripts/bootgrub.mksh -B 11 | \
1311             dd of=boot/grub/toriboot.bin conv=notrunc 2>/dev/null
1312       fi
1313       log "$MKISOFS -V '${GRML_NAME} ${VERSION}' -publisher 'grml-live | grml.org' -l -r -J $BOOT_ARGS -o ${ISO_OUTPUT}/${ISO_NAME} ."
1314       $MKISOFS -V "${GRML_NAME} ${VERSION}" -publisher 'grml-live | grml.org' \
1315               -l -r -J $BOOT_ARGS -no-pad \
1316               -o "${ISO_OUTPUT}/${ISO_NAME}" . ; RC=$?
1317       # both of these need core.img there, so it’s easier to write it here
1318       if [ "$BOOT_METHOD" = "grub2" ] || [ "$HYBRID_METHOD" = "grub2" ]; then
1319          # must be <= 30720 bytes
1320          dd if=boot/grub/core.img of="${ISO_OUTPUT}/${ISO_NAME}" \
1321            conv=notrunc bs=512 seek=4 2>/dev/null
1322       fi
1323
1324       # pad the output ISO to multiples of 256 KiB for partition table support
1325       siz=$($getfilesize "${ISO_OUTPUT}/${ISO_NAME}")
1326       cyls=$((siz / 512 / 32 / 16 + 1))   # C=$cyls H=16 S=32
1327       siz=$((cyls * 16 * 32 * 512))   # size after padding
1328       dd if=/dev/zero bs=1 count=1 seek=$((siz - 1)) \
1329          of="${ISO_OUTPUT}/${ISO_NAME}" 2>/dev/null
1330
1331       # support disabling hybrid ISO image
1332       if [ "$HYBRID_METHOD" = "disable" ] ; then
1333         log   "Skipping creation of hybrid ISO file as requested via HYBRID_METHOD=disable"
1334         einfo "Skipping creation of hybrid ISO file as requested via HYBRID_METHOD=disable"
1335         eend 0
1336       elif [ "$HYBRID_METHOD" = "manifold" ] || [ "$HYBRID_METHOD" = "grub2" ] ; then
1337         # isoinfo is part of both mkisofs and genisoimage so we're good
1338         bootoff=$(isoinfo -l -i "${ISO_OUTPUT}/${ISO_NAME}" | \
1339           sed -n '/^.*\[ *\([0-9]*\)[] ].* ISOLINUX.BIN[;1]* *$/s//\1/p')
1340
1341         if ! [ -r boot/grub/core.img ] ; then
1342           log   "boot/grub/core.img not found, not creating manifold boot ISO file"
1343           ewarn "boot/grub/core.img not found, not creating manifold boot ISO file"
1344         elif [ "${bootoff:-0}" -lt 1 ] ; then
1345           log   "isolinux.bin not found on the ISO file, disabling manifold boot"
1346           ewarn "isolinux.bin not found on the ISO file, disabling manifold boot"
1347         else
1348           if [ "$HYBRID_METHOD" = "grub2" ] ; then
1349             log   "Creating hybrid ISO file with manifold/grub2 method"
1350             einfo "Creating hybrid ISO file with manifold/grub2 method"
1351             # 512 bytes: MBR, partition table, load GRUB 2
1352             echo 4 63 | mksh /usr/share/grml-live/scripts/bootgrub.mksh -A -M 4:0x96 -g $cyls:16:32
1353           else
1354             log   "Creating hybrid ISO file with manifold method"
1355             einfo "Creating hybrid ISO file with manifold method"
1356             # read only one but 2048-byte sized (scale: << 2) sector
1357             echo $bootoff $bootoff | \
1358               mksh /usr/share/grml-live/scripts/bootilnx.mksh -A -M 4:0x96 -g $cyls:16:32 -S 2
1359           fi | dd of="${ISO_OUTPUT}/${ISO_NAME}" conv=notrunc 2>/dev/null
1360           eend $?
1361         fi
1362       elif [ "$HYBRID_METHOD" = "isohybrid" ] ; then
1363         if ! which isohybrid >/dev/null 2>&1 ; then
1364           bailout 12 "isohybrid binary not found - please install syslinux/syslinux-common"
1365         else
1366           log   "Creating hybrid ISO file with isohybrid method"
1367           einfo "Creating hybrid ISO file with isohybrid method"
1368           # Notes for consideration:
1369           # "-entry 4 -type 1c"
1370           # * using 4 as the partition number is supposed to help with BIOSes
1371           #   that only support USB-Zip boot
1372           # * using 1c (i.e. hidden FAT32 LBA), instead of the default 0x17
1373           #   (hidden NTFS, IIRC), as the partition type is sometimes needed
1374           #   to get the BIOS even look at the partition created by isohybrid
1375           if isohybrid --help | grep -q -- --uefi ; then
1376             if echo $CLASSES | grep -qw I386 ; then
1377               log   "Detected uefi support for isohybrid but 32bit systems do not support it, ignoring."
1378               einfo "Detected uefi support for isohybrid but 32bit systems do not support it, ignoring."
1379             else
1380               log   "Detected uefi support for isohybrid, enabling"
1381               einfo "Detected uefi support for isohybrid, enabling"
1382               ISOHYBRID_OPTIONS=--uefi
1383             fi
1384           fi
1385
1386           log "isohybrid $ISOHYBRID_OPTIONS ${ISO_OUTPUT}/${ISO_NAME}"
1387           isohybrid $ISOHYBRID_OPTIONS "${ISO_OUTPUT}/${ISO_NAME}"
1388           eend $?
1389         fi
1390       else
1391         bailout 12 "Unknown HYBRID_METHOD [${HYBRID_METHOD}]. Supported values: disable, isohybrid, grub2, manifold"
1392       fi
1393
1394       # generate md5sum and sha1sum of ISO if we are using class 'RELEASE':
1395       case $CLASSES in *RELEASE*)
1396          [ "$RC" = 0 ] && \
1397          (
1398            if cd $ISO_OUTPUT ; then
1399              md5sum ${ISO_NAME} > ${ISO_NAME}.md5 && \
1400              touch -r ${ISO_NAME} ${ISO_NAME}.md5
1401              sha1sum ${ISO_NAME} > ${ISO_NAME}.sha1 && \
1402              touch -r ${ISO_NAME} ${ISO_NAME}.sha1
1403            fi
1404          )
1405          ;;
1406       esac
1407
1408       cd "$CURRENT_DIR"
1409    fi
1410
1411    if [ "$RC" = 0 ] ; then
1412       log   "Finished execution of stage 'iso build' [$(date)]"
1413       einfo "Finished execution of stage 'iso build'" ; eend 0
1414    else
1415       log    "Error: there was a critical error ($RC) executing stage 'iso build' [$(date)]"
1416       eerror "Error: there was a critical error executing stage 'iso build'" ; eend 1
1417       bailout $RC
1418    fi
1419 fi
1420 # }}}
1421
1422 # netboot package {{{
1423 create_netbootpackage() {
1424   local OUTPUT_FILE="${NETBOOT}/grml_netboot_package_${GRML_NAME}_${VERSION}.tar.bz2"
1425
1426   if [ -f "${OUTPUT_FILE}" -a -z "$UPDATE" -a -z "$BUILD_ONLY" -a -z "$BUILD_DIRTY" ] ; then
1427     log   "Skipping stage 'netboot' as $OUTPUT_FILE exists already."
1428     ewarn "Skipping stage 'netboot' as $OUTPUT_FILE exists already." ; eend 0
1429     return 0
1430   elif [ -n "$SKIP_NETBOOT" ] ; then
1431     log   "Skipping stage 'netboot' as requested via option -Q"
1432     ewarn "Skipping stage 'netboot' as requested via option -Q" ; eend 0
1433     return 0
1434   fi
1435
1436   mkdir -p "$NETBOOT"
1437
1438   if ! [ -r "${CHROOT_OUTPUT}/usr/lib/syslinux/pxelinux.0" ] ; then
1439     ewarn "File /usr/lib/syslinux/pxelinux.0 not found in build chroot." ; eend 0
1440     eindent
1441     einfo "Install syslinux[-common] package in chroot to get a netboot package."
1442     eoutdent
1443     return 0
1444   fi
1445
1446   local OUTPUTDIR="${NETBOOT}/build_tmp"
1447   local WORKING_DIR="${OUTPUTDIR}/grml_netboot_package_${GRML_NAME}_${VERSION}/tftpboot/"
1448
1449   mkdir -p "$WORKING_DIR"
1450
1451   cp "${CHROOT_OUTPUT}"/boot/vmlinuz-*    "$WORKING_DIR"/vmlinuz
1452   cp "${CHROOT_OUTPUT}"/boot/initrd.img-* "$WORKING_DIR"/initrd.img
1453   cp "${CHROOT_OUTPUT}"/usr/lib/syslinux/pxelinux.0 "${WORKING_DIR}/pxelinux.0"
1454
1455   mkdir -p "${WORKING_DIR}/pxelinux.cfg"
1456   if [ -r "${BUILD_OUTPUT}/boot/isolinux/netboot.cfg" ] ; then
1457     cp "${BUILD_OUTPUT}/boot/isolinux/netboot.cfg" "${WORKING_DIR}/pxelinux.cfg/default"
1458   else
1459     log   "File ${BUILD_OUTPUT}/boot/isolinux/netboot.cfg not found."
1460     ewarn "File ${BUILD_OUTPUT}/boot/isolinux/netboot.cfg not found."
1461     eindent
1462     log   "Hint: Are you using custom templates which do not provide netboot.cfg?"
1463     ewarn "Hint: Are you using custom templates which do not provide netboot.cfg?" ; eend 0
1464     eoutdent
1465   fi
1466
1467   if tar -C "$OUTPUTDIR" -jcf "${OUTPUT_FILE}" "grml_netboot_package_${GRML_NAME}_${VERSION}" ; then
1468     (
1469       cd $(dirname "${OUTPUT_FILE}")
1470       sha1sum $(basename "${OUTPUT_FILE}") > "${OUTPUT_FILE}.sha1"
1471     )
1472     einfo "Generated netboot package ${OUTPUT_FILE}" ; eend 0
1473     rm -rf "${OUTPUTDIR}"
1474   else
1475     rm -rf "${OUTPUTDIR}"
1476     eerror "Could not generate netboot package ${OUTPUT_FILE}" ; eend 1
1477     bailout 21
1478   fi
1479 }
1480
1481 create_netbootpackage
1482 # }}}
1483
1484 # log build information to database if grml-live-db is installed and enabled {{{
1485 dpkg_to_db() {
1486 if [ -d /usr/share/grml-live-db ] ; then
1487
1488   # safe defaults
1489   DPKG_LIST="/var/log/fai/$HOSTNAME/last/dpkg.list" # the dpkg --list output of the chroot:
1490   [ -n "$DPKG_DATABASE" ]  || DPKG_DATABASE=/var/log/grml-live.db
1491   [ -n "$DPKG_DBSCRIPT" ]  || DPKG_DBSCRIPT=/usr/share/grml-live-db/scripts/dpkg-to-db
1492   [ -n "$DPKG_DBOPTIONS" ] || DPKG_DBOPTIONS="--database $DPKG_DATABASE --logfile $LOGFILE --flavour $GRML_NAME --dpkg $DPKG_LIST"
1493
1494   if ! [ -x "$DPKG_DBSCRIPT" ] ; then
1495     log "Error: $DPKG_DBSCRIPT is not executable, can not log dpkg information."
1496     eerror "Error: $DPKG_DBSCRIPT is not executable, can not log dpkg information." ; eend 1
1497     bailout 14
1498   fi
1499
1500   # disable by default for now, not sure whether really everyone is using a local db file
1501   #if ! touch "$DPKG_DATABASE" ; then
1502   #  eerror "Error: can not write to ${DPKG_DATABASE}, can not log dpkg information." ; eend 1
1503   #  bailout 14
1504   #fi
1505
1506   if ! [ -r "$DPKG_LIST" ] ; then
1507      log   "Warning: can not read $DPKG_LIST - can not provide information to $DPKG_DBSCRIPT (dirty build?)"
1508      ewarn "Warning: can not read $DPKG_LIST - can not provide information to $DPKG_DBSCRIPT (dirty build?)" ; eend 0
1509   else
1510      einfo "Logging $DPKG_LIST to database $DPKG_DATABASE"
1511      log "Logging $DPKG_LIST to database $DPKG_DATABASE"
1512      log "Executing $DPKG_DBSCRIPT $DPKG_DBOPTIONS"
1513      eindent
1514
1515      if DB_INFO=$("$DPKG_DBSCRIPT" $DPKG_DBOPTIONS 2>&1) ; then
1516        einfo "$DB_INFO"
1517        eend 0
1518      else
1519        eerror "$DB_INFO"
1520        eend 1
1521      fi
1522
1523      eoutdent
1524   fi
1525
1526 fi
1527 }
1528 # }}}
1529
1530 # finalize {{{
1531 [ -n "$start_seconds" ] && SECONDS="$[$(cut -d . -f 1 /proc/uptime)-$start_seconds]" || SECONDS="unknown"
1532 log "Successfully finished execution of $PN [$(date) - running ${SECONDS} seconds]"
1533
1534 dpkg_to_db # make sure we catch the last log line as well, therefore execute between log + einfo
1535
1536 einfo "Successfully finished execution of $PN [$(date) - running ${SECONDS} seconds]" ; eend 0
1537 bailout 0
1538 # }}}
1539
1540 ## END OF FILE #################################################################
1541 # vim:foldmethod=marker ts=2 ft=sh ai expandtab tw=80 sw=2