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