Do not bailout (incl. cleanup) but just exit if user wants to quit
[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       echo "Exiting as requested."
468       exit 0
469    fi
470    echo
471 fi
472 # }}}
473
474 # clean up before start {{{
475 if [ -n "${CLEAN_ARTIFACTS}" ]; then
476   echo "Wiping old artifacts"
477   [ -n "${CHROOT_OUTPUT}"  -a -d "${CHROOT_OUTPUT}"  ] && rm -r "${CHROOT_OUTPUT}"
478   [ -n "${BUILD_OUTPUT}"   -a -d "${BUILD_OUTPUT}"   ] && rm -r "${BUILD_OUTPUT}"
479   [ -n "${ISO_OUTPUT}"     -a -d "${ISO_OUTPUT}"     ] && rm -r "${ISO_OUTPUT}"
480   [ -n "${LOG_OUTPUT}"     -a -d "${LOG_OUTPUT}"     ] && rm -r "${LOG_OUTPUT}"
481   [ -n "${NETBOOT}"        -a -d "${NETBOOT}"        ] && rm -r "${NETBOOT}"
482 fi
483 # }}}
484
485 # create log file {{{
486 [ -n "$LOGFILE" ] || LOGFILE=${LOG_OUTPUT}/grml-live.log
487 mkdir -p $(dirname "${LOGFILE}")
488 touch $LOGFILE
489 chown root:adm $LOGFILE
490 chmod 664 $LOGFILE
491 # }}}
492
493 # clean/zero/remove logfiles {{{
494
495 if [ -n "$PRESERVE_LOGFILE" ] ; then
496    echo "Preserving logfile $LOGFILE as requested via \$PRESERVE_LOGFILE"
497 else
498    # make sure it is empty (as it is e.g. appended to grml-live-db)
499    echo -n > $LOGFILE
500 fi
501
502 if [ -n "$ZERO_FAI_LOGFILE" ] ; then
503    if [ -d /var/log/fai/"$HOSTNAME" ] ; then
504       rm -rf /var/log/fai/"$HOSTNAME"/"$(readlink /var/log/fai/"$HOSTNAME"/last)"
505       rm -rf /var/log/fai/"$HOSTNAME"/"$(readlink /var/log/fai/"$HOSTNAME"/last-dirinstall)"
506       rm -rf /var/log/fai/"$HOSTNAME"/"$(readlink /var/log/fai/"$HOSTNAME"/last-softupdate)"
507       rm -f /var/log/fai/"$HOSTNAME"/last \
508             /var/log/fai/"$HOSTNAME"/last-dirinstall \
509             /var/log/fai/"$HOSTNAME"/last-softupdate
510    fi
511 fi
512 # }}}
513
514 # source config and startup {{{
515 if [ -n "$CONFIG" ] ; then
516    if ! [ -f "$CONFIG" ] ; then
517       log    "Error: $CONFIG could not be read. Exiting. [$(date)]"
518       eerror "Error: $CONFIG could not be read. Exiting." ; eend 1
519       bailout 1
520    else
521       log "Sourcing $CONFIG"
522       . $CONFIG
523    fi
524 fi
525
526 start_seconds=$(cut -d . -f 1 /proc/uptime)
527 log "------------------------------------------------------------------------------"
528 log "Starting grml-live [${GRML_LIVE_VERSION}] run on $(date)"
529 log "Using local config file: $LOCAL_CONFIG"
530 log "Executed grml-live command line:"
531 log "$CMDLINE"
532
533 einfo "Logging actions to logfile $LOGFILE"
534 # }}}
535
536 # dump config variables into file, for script access {{{
537 CONFIGDUMP=$(mktemp)
538 set | egrep \
539   '^(GRML_NAME|RELEASENAME|DATE|VERSION|SUITE|ARCH|DISTRI_NAME|USERNAME|HOSTNAME|APT_PROXY)=' \
540   > ${CONFIGDUMP}
541 # }}}
542
543 # unpack iso/squashfs {{{
544 extract_iso() {
545 if [ -n "$EXTRACT_ISO_NAME" ]; then
546   log "Unpacking ISO from ${EXTRACT_ISO_NAME}"
547   einfo "Unpacking ISO from ${EXTRACT_ISO_NAME}"
548   local mountpoint=$(mktemp -d)
549   local rc=0
550   mount -o loop "${EXTRACT_ISO_NAME}" "$mountpoint" ; rc=$?
551   if [ "$rc" != 0 ]; then
552     rmdir "$mountpoint"
553     log "mount failed"
554     eerror "mount failed"
555     eend 1
556     bailout 1
557   fi
558
559   if ls "${mountpoint}"/live/*/*.squashfs 2>/dev/null | grep -q . ; then # ISOs >=2011.12
560     log "Using ${mountpoint}/live/*/*.squashfs for unsquashfs"
561     unsquashfs -d "${CHROOT_OUTPUT}" "${mountpoint}"/live/*/*.squashfs ; rc=$?
562   elif ls "${mountpoint}"/live/*.squashfs 2>/dev/null | grep -q . ; then # ISOs before 2011.12
563     log "Using ${mountpoint}/live/*.squashfs for unsquashfs"
564     unsquashfs -d "${CHROOT_OUTPUT}" "${mountpoint}"/live/*.squashfs ; rc=$?
565   else
566     log "Error: Could not find any *.squashfs files on the ISO"
567     eerror "Error: Could not find any *.squashfs files on the ISO"
568     eend 1
569     bailout 1
570   fi
571
572   umount "$mountpoint"
573   rmdir "$mountpoint"
574   if [ "$rc" != 0 ]; then
575     log "unsquashfs failed"
576     eerror "unsquashfs failed"
577     eend 1
578     bailout 1
579   fi
580 fi
581 }
582 extract_iso
583 # }}}
584
585 # on-the-fly configuration {{{
586
587 # does this suck? YES!
588 # /usr/share/debootstrap/scripts/unstable does not exist, instead use 'sid':
589 case $SUITE in
590    unstable) SUITE='sid' ; CLASSES="DEBIAN_UNSTABLE,$CLASSES" ;;
591    *) CLASSES="DEBIAN_$(echo $SUITE | tr 'a-z' 'A-Z'),$CLASSES";;
592 esac
593 export SUITE # make sure it's available in FAI scripts
594
595 # validate whether the specified architecture class matches the
596 # architecture (option), otherwise installation of kernel will fail
597 if echo $CLASSES | grep -qw I386 ; then
598    if ! [[ "$ARCH" == "i386" ]] ; then
599       log    "Error: You specified the I386 class but are trying to build something else (AMD64?)."
600       eerror "Error: You specified the I386 class but are trying to build something else (AMD64?)."
601       eerror "Tip:   Either invoke grml-live with '-a i386' or adjust the architecture class. Exiting."
602       eend 1
603       bailout
604    fi
605 elif echo $CLASSES | grep -qi amd64 ; then
606    if ! [[ "$ARCH" == "amd64" ]] ; then
607       log    "Error: You specified the AMD64 class but are trying to build something else (I386?)."
608       eerror "Error: You specified the AMD64 class but are trying to build something else (I386?)."
609       eerror "Tip:   Either invoke grml-live with '-a amd64' or adjust the architecture class. Exiting."
610       eend 1
611       bailout
612    fi
613 fi
614
615 # generate nfsroot configuration for FAI on the fly
616 if [ -z "$FAI_DEBOOTSTRAP" ] ; then
617   FAI_DEBOOTSTRAP="$SUITE http://cdn.debian.net/debian"
618 fi
619
620 if [ -z "$FAI_DEBOOTSTRAP_OPTS" ] ; then
621   FAI_DEBOOTSTRAP_OPTS="--exclude=info,tasksel,tasksel-data --arch $ARCH"
622 fi
623
624 # create backup of old (not yet automatically generated) config file
625 if [ -f "${GRML_FAI_CONFIG}/make-fai-nfsroot.conf" ] ; then
626   if ! grep -q 'This is an automatically generated file by grml-live' "${GRML_FAI_CONFIG}/make-fai-nfsroot.conf" ; then
627     ewarn "Found old ${GRML_FAI_CONFIG}/make-fai-nfsroot.conf - moving to ${GRML_FAI_CONFIG}/make-fai-nfsroot.conf.outdated"
628     mv "${GRML_FAI_CONFIG}/make-fai-nfsroot.conf" "${GRML_FAI_CONFIG}/make-fai-nfsroot.conf.outdated"
629     eend $?
630   fi
631 fi
632
633 echo "# This is an automatically generated file by grml-live.
634 # Do NOT edit this file, your changes will be lost.
635 FAI_DEBOOTSTRAP=\"$FAI_DEBOOTSTRAP\"
636 FAI_DEBOOTSTRAP_OPTS=\"$FAI_DEBOOTSTRAP_OPTS\"
637 # EOF " > "${GRML_FAI_CONFIG}/nfsroot.conf"
638 # support FAI <=3.4.8, versions >=4.0 use nfsroot.conf
639 ( cd ${GRML_FAI_CONFIG} && ln -sf nfsroot.conf make-fai-nfsroot.conf )
640 # }}}
641
642 # CHROOT_OUTPUT - execute FAI {{{
643 if [ -n "$BUILD_DIRTY" ]; then
644    log   "Skipping stage 'fai' as requested via option -B"
645    ewarn "Skipping stage 'fai' as requested via option -B" ; eend 0
646 else
647    [ -n "$CHROOT_OUTPUT" ] || CHROOT_OUTPUT="$OUTPUT/grml_chroot"
648
649    if [ -n "$UPDATE" -o -n "$BUILD_ONLY" ] ; then
650       FAI_ACTION=softupdate
651    else
652       FAI_ACTION=dirinstall
653    fi
654
655    if [ -n "$UPDATE" -o -n "$BUILD_ONLY" ] ; then
656       if ! [ -r "$CHROOT_OUTPUT/etc/debian_version" ] ; then
657          log    "Error: does not look like you have a working chroot. Updating/building not possible."
658          eerror "Error: does not look like you have a working chroot. Updating/building not possible. (Drop -u/-b option?)"
659          eend 1
660          bailout 20
661       fi
662    fi
663
664    if [ -d "$CHROOT_OUTPUT/bin" -a -z "$UPDATE" -a -z "$BUILD_ONLY" ] ; then
665       log   "Skipping stage 'fai dirinstall' as $CHROOT_OUTPUT exists already."
666       ewarn "Skipping stage 'fai dirinstall' as $CHROOT_OUTPUT exists already." ; eend 0
667    else
668       mkdir -p "$CHROOT_OUTPUT" || bailout 5 "Problem with creating $CHROOT_OUTPUT for FAI"
669
670       if [ -n "${MIRROR_DIRECTORY}" ] ; then
671          mkdir -p "${CHROOT_OUTPUT}/${MIRROR_DIRECTORY}"
672          mount --bind "${MIRROR_DIRECTORY}" "${CHROOT_OUTPUT}/${MIRROR_DIRECTORY}"
673       fi
674
675       mkdir -p "${OUTPUT}/grml_sources/" "${CHROOT_OUTPUT}/grml-live/sources/"
676       mount --bind "${OUTPUT}/grml_sources/" "${CHROOT_OUTPUT}/grml-live/sources/"
677
678       log "Executed FAI command line:"
679       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"
680       BUILD_ONLY="$BUILD_ONLY" BOOTSTRAP_ONLY="$BOOTSTRAP_ONLY" GRML_LIVE_CONFIG="$CONFIGDUMP" fai $VERBOSE \
681                   -C "$GRML_FAI_CONFIG" -s "file:///$GRML_FAI_CONFIG/config" -c"$CLASSES" \
682                   -u "$HOSTNAME" "$FAI_ACTION" "$CHROOT_OUTPUT" $FAI_ARGS | tee -a $LOGFILE
683       RC="$PIPESTATUS" # notice: bash-only
684
685       # provide inform fai about the ISO we build, needs to be provided
686       # *after* FAI stage, otherwise FAI skips the debootstrap stage if
687       # there is not BASEFILE (as it checks for presence of /etc) :(
688       echo '# This file has been generated by grml-live.' > "$CHROOT_OUTPUT/etc/grml_live_version"
689       [ -n "$GRML_LIVE_VERSION" ] && echo "GRML_LIVE_VERSION=$GRML_LIVE_VERSION" >> "$CHROOT_OUTPUT/etc/grml_live_version"
690       [ -n "$SUITE" ] && echo "SUITE=$SUITE" >> "$CHROOT_OUTPUT/etc/grml_live_version"
691
692       FORCE_ISO_REBUILD=true
693
694       if [ "$RC" != 0 ] ; then
695          log    "Error: critical error while executing fai [exit code ${RC}]. Exiting."
696          eerror "Error: critical error while executing fai [exit code ${RC}]. Exiting." ; eend 1
697          bailout 1
698       fi
699
700       # move fai logs into grml_logs directory
701       mkdir -p "$LOG_OUTPUT"/fai/
702       cp -r "$CHROOT_OUTPUT"/var/log/fai/"$HOSTNAME"/last/* "$LOG_OUTPUT"/fai/
703       rm -rf "$CHROOT_OUTPUT"/var/log/fai
704
705       # store copy of autogenerated configuration file
706       cp ${GRML_FAI_CONFIG}/nfsroot.conf "$LOG_OUTPUT"/fai/
707
708       # copy fai package list
709       cp "$CHROOT_OUTPUT"/var/log/install_packages.list "$LOG_OUTPUT"/fai/
710       # fixup owners
711       chown root:adm "$LOG_OUTPUT"/fai/*
712       chmod 664 "$LOG_OUTPUT"/fai/*
713
714       umount_all
715
716       # notice: 'fai dirinstall' does not seem to exit appropriate, so:
717       ERROR=''
718       CHECKLOG="$LOG_OUTPUT"/fai/
719       if [ -r "$CHECKLOG/software.log" ] ; then
720          # 1 errors during executing of commands
721          grep 'dpkg: error processing' $CHECKLOG/software.log >> $LOGFILE && ERROR=1
722          grep 'E: Method http has died unexpectedly!' $CHECKLOG/software.log >> $LOGFILE && ERROR=2
723          grep 'ERROR: chroot' $CHECKLOG/software.log >> $LOGFILE && ERROR=3
724          grep 'E: Failed to fetch' $CHECKLOG/software.log >> $LOGFILE && ERROR=4
725          grep 'Unable to write mmap - msync (28 No space left on device)' $CHECKLOG/software.log >> $LOGFILE && ERROR=5
726       fi
727
728       if [ -r "$CHECKLOG/shell.log" ] ; then
729          grep 'FAILED with exit code' $CHECKLOG/shell.log >> $LOGFILE && ERROR=6
730       fi
731
732       if [ -n "$ERROR" ] ; then
733          log    "Error: there was a critical error [${ERROR}] during execution of stage 'fai dirinstall' [$(date)]"
734          eerror "Error: there was a critical error during execution of stage 'fai dirinstall'"
735          eerror "Note:  check out ${CHECKLOG}/ for details. [exit ${ERROR}]"
736          eend 1
737          bailout 1
738       else
739          log "Finished execution of stage 'fai dirinstall' [$(date)]"
740          einfo "Finished execution of stage 'fai dirinstall'"
741       fi
742    fi
743 fi # BUILD_DIRTY?
744 # }}}
745
746 # package validator {{{
747 CHECKLOG=/var/log/fai/$HOSTNAME/last
748 if [ -r "$CHECKLOG/dpkg.selections" ] ; then
749   package_count=$(wc -l "$CHECKLOG/dpkg.selections" | awk '{print $1}')
750 else
751   package_count="unknown"
752 fi
753
754 mkdir -p "$REPORTS"
755 REPORT_MISSING_PACKAGES="${REPORTS}/TEST-MissingPackages.xml"
756
757 # check for missing packages
758 if ! [ -s "$CHECKLOG/package_errors.log" ] ; then
759   einfo "No missing packages found, generating empty junit report."
760
761   cat > "${REPORT_MISSING_PACKAGES}" << EOF
762 <?xml version="1.0" encoding="UTF-8"?>
763 <testsuite name="grml-live-missing-packages" tests="${package_count}" time="1" failures="0" errors="0" skipped="0" assertions="0">
764   <testcase name="test_missing_packages" time="0" assertions="0">
765   </testcase>
766   <system-out>
767   </system-out>
768   <system-err>
769   </system-err>
770 </testsuite>
771 EOF
772   eend 0
773 else
774   einfo "Missing packages found, generating junit report."
775
776   if [ -r "$CHECKLOG/package_errors.log" ] ; then
777     package_errors=$(wc -l "$CHECKLOG/package_errors.log" | awk '{print $1}')
778   else
779     package_errors="unknown"
780   fi
781
782   mkdir -p "$REPORTS"
783   REPORT_MISSING_PACKAGES="${REPORTS}/TEST-MissingPackages.xml"
784
785   cat > "${REPORT_MISSING_PACKAGES}" << EOF
786 <?xml version="1.0" encoding="UTF-8"?>
787 <testsuite name="grml-live-missing-packages" tests="${package_count}" time="1" failures="${package_errors}" errors="${package_errors}" skipped="0" assertions="0">
788 EOF
789
790   for package in $(awk '{print $1}' "${CHECKLOG}/package_errors.log") ; do
791     failure_reason="$(awk "/$package/ {print \$2}" "${CHECKLOG}/package_errors.log")"
792     cat >> "${REPORT_MISSING_PACKAGES}" << EOF
793   <testcase name="test_missing_packages_${package}" time="0" assertions="0">
794     <failure type="${failure_reason}" message="Package ${package} is missing">
795 Package $package is missing in chroot (${failure_reason})
796   </failure>
797   </testcase>
798 EOF
799   done
800
801   cat >> "${REPORT_MISSING_PACKAGES}" << EOF
802   <system-out>
803   </system-out>
804   <system-err>
805   </system-err>
806 </testsuite>
807 EOF
808   eend 0
809
810   if [ -n "$EXIT_ON_MISSING_PACKAGES" -a -z "$BUILD_DIRTY" ] ; then
811     eerror "The following packages were requested for installation but could not be processed:"
812     cat "$CHECKLOG/package_errors.log"
813     eerror "... exiting as requested via \$EXIT_ON_MISSING_PACKAGES."
814     eend 1
815     bailout 13
816   else
817     ewarn "The following packages were requested for installation but could not be processed:"
818     cat "$CHECKLOG/package_errors.log"
819     eend 0
820   fi
821 fi
822 # }}}
823
824 # BUILD_OUTPUT - execute arch specific stuff and squashfs {{{
825 [ -n "$BUILD_OUTPUT" ] || BUILD_OUTPUT="$OUTPUT/grml_cd"
826 mkdir -p "$BUILD_OUTPUT" || bailout 6 "Problem with creating $BUILD_OUTPUT for stage ARCH"
827
828 # prepare ISO
829 if [ "$ARCH" = i386 ] || [ "$ARCH" = amd64 ] ; then
830   if [ -n "$BOOTSTRAP_ONLY" ] ; then
831      log   "Skipping stage 'boot' as building with bootstrap only."
832      ewarn "Skipping stage 'boot' as building with bootstrap only." ; eend 0
833   else
834     if [ -d "$BUILD_OUTPUT"/boot/isolinux -a -z "$UPDATE" -a -z "$BUILD_ONLY" ] ; then
835        log   "Skipping stage 'boot' as $BUILD_OUTPUT/boot/isolinux exists already."
836        ewarn "Skipping stage 'boot' as $BUILD_OUTPUT/boot/isolinux exists already." ; eend 0
837     else
838        # booting stuff:
839        [ -d "$BUILD_OUTPUT"/boot/isolinux ] || mkdir -p "$BUILD_OUTPUT"/boot/isolinux
840        [ -d "$BUILD_OUTPUT"/boot/"${SHORT_NAME}" ] || mkdir -p "$BUILD_OUTPUT"/boot/"${SHORT_NAME}"
841
842        # if we don't have an initrd we a) can't boot and b) there was an error
843        # during build, so check for the file:
844        INITRD="$(ls $CHROOT_OUTPUT/boot/initrd* 2>/dev/null| grep -v '.bak$' | sort -r | head -1)"
845        if [ -n "$INITRD" ] ; then
846           cp $INITRD "$BUILD_OUTPUT"/boot/"${SHORT_NAME}"/initrd.img
847           find $CHROOT_OUTPUT/boot/ -name initrd\*.bak -exec rm {} \;
848        else
849           log    "Error: No initrd found inside $CHROOT_OUTPUT/boot/ - Exiting"
850           eerror "Error: No initrd found inside $CHROOT_OUTPUT/boot/ - Exiting" ; eend 1
851           bailout 10
852        fi
853
854        KERNEL_IMAGE="$(ls $CHROOT_OUTPUT/boot/vmlinuz* 2>/dev/null | sort -r | head -1)"
855        if [ -n "$KERNEL_IMAGE" ] ; then
856           cp "$KERNEL_IMAGE" "$BUILD_OUTPUT"/boot/"${SHORT_NAME}"/vmlinuz
857        else
858           log    "Error: No kernel found inside $CHROOT_OUTPUT/boot/ - Exiting"
859           eerror "Error: No kernel found inside $CHROOT_OUTPUT/boot/ - Exiting" ; eend 1
860           bailout 11
861        fi
862
863        # EFI boot files
864        if [ -r "${CHROOT_OUTPUT}/boot/efi.img" -a -r "${CHROOT_OUTPUT}/boot/bootx64.efi" ] ; then
865           einfo "Moving EFI boot files into ISO path."
866           log "Moving EFI boot files into ISO path."
867           RC=$0
868           mv "${CHROOT_OUTPUT}/boot/efi.img" "${BUILD_OUTPUT}/boot/" || RC=$?
869           mkdir -p "${BUILD_OUTPUT}/efi/boot/" || RC=$?
870           mv "${CHROOT_OUTPUT}/boot/bootx64.efi" "${BUILD_OUTPUT}/efi/boot/bootx64.efi" || RC=$?
871           eend $?
872        fi
873
874        [ -n "$TEMPLATE_DIRECTORY" ] || TEMPLATE_DIRECTORY='/usr/share/grml-live/templates'
875        if ! [ -d "${TEMPLATE_DIRECTORY}"/boot ] ; then
876           log    "Error: ${TEMPLATE_DIRECTORY}/boot does not exist. Exiting."
877           eerror "Error: ${TEMPLATE_DIRECTORY}/boot does not exist. Exiting." ; eend 1
878           bailout 8
879        fi
880
881        # copy _required_ isolinux files
882        for file in ifcpu64.c32 isolinux.bin vesamenu.c32; do
883          copy_addon_file "${file}" /usr/lib/syslinux isolinux
884        done
885
886        # *always* copy files to output directory so the variables
887        # get adjusted according to the build.
888        cp ${TEMPLATE_DIRECTORY}/boot/isolinux/*  "$BUILD_OUTPUT"/boot/isolinux/
889
890        if [ -n "$NO_ADDONS" ] ; then
891           log   "Skipping installation of boot addons as requested via \$NO_ADDONS."
892           einfo "Skipping installation of boot addons as requested via \$NO_ADDONS."; eend 0
893        else
894           if ! [ -d "$TEMPLATE_DIRECTORY"/boot/addons ] ; then
895             log   "Boot addons not found, skipping therefore. (Consider installing package grml-live-addons)"
896             ewarn "Boot addons not found, skipping therefore. (Consider installing package grml-live-addons)" ; eend 0
897           else
898             # copy addons from system packages or grml-live-compat
899             copy_addon_file ipxe.lkrn /usr/lib/ipxe addons
900             copy_addon_file pci.ids /usr/share/misc addons
901             copy_addon_file memtest86+.bin /boot addons
902             for file in memdisk chain.c32 hdt.c32 mboot.c32 menu.c32; do
903               copy_addon_file "${file}" /usr/lib/syslinux addons
904             done
905
906             # make memtest filename FAT16/8.3 compatible
907             mv "${BUILD_OUTPUT}/boot/addons/memtest86+.bin" \
908               "${BUILD_OUTPUT}/boot/addons/memtest"
909
910             # copy only files so we can handle bsd4grml on its own
911             for file in ${TEMPLATE_DIRECTORY}/boot/addons/* ; do
912               test -f $file && cp $file "$BUILD_OUTPUT"/boot/addons/
913             done
914
915             if [ -n "$NO_ADDONS_BSD4GRML" ] ; then
916                log   "Skipping installation of bsd4grml as requested via \$NO_ADDONS_BSD4GRML."
917                einfo "Skipping installation of bsd4grml as requested via \$NO_ADDONS_BSD4GRML."; eend 0
918             else
919                if [ -d "$TEMPLATE_DIRECTORY"/boot/addons/bsd4grml ] ; then
920                  cp -a ${TEMPLATE_DIRECTORY}/boot/addons/bsd4grml "$BUILD_OUTPUT"/boot/addons/
921                else
922                  log   "Missing addon file: bsd4grml"
923                  ewarn "Missing addon file: bsd4grml" ; eend 0
924                fi
925             fi
926
927           fi # no "$TEMPLATE_DIRECTORY"/boot/addons
928        fi # NO_ADDONS
929
930        if ! [ -d "${BUILD_OUTPUT}/boot/grub" ] ; then
931          mkdir -p "${BUILD_OUTPUT}/boot/grub"
932        fi
933        cp -a ${TEMPLATE_DIRECTORY}/boot/grub/* "$BUILD_OUTPUT"/boot/grub/
934
935        # generate loopback.cfg config file without depending on grub's regexp module
936        # which isn't available in Debian/squeeze
937        echo "## grub2 loopback configuration" > "${BUILD_OUTPUT}"/boot/grub/loopback.cfg
938        echo "source /boot/grub/header.cfg" >> "${BUILD_OUTPUT}"/boot/grub/loopback.cfg
939        for config in "${BUILD_OUTPUT}"/boot/grub/*_default.cfg "${BUILD_OUTPUT}"/boot/grub/*_options.cfg ; do
940          [ -r "$config" ] || continue
941          echo "source ${config##$BUILD_OUTPUT}" >> "${BUILD_OUTPUT}"/boot/grub/loopback.cfg
942        done
943        echo "source /boot/grub/addons.cfg" >> "${BUILD_OUTPUT}"/boot/grub/loopback.cfg
944        echo "source /boot/grub/footer.cfg" >> "${BUILD_OUTPUT}"/boot/grub/loopback.cfg
945
946        # copy grub files from target
947        cp -a "${CHROOT_OUTPUT}"/usr/lib/grub/*-pc/*.mod "${BUILD_OUTPUT}"/boot/grub/
948        cp -a "${CHROOT_OUTPUT}"/usr/lib/grub/*-pc/*.o "${BUILD_OUTPUT}"/boot/grub/
949        cp -a "${CHROOT_OUTPUT}"/usr/lib/grub/*-pc/*.lst "${BUILD_OUTPUT}"/boot/grub/
950        cp -a "${CHROOT_OUTPUT}"/usr/share/grub/ascii.pf2 "${BUILD_OUTPUT}"/boot/grub/
951        cp -a "${CHROOT_OUTPUT}"/boot/grub/core.img "${BUILD_OUTPUT}"/boot/grub/
952        cp -a "${CHROOT_OUTPUT}"/boot/grub/grub.img "${BUILD_OUTPUT}"/boot/grub/
953
954        if ! [ -d "${TEMPLATE_DIRECTORY}"/GRML ] ; then
955           log    "Error: ${TEMPLATE_DIRECTORY}/GRML does not exist. Exiting."
956           eerror "Error: ${TEMPLATE_DIRECTORY}/GRML does not exist. Exiting." ; eend 1
957           bailout 9
958        fi
959
960        mkdir -p "$BUILD_OUTPUT"/GRML/"${GRML_NAME}"/
961        cp -a ${TEMPLATE_DIRECTORY}/GRML/* "$BUILD_OUTPUT"/GRML/"${GRML_NAME}"/
962
963        # adjust boot splash information:
964        RELEASE_INFO="$GRML_NAME $VERSION - Release Codename $RELEASENAME"
965        RELEASE_INFO="$(cut_string 68 "$RELEASE_INFO")"
966        RELEASE_INFO="$(extend_string_end 68 "$RELEASE_INFO")"
967
968        if [ -r "$BUILD_OUTPUT"/GRML/"${GRML_NAME}"/grml-version ] ; then
969           sed -i "s/%RELEASE_INFO%/$GRML_NAME $VERSION - $RELEASENAME/" "$BUILD_OUTPUT"/GRML/"${GRML_NAME}"/grml-version
970           sed -i "s/%DATE%/$DATE/"                                      "$BUILD_OUTPUT"/GRML/"${GRML_NAME}"/grml-version
971        fi
972
973        # make sure the squashfs filename is set accordingly:
974        SQUASHFS_NAME="$GRML_NAME.squashfs"
975
976        if [ -n "$NO_BOOTID" ] ; then
977           log   'Skipping bootid feature as requested via $NO_BOOTID.'
978           einfo 'Skipping bootid feature as requested via $NO_BOOTID.'
979        else
980           [ -n "$BOOTID" ] || BOOTID="$(echo ${GRML_NAME}${VERSION} | tr -d ',./;\- ')"
981           [ -d "$BUILD_OUTPUT"/conf ] || mkdir "$BUILD_OUTPUT"/conf
982           einfo "Generating /conf/bootid.txt with entry ${BOOTID}."
983           log   "Generating /conf/bootid.txt with entry ${BOOTID}."
984           echo "$BOOTID" > "$BUILD_OUTPUT"/conf/bootid.txt
985           eend $?
986        fi
987
988        # adjust all variables in the templates with the according distribution information
989        for file in "${BUILD_OUTPUT}"/boot/isolinux/*.cfg "${BUILD_OUTPUT}"/boot/isolinux/*.msg \
990                    "${BUILD_OUTPUT}"/boot/grub/* ; do
991          if [ -r "${file}" ] && [ -f "${file}" ] ; then
992            sed -i "s/%ARCH%/$ARCH/g"                    "${file}"
993            sed -i "s/%DATE%/$DATE/g"                    "${file}"
994            sed -i "s/%DISTRI_INFO%/$DISTRI_INFO/g"      "${file}"
995            sed -i "s/%DISTRI_NAME%/$DISTRI_NAME/g"      "${file}"
996            sed -i "s/%DISTRI_SPLASH%/$DISTRI_SPLASH/g"  "${file}"
997            sed -i "s/%GRML_NAME%/$GRML_NAME/g"          "${file}"
998            sed -i "s/%SQUASHFS_NAME%/$SQUASHFS_NAME/g"  "${file}"
999            sed -i "s/%RELEASE_INFO%/$RELEASE_INFO/g"    "${file}"
1000            sed -i "s/%SHORT_NAME%/$SHORT_NAME/g"        "${file}"
1001            sed -i "s/%VERSION%/$VERSION/g"              "${file}"
1002
1003            [ -n "$DEFAULT_BOOTOPTIONS" ] && sed -i "s; boot=live; boot=live $DEFAULT_BOOTOPTIONS;"  "${file}"
1004
1005            if [ -n "$NO_BOOTID" ] ; then
1006               sed -i "s/ bootid=%BOOTID%//g" "${file}" # drop bootid bootoption
1007            else
1008               sed -i "s/%BOOTID%/$BOOTID/g" "${file}" # adjust bootid=... argument
1009            fi
1010          fi
1011        done
1012
1013        for param in ARCH DATE DISTRI_INFO DISTRI_NAME DISTRI_SPLASH GRML_NAME SQUASHFS_NAME \
1014            RELEASE_INFO SHORT_NAME VERSION ; do
1015            for file in $(find "${BUILD_OUTPUT}" -name "*%$param%*") ; do
1016                value="$(eval echo '$'"$param")"
1017                mv ${file} ${file/\%${param}\%/$value}
1018            done
1019        done
1020
1021        # adjust bootsplash accordingly but make sure the string has the according lenght
1022        SQUASHFS_NAME="$(cut_string 20 "$SQUASHFS_NAME")"
1023        SQUASHFS_NAME="$(extend_string_end 20 "$SQUASHFS_NAME")"
1024        for file in f4 f5 ; do
1025           if [ -r "${BUILD_OUTPUT}/boot/isolinux/${file}" ] ; then
1026              sed -i "s/%SQUASHFS_NAME%/$SQUASHFS_NAME/" "${BUILD_OUTPUT}/boot/isolinux/${file}"
1027              sed -i "s/%SQUASHFS_NAME%/$SQUASHFS_NAME/" "${BUILD_OUTPUT}/boot/isolinux/${file}"
1028           fi
1029        done
1030
1031        # generate addon list
1032        rm -f "${BUILD_OUTPUT}/${ADDONS_LIST_FILE}"
1033        for name in "${BUILD_OUTPUT}"/boot/isolinux/addon_*.cfg ; do
1034          include_name=$(basename "$name")
1035          echo "include $include_name"  >> "${BUILD_OUTPUT}/${ADDONS_LIST_FILE}"
1036        done
1037
1038        if ! [ -r "${BUILD_OUTPUT}/boot/isolinux/${DISTRI_NAME}.cfg" ] || [ "$DISTRI_NAME" = "grml" ] ; then
1039           log "including grmlmain.cfg in ${BUILD_OUTPUT}/boot/isolinux/distri.cfg"
1040           echo "include grmlmain.cfg"    >  "${BUILD_OUTPUT}/boot/isolinux/distri.cfg"
1041           echo "include default.cfg"     >  "${BUILD_OUTPUT}/boot/isolinux/grmlmain.cfg"
1042           echo "include menuoptions.cfg" >> "${BUILD_OUTPUT}/boot/isolinux/grmlmain.cfg"
1043           echo "include grml.cfg"        >> "${BUILD_OUTPUT}/boot/isolinux/grmlmain.cfg"
1044
1045           for f in "${BUILD_OUTPUT}"/boot/isolinux/submenu*.cfg ; do
1046             echo "include $(basename $f)"     >> "${BUILD_OUTPUT}/boot/isolinux/grmlmain.cfg"
1047           done
1048
1049           echo "include options.cfg"     >> "${BUILD_OUTPUT}/boot/isolinux/grmlmain.cfg"
1050           if [ ! -n "$NO_ADDONS" ] ; then
1051             echo "include addons.cfg"    >> "${BUILD_OUTPUT}/boot/isolinux/grmlmain.cfg"
1052           fi
1053           echo "include isoprompt.cfg"   >> "${BUILD_OUTPUT}/boot/isolinux/grmlmain.cfg"
1054           echo "include hd.cfg"          >> "${BUILD_OUTPUT}/boot/isolinux/grmlmain.cfg"
1055           echo "include hidden.cfg"      >> "${BUILD_OUTPUT}/boot/isolinux/grmlmain.cfg"
1056        else # assume we are building a custom distribution:
1057           log "File ${BUILD_OUTPUT}/boot/isolinux/${DISTRI_NAME}.cfg found, using it."
1058           einfo "File ${BUILD_OUTPUT}/boot/isolinux/${DISTRI_NAME}.cfg found, using it."
1059           if grep -q "^include ${DISTRI_NAME}.cfg" "${BUILD_OUTPUT}/boot/isolinux/distri.cfg" ; then
1060             log "include for ${DISTRI_NAME}.cfg already present, nothing to do."
1061             eindent
1062             einfo "include for ${DISTRI_NAME}.cfg already present, nothing to do."
1063             eoutdent
1064             eend $?
1065          else
1066             log "including ${DISTRI_NAME}.cfg in ${BUILD_OUTPUT}/boot/isolinux/distri.cfg"
1067             echo "include ${DISTRI_NAME}.cfg" > "${BUILD_OUTPUT}/boot/isolinux/distri.cfg"
1068             [ -n "$NO_ADDONS" ] || echo "include addons.cfg" >> "${BUILD_OUTPUT}/boot/isolinux/distri.cfg"
1069           fi
1070        fi
1071
1072        # use old style console based isolinux method only if requested:
1073        if [[ "${ISOLINUX_METHOD}" == "console" ]] ; then
1074           log 'Using console based isolinux method as requested via $ISOLINUX_METHOD.'
1075           einfo 'Using console based isolinux method as requested via $ISOLINUX_METHOD.'
1076           if grep -q '^include console.cfg' "${BUILD_OUTPUT}/boot/isolinux/distri.cfg" ; then
1077             einfo "include for console.cfg already found, nothing to do."
1078             eend 0
1079           else
1080             log "including console.cfg in ${BUILD_OUTPUT}/boot/isolinux/isolinux.cfg"
1081             einfo "including console.cfg in ${BUILD_OUTPUT}/boot/isolinux/isolinux.cfg"
1082             echo "include console.cfg" >> "${BUILD_OUTPUT}/boot/isolinux/isolinux.cfg"
1083             eend $?
1084           fi
1085        else
1086           log 'Using graphical boot menu.'
1087           if grep -q '^include vesamenu.cfg' "${BUILD_OUTPUT}/boot/isolinux/isolinux.cfg" ; then
1088             log "include for vesamenu.cfg already found, nothing to do."
1089           else
1090             log "including vesamenu.cfg in ${BUILD_OUTPUT}/boot/isolinux/isolinux.cfg"
1091             echo "include vesamenu.cfg" >> "${BUILD_OUTPUT}/boot/isolinux/isolinux.cfg"
1092           fi
1093        fi
1094
1095        if [ -e "$BUILD_OUTPUT"/boot/addons/bsd4grml/boot.6 ]; then
1096           sed -i "s/%RELEASE_INFO%/$GRML_NAME $VERSION - $RELEASENAME/" "$BUILD_OUTPUT"/boot/addons/bsd4grml/boot.6
1097        fi
1098
1099        DPKG_LIST="/var/log/fai/$HOSTNAME/last/dpkg.list" # the dpkg --list output of the chroot
1100        if ! [ -r "$DPKG_LIST" ] ; then
1101           ewarn "$DPKG_LIST could not be read, ignoring to store package information on ISO therefore."
1102        else
1103           einfo "Storing package list information as /GRML/${GRML_NAME}/packages.txt on ISO."
1104           cp "$DPKG_LIST" "${BUILD_OUTPUT}"/GRML/"${GRML_NAME}"/packages.txt
1105           eend $?
1106        fi
1107
1108        # autostart for Windows:
1109        if [ -d "${TEMPLATE_DIRECTORY}/windows/autostart/" ] ; then
1110           cp ${TEMPLATE_DIRECTORY}/windows/autostart/* "$BUILD_OUTPUT"/
1111        fi
1112
1113     FORCE_ISO_REBUILD=true
1114     einfo "Finished execution of stage 'boot'" ; eend 0
1115     fi
1116   fi # BOOTSTRAP_ONLY
1117 else
1118    log    'Error: Unsupported ARCH, sorry. Want to support it? Contribute!'
1119    eerror 'Error: Unsupported ARCH, sorry. Want to support it? Contribute!' ; eend 1
1120    bailout
1121 fi
1122
1123 # support installation of local files into the chroot/ISO
1124 if [ -n "$CHROOT_INSTALL" ] ; then
1125   if ! [ -d "$CHROOT_INSTALL" ] ; then
1126      log "Configuration variable \$CHROOT_INSTALL is set but not a directory; ignoring"
1127      ewarn "Configuration variable \$CHROOT_INSTALL is set but not a directory; ignoring"
1128   else
1129      log "Copying local files to chroot as requested via \$CHROOT_INSTALL"
1130      einfo "Copying local files to chroot as requested via \$CHROOT_INSTALL"
1131      rsync -avz --inplace "$CHROOT_INSTALL"/ "$CHROOT_OUTPUT/"
1132      eend $?
1133      einfo "Make sure to run squashfs stage, otherwise your local files won't be part of the ISO."
1134      FORCE_ISO_REBUILD=true
1135   fi
1136 fi
1137
1138 if [ -f "$BUILD_OUTPUT"/live/${GRML_NAME}.squashfs -a -z "$UPDATE" -a -z "$BUILD_ONLY" -a -z "$BUILD_DIRTY" ] ; then
1139    log   "Skipping stage 'squashfs' as $BUILD_OUTPUT/live exists already."
1140    ewarn "Skipping stage 'squashfs' as $BUILD_OUTPUT/live exists already." ; eend 0
1141 elif [ -n "$SKIP_MKSQUASHFS" ] ; then
1142    log   "Skipping stage 'squashfs' as requested via option -q or -N"
1143    ewarn "Skipping stage 'squashfs' as requested via option -q or -N" ; eend 0
1144 else
1145    mkdir -p "$BUILD_OUTPUT"/live/"${GRML_NAME}"/
1146    # make sure we don't leave (even an empty) base.tgz:
1147    [ -f "$CHROOT_OUTPUT/base.tgz" ] && rm -f "$CHROOT_OUTPUT/base.tgz"
1148
1149    # if unconfigured default to squashfs-tools' mksquashfs binary
1150    if [ -z "$SQUASHFS_BINARY" ] ; then
1151       SQUASHFS_BINARY='mksquashfs'
1152    fi
1153
1154    if which "$SQUASHFS_BINARY" >/dev/null 2>&1 ; then
1155       log    "Using mksquashfs binary ${SQUASHFS_BINARY}"
1156       einfo  "Using mksquashfs binary ${SQUASHFS_BINARY}" ; eend 0
1157    else
1158       log    "Error: mksquashfs binary ($SQUASHFS_BINARY) not found. Exiting."
1159       eerror "Error: mksquashfs binary ($SQUASHFS_BINARY) not found. Exiting." ; eend 1
1160       bailout
1161    fi
1162
1163    # use sane defaults if $SQUASHFS_OPTIONS isn't set
1164    if [ -z "$SQUASHFS_OPTIONS" ] ; then
1165      # use blocksize 256k as this gives best result with regards to time + compression
1166      SQUASHFS_OPTIONS="-b 256k"
1167
1168      # set lzma/xz compression by default, unless -z option has been specified on command line
1169      if [ -z "$SQUASHFS_ZLIB" ] ; then
1170         SQUASHFS_OPTIONS="$SQUASHFS_OPTIONS -comp xz"
1171      else
1172         SQUASHFS_OPTIONS="$SQUASHFS_OPTIONS -comp gzip"
1173      fi
1174    fi
1175
1176    # support exclusion of files via exclude-file:
1177    if [ -n "$SQUASHFS_EXCLUDES_FILE" -a "$SQUASHFS_EXCLUDES_FILE" ] ; then
1178       SQUASHFS_OPTIONS="$SQUASHFS_OPTIONS -ef $SQUASHFS_EXCLUDES_FILE -wildcards"
1179    fi
1180
1181    # get rid of unnecessary files when building grml-small for final release:
1182    if echo "$CLASSES" | grep -q GRML_SMALL ; then
1183       SQUASHFS_OPTIONS="$SQUASHFS_OPTIONS -e initrd.img* vmlinuz*"
1184    fi
1185
1186    # log stuff
1187    SQUASHFS_STDERR="$(mktemp -t grml-live.XXXXXX)"
1188
1189    # informational stuff
1190    [ -n "$SQUASHFS_OPTIONS" ]  && SQUASHFS_INFO_MSG="$SQUASHFS_OPTIONS"
1191    [ -n "$SQUASHFS_INFO_MSG" ] && SQUASHFS_INFO_MSG="using options: $SQUASHFS_INFO_MSG"
1192    einfo "Squashfs build information: running binary $SQUASHFS_BINARY $SQUASHFS_INFO_MSG"
1193
1194    log "$SQUASHFS_BINARY $CHROOT_OUTPUT/ $BUILD_OUTPUT/live/${GRML_NAME}/${GRML_NAME}.squashfs -noappend $SQUASHFS_OPTIONS"
1195
1196    if $SQUASHFS_BINARY $CHROOT_OUTPUT/ $BUILD_OUTPUT/live/"${GRML_NAME}"/"${GRML_NAME}".squashfs \
1197       -noappend $SQUASHFS_OPTIONS 2>"${SQUASHFS_STDERR}" ; then
1198       echo "${GRML_NAME}.squashfs" > $BUILD_OUTPUT/live/"${GRML_NAME}"/filesystem.module
1199       log "Finished execution of stage 'squashfs' [$(date)]"
1200       einfo "Finished execution of stage 'squashfs'" ; eend 0
1201    else
1202       log    "Error: there was a critical error executing stage 'squashfs' [$(date)]:"
1203       log    "$(cat $SQUASHFS_STDERR)"
1204       eerror "Error: there was a critical error executing stage 'squashfs':"
1205       cat    "${SQUASHFS_STDERR}"
1206       eend 1
1207       bailout
1208    fi
1209
1210    FORCE_ISO_REBUILD=true
1211 fi
1212
1213 # create md5sum file:
1214 if [ -z "$BOOTSTRAP_ONLY" ] ; then
1215   ( cd $BUILD_OUTPUT/GRML/"${GRML_NAME}" &&
1216   find .. -type f -not -name md5sums -not -name isolinux.bin -exec md5sum {} \; > md5sums )
1217 fi
1218 # }}}
1219
1220 # ISO_OUTPUT - mkisofs {{{
1221 [ -n "$ISO_OUTPUT" ] || ISO_OUTPUT="$OUTPUT/grml_isos"
1222 [ -n "$ISO_NAME" ] || ISO_NAME="${GRML_NAME}_${VERSION}.iso"
1223
1224 if [ "$BOOT_METHOD" = "isolinux" ] ; then
1225    BOOT_ARGS="-no-emul-boot -boot-load-size 4 -boot-info-table -b boot/isolinux/isolinux.bin -c boot/isolinux/boot.cat"
1226 elif [ "$BOOT_METHOD" = "grub2" ] ; then
1227    BOOT_ARGS="-no-emul-boot -boot-load-size 4 -b boot/grub/toriboot.bin"
1228 fi
1229
1230 # Work around http://bts.grml.org/grml/issue945
1231 if [[ $BOOT_METHOD != isolinux && ($HYBRID_METHOD = isohybrid || $HYBRID_METHOD = manifold) ]]; then
1232   log   "Setting HYBRID_METHOD to grub2 as hybrid mode does not work with isohybrid yet."
1233   ewarn "Setting HYBRID_METHOD to grub2 as hybrid mode does not work with isohybrid yet."
1234   HYBRID_METHOD='grub2'
1235   eend 0
1236 fi
1237
1238 if [ -f "${ISO_OUTPUT}/${ISO_NAME}" -a -z "$UPDATE" -a -z "$BUILD_ONLY" -a -z "$BUILD_DIRTY" -a "$FORCE_ISO_REBUILD" = "false" ]  ; then
1239    log   "Skipping stage 'iso build' as $ISO_OUTPUT/${ISO_NAME} exists already."
1240    ewarn "Skipping stage 'iso build' as $ISO_OUTPUT/${ISO_NAME} exists already." ; eend 0
1241 elif [ -n "$SKIP_MKISOFS" ] ; then
1242    log   "Skipping stage 'iso build' as requested via option -n or -N"
1243    ewarn "Skipping stage 'iso build' as requested via option -n or -N" ; eend 0
1244 else
1245    mkdir -p "$ISO_OUTPUT" || bailout 6 "Problem with creating $ISO_OUTPUT for stage 'iso build'"
1246
1247    if $FORCE_ISO_REBUILD && ! [ -f "${ISO_OUTPUT}/${ISO_NAME}" ] ; then
1248       log   "Forcing rebuild of ISO because files on ISO have been modified."
1249       einfo "Forcing rebuild of ISO because files on ISO have been modified."
1250    fi
1251
1252    # support xorriso as well mkisofs and genisoimage
1253    if which xorriso >/dev/null 2>&1 ; then
1254       MKISOFS='xorriso -as mkisofs'
1255     elif which mkisofs >/dev/null 2>&1; then
1256       MKISOFS='mkisofs'
1257    elif which genisoimage >/dev/null 2>&1; then
1258       MKISOFS='genisoimage'
1259    else
1260       log    "Error: neither xorriso nor mkisofs nor genisoimage available - can not create ISO."
1261       eerror "Error: neither xorriso nor mkisofs nor genisoimage available - can not create ISO." ; eend 1
1262       bailout
1263    fi
1264
1265    einfo "Using ${MKISOFS} to build ISO." ;  eend 0
1266    case "${ARCH}-${MKISOFS}" in
1267      # using -eltorito-alt-boot is limited to xorriso for now
1268      amd64-xorriso*)
1269        eindent
1270
1271        if ! dpkg --compare-versions $(dpkg-query -W -f='${Version}\n' xorriso 2>/dev/null) gt-nl 1.1.6-1 ; then
1272          log   "Disabling (U)EFI boot support because xorriso version is too old."
1273          ewarn "Disabling (U)EFI boot support because xorriso version is too old." ; eend 0
1274        else
1275          if [ -r "${BUILD_OUTPUT}"/boot/efi.img ] ; then
1276            einfo "Enabling (U)EFI boot."
1277            log   "Enabling (U)EFI boot."
1278            BOOT_ARGS="$BOOT_ARGS -boot-info-table -eltorito-alt-boot -e boot/efi.img -no-emul-boot"
1279            eend $?
1280          else
1281            log   "Disabling (U)EFI boot support because /boot/efi.img is missing."
1282            ewarn "Disabling (U)EFI boot support because /boot/efi.img is missing." ; eend 0
1283          fi
1284        fi
1285
1286        eoutdent
1287        ;;
1288    esac
1289
1290    CURRENT_DIR=$(pwd)
1291    if cd "$BUILD_OUTPUT" ; then
1292       if [ "$BOOT_METHOD" = "grub2" ]; then
1293          # make a 2048-byte bootsector for El Torito
1294          dd if=/dev/zero of=boot/grub/toriboot.bin bs=512 count=4 2>/dev/null
1295          # those are in 2048-byte sectors, so 1 16 matches 4 63 below
1296          echo 1 16 | mksh /usr/share/grml-live/scripts/bootgrub.mksh -B 11 | \
1297             dd of=boot/grub/toriboot.bin conv=notrunc 2>/dev/null
1298       fi
1299       log "$MKISOFS -V '${GRML_NAME} ${VERSION}' -publisher 'grml-live | grml.org' -l -r -J $BOOT_ARGS -o ${ISO_OUTPUT}/${ISO_NAME} ."
1300       $MKISOFS -V "${GRML_NAME} ${VERSION}" -publisher 'grml-live | grml.org' \
1301               -l -r -J $BOOT_ARGS -no-pad \
1302               -o "${ISO_OUTPUT}/${ISO_NAME}" . ; RC=$?
1303       # both of these need core.img there, so it’s easier to write it here
1304       if [ "$BOOT_METHOD" = "grub2" ] || [ "$HYBRID_METHOD" = "grub2" ]; then
1305          # must be <= 30720 bytes
1306          dd if=boot/grub/core.img of="${ISO_OUTPUT}/${ISO_NAME}" \
1307            conv=notrunc bs=512 seek=4 2>/dev/null
1308       fi
1309
1310       # pad the output ISO to multiples of 256 KiB for partition table support
1311       siz=$($getfilesize "${ISO_OUTPUT}/${ISO_NAME}")
1312       cyls=$((siz / 512 / 32 / 16 + 1))   # C=$cyls H=16 S=32
1313       siz=$((cyls * 16 * 32 * 512))   # size after padding
1314       dd if=/dev/zero bs=1 count=1 seek=$((siz - 1)) \
1315          of="${ISO_OUTPUT}/${ISO_NAME}" 2>/dev/null
1316
1317       # support disabling hybrid ISO image
1318       if [ "$HYBRID_METHOD" = "disable" ] ; then
1319         log   "Skipping creation of hybrid ISO file as requested via HYBRID_METHOD=disable"
1320         einfo "Skipping creation of hybrid ISO file as requested via HYBRID_METHOD=disable"
1321         eend 0
1322       elif [ "$HYBRID_METHOD" = "manifold" ] || [ "$HYBRID_METHOD" = "grub2" ] ; then
1323         # isoinfo is part of both mkisofs and genisoimage so we're good
1324         bootoff=$(isoinfo -l -i "${ISO_OUTPUT}/${ISO_NAME}" | \
1325           sed -n '/^.*\[ *\([0-9]*\)[] ].* ISOLINUX.BIN[;1]* *$/s//\1/p')
1326
1327         if ! [ -r boot/grub/core.img ] ; then
1328           log   "boot/grub/core.img not found, not creating manifold boot ISO file"
1329           ewarn "boot/grub/core.img not found, not creating manifold boot ISO file"
1330         elif [ "${bootoff:-0}" -lt 1 ] ; then
1331           log   "isolinux.bin not found on the ISO file, disabling manifold boot"
1332           ewarn "isolinux.bin not found on the ISO file, disabling manifold boot"
1333         else
1334           if [ "$HYBRID_METHOD" = "grub2" ] ; then
1335             log   "Creating hybrid ISO file with manifold/grub2 method"
1336             einfo "Creating hybrid ISO file with manifold/grub2 method"
1337             # 512 bytes: MBR, partition table, load GRUB 2
1338             echo 4 63 | mksh /usr/share/grml-live/scripts/bootgrub.mksh -A -M 4:0x96 -g $cyls:16:32
1339           else
1340             log   "Creating hybrid ISO file with manifold method"
1341             einfo "Creating hybrid ISO file with manifold method"
1342             # read only one but 2048-byte sized (scale: << 2) sector
1343             echo $bootoff $bootoff | \
1344               mksh /usr/share/grml-live/scripts/bootilnx.mksh -A -M 4:0x96 -g $cyls:16:32 -S 2
1345           fi | dd of="${ISO_OUTPUT}/${ISO_NAME}" conv=notrunc 2>/dev/null
1346           eend $?
1347         fi
1348       elif [ "$HYBRID_METHOD" = "isohybrid" ] ; then
1349         if ! which isohybrid >/dev/null 2>&1 ; then
1350           bailout 12 "isohybrid binary not found - please install syslinux/syslinux-common"
1351         else
1352           log   "Creating hybrid ISO file with isohybrid method"
1353           einfo "Creating hybrid ISO file with isohybrid method"
1354           # Notes for consideration:
1355           # "-entry 4 -type 1c"
1356           # * using 4 as the partition number is supposed to help with BIOSes
1357           #   that only support USB-Zip boot
1358           # * using 1c (i.e. hidden FAT32 LBA), instead of the default 0x17
1359           #   (hidden NTFS, IIRC), as the partition type is sometimes needed
1360           #   to get the BIOS even look at the partition created by isohybrid
1361           if isohybrid --help | grep -q -- --uefi ; then
1362             if echo $CLASSES | grep -qw I386 ; then
1363               log   "Detected uefi support for isohybrid but 32bit systems do not support it, ignoring."
1364               einfo "Detected uefi support for isohybrid but 32bit systems do not support it, ignoring."
1365             else
1366               log   "Detected uefi support for isohybrid, enabling"
1367               einfo "Detected uefi support for isohybrid, enabling"
1368               ISOHYBRID_OPTIONS=--uefi
1369             fi
1370           fi
1371
1372           log "isohybrid $ISOHYBRID_OPTIONS ${ISO_OUTPUT}/${ISO_NAME}"
1373           isohybrid $ISOHYBRID_OPTIONS "${ISO_OUTPUT}/${ISO_NAME}"
1374           eend $?
1375         fi
1376       else
1377         bailout 12 "Unknown HYBRID_METHOD [${HYBRID_METHOD}]. Supported values: disable, isohybrid, grub2, manifold"
1378       fi
1379
1380       # generate md5sum and sha1sum of ISO if we are using class 'RELEASE':
1381       case $CLASSES in *RELEASE*)
1382          [ "$RC" = 0 ] && \
1383          (
1384            if cd $ISO_OUTPUT ; then
1385              md5sum ${ISO_NAME} > ${ISO_NAME}.md5 && \
1386              touch -r ${ISO_NAME} ${ISO_NAME}.md5
1387              sha1sum ${ISO_NAME} > ${ISO_NAME}.sha1 && \
1388              touch -r ${ISO_NAME} ${ISO_NAME}.sha1
1389            fi
1390          )
1391          ;;
1392       esac
1393
1394       cd "$CURRENT_DIR"
1395    fi
1396
1397    if [ "$RC" = 0 ] ; then
1398       log   "Finished execution of stage 'iso build' [$(date)]"
1399       einfo "Finished execution of stage 'iso build'" ; eend 0
1400    else
1401       log    "Error: there was a critical error ($RC) executing stage 'iso build' [$(date)]"
1402       eerror "Error: there was a critical error executing stage 'iso build'" ; eend 1
1403       bailout $RC
1404    fi
1405 fi
1406 # }}}
1407
1408 # netboot package {{{
1409 create_netbootpackage() {
1410   local OUTPUT_FILE="${NETBOOT}/grml_netboot_package_${GRML_NAME}_${VERSION}.tar.bz2"
1411
1412   if [ -f "${OUTPUT_FILE}" -a -z "$UPDATE" -a -z "$BUILD_ONLY" -a -z "$BUILD_DIRTY" ] ; then
1413     log   "Skipping stage 'netboot' as $OUTPUT_FILE exists already."
1414     ewarn "Skipping stage 'netboot' as $OUTPUT_FILE exists already." ; eend 0
1415     return 0
1416   elif [ -n "$SKIP_NETBOOT" ] ; then
1417     log   "Skipping stage 'netboot' as requested via option -Q"
1418     ewarn "Skipping stage 'netboot' as requested via option -Q" ; eend 0
1419     return 0
1420   fi
1421
1422   mkdir -p "$NETBOOT"
1423
1424   if ! [ -r "${CHROOT}/usr/lib/syslinux/pxelinux.0" ] ; then
1425     ewarn "File /usr/lib/syslinux/pxelinux.0 not found in build chroot." ; eend 0
1426     eindent
1427     einfo "Install syslinux[-common] package in chroot to get a netboot package."
1428     eoutdent
1429     return 0
1430   fi
1431
1432   local OUTPUTDIR="${NETBOOT}/build_tmp"
1433   local WORKING_DIR="${OUTPUTDIR}/grml_netboot_package_${GRML_NAME}_${VERSION}/tftpboot/"
1434
1435   mkdir -p "$WORKING_DIR"
1436
1437   cp "${CHROOT_OUTPUT}"/boot/vmlinuz-*    "$WORKING_DIR"/vmlinuz
1438   cp "${CHROOT_OUTPUT}"/boot/initrd.img-* "$WORKING_DIR"/initrd.img
1439   cp "${CHROOT_OUTPUT}"/usr/lib/syslinux/pxelinux.0 "${WORKING_DIR}/pxelinux.0"
1440
1441   mkdir -p "${WORKING_DIR}/pxelinux.cfg"
1442   if [ -r "${BUILD_OUTPUT}/boot/isolinux/netboot.cfg" ] ; then
1443     cp "${BUILD_OUTPUT}/boot/isolinux/netboot.cfg" "${WORKING_DIR}/pxelinux.cfg/default"
1444   else
1445     log   "File ${BUILD_OUTPUT}/boot/isolinux/netboot.cfg not found."
1446     ewarn "File ${BUILD_OUTPUT}/boot/isolinux/netboot.cfg not found."
1447     eindent
1448     log   "Hint: Are you using custom templates which do not provide netboot.cfg?"
1449     ewarn "Hint: Are you using custom templates which do not provide netboot.cfg?" ; eend 0
1450     eoutdent
1451   fi
1452
1453   if tar -C "$OUTPUTDIR" -jcf "${OUTPUT_FILE}" "grml_netboot_package_${GRML_NAME}_${VERSION}" ; then
1454     (
1455       cd $(dirname "${OUTPUT_FILE}")
1456       sha1sum $(basename "${OUTPUT_FILE}") > "${OUTPUT_FILE}.sha1"
1457     )
1458     einfo "Generated netboot package ${OUTPUT_FILE}" ; eend 0
1459     rm -rf "${OUTPUTDIR}"
1460   else
1461     rm -rf "${OUTPUTDIR}"
1462     eerror "Could not generate netboot package ${OUTPUT_FILE}" ; eend 1
1463     bailout 21
1464   fi
1465 }
1466
1467 create_netbootpackage
1468 # }}}
1469
1470 # log build information to database if grml-live-db is installed and enabled {{{
1471 dpkg_to_db() {
1472 if [ -d /usr/share/grml-live-db ] ; then
1473
1474   # safe defaults
1475   DPKG_LIST="/var/log/fai/$HOSTNAME/last/dpkg.list" # the dpkg --list output of the chroot:
1476   [ -n "$DPKG_DATABASE" ]  || DPKG_DATABASE=/var/log/grml-live.db
1477   [ -n "$DPKG_DBSCRIPT" ]  || DPKG_DBSCRIPT=/usr/share/grml-live-db/scripts/dpkg-to-db
1478   [ -n "$DPKG_DBOPTIONS" ] || DPKG_DBOPTIONS="--database $DPKG_DATABASE --logfile $LOGFILE --flavour $GRML_NAME --dpkg $DPKG_LIST"
1479
1480   if ! [ -x "$DPKG_DBSCRIPT" ] ; then
1481     log "Error: $DPKG_DBSCRIPT is not executable, can not log dpkg information."
1482     eerror "Error: $DPKG_DBSCRIPT is not executable, can not log dpkg information." ; eend 1
1483     bailout 14
1484   fi
1485
1486   # disable by default for now, not sure whether really everyone is using a local db file
1487   #if ! touch "$DPKG_DATABASE" ; then
1488   #  eerror "Error: can not write to ${DPKG_DATABASE}, can not log dpkg information." ; eend 1
1489   #  bailout 14
1490   #fi
1491
1492   if ! [ -r "$DPKG_LIST" ] ; then
1493      log   "Warning: can not read $DPKG_LIST - can not provide information to $DPKG_DBSCRIPT (dirty build?)"
1494      ewarn "Warning: can not read $DPKG_LIST - can not provide information to $DPKG_DBSCRIPT (dirty build?)" ; eend 0
1495   else
1496      einfo "Logging $DPKG_LIST to database $DPKG_DATABASE"
1497      log "Logging $DPKG_LIST to database $DPKG_DATABASE"
1498      log "Executing $DPKG_DBSCRIPT $DPKG_DBOPTIONS"
1499      eindent
1500
1501      if DB_INFO=$("$DPKG_DBSCRIPT" $DPKG_DBOPTIONS 2>&1) ; then
1502        einfo "$DB_INFO"
1503        eend 0
1504      else
1505        eerror "$DB_INFO"
1506        eend 1
1507      fi
1508
1509      eoutdent
1510   fi
1511
1512 fi
1513 }
1514 # }}}
1515
1516 # finalize {{{
1517 [ -n "$start_seconds" ] && SECONDS="$[$(cut -d . -f 1 /proc/uptime)-$start_seconds]" || SECONDS="unknown"
1518 log "Successfully finished execution of $PN [$(date) - running ${SECONDS} seconds]"
1519
1520 dpkg_to_db # make sure we catch the last log line as well, therefore execute between log + einfo
1521
1522 einfo "Successfully finished execution of $PN [$(date) - running ${SECONDS} seconds]" ; eend 0
1523 bailout 0
1524 # }}}
1525
1526 ## END OF FILE #################################################################
1527 # vim:foldmethod=marker ts=2 ft=sh ai expandtab tw=80 sw=2