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