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