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