Install grub modules into /boot/grub/i386-pc [Testing: issue1297]
[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        # copy _required_ isolinux files
896        for file in ifcpu64.c32 isolinux.bin vesamenu.c32; do
897          copy_addon_file "${file}" /usr/lib/syslinux isolinux
898        done
899
900        # *always* copy files to output directory so the variables
901        # get adjusted according to the build.
902        cp ${TEMPLATE_DIRECTORY}/boot/isolinux/*  "$BUILD_OUTPUT"/boot/isolinux/
903
904        if [ -n "$NO_ADDONS" ] ; then
905           log   "Skipping installation of boot addons as requested via \$NO_ADDONS."
906           einfo "Skipping installation of boot addons as requested via \$NO_ADDONS."; eend 0
907        else
908           if ! [ -d "$TEMPLATE_DIRECTORY"/boot/addons ] ; then
909             log   "Boot addons not found, skipping therefore. (Consider installing package grml-live-addons)"
910             ewarn "Boot addons not found, skipping therefore. (Consider installing package grml-live-addons)" ; eend 0
911           else
912             # copy addons from system packages or grml-live-compat
913             copy_addon_file ipxe.lkrn /usr/lib/ipxe addons
914             copy_addon_file pci.ids /usr/share/misc addons
915             copy_addon_file memtest86+.bin /boot addons
916             for file in memdisk chain.c32 hdt.c32 mboot.c32 menu.c32; do
917               copy_addon_file "${file}" /usr/lib/syslinux addons
918             done
919
920             # make memtest filename FAT16/8.3 compatible
921             mv "${BUILD_OUTPUT}/boot/addons/memtest86+.bin" \
922               "${BUILD_OUTPUT}/boot/addons/memtest"
923
924             # copy only files so we can handle bsd4grml on its own
925             for file in ${TEMPLATE_DIRECTORY}/boot/addons/* ; do
926               test -f $file && cp $file "$BUILD_OUTPUT"/boot/addons/
927             done
928
929             if [ -n "$NO_ADDONS_BSD4GRML" ] ; then
930                log   "Skipping installation of bsd4grml as requested via \$NO_ADDONS_BSD4GRML."
931                einfo "Skipping installation of bsd4grml as requested via \$NO_ADDONS_BSD4GRML."; eend 0
932             else
933                if [ -d "$TEMPLATE_DIRECTORY"/boot/addons/bsd4grml ] ; then
934                  cp -a ${TEMPLATE_DIRECTORY}/boot/addons/bsd4grml "$BUILD_OUTPUT"/boot/addons/
935                else
936                  log   "Missing addon file: bsd4grml"
937                  ewarn "Missing addon file: bsd4grml" ; eend 0
938                fi
939             fi
940
941           fi # no "$TEMPLATE_DIRECTORY"/boot/addons
942        fi # NO_ADDONS
943
944        if ! [ -d "${BUILD_OUTPUT}/boot/grub" ] ; then
945          mkdir -p "${BUILD_OUTPUT}/boot/grub"
946        fi
947        cp -a ${TEMPLATE_DIRECTORY}/boot/grub/* "$BUILD_OUTPUT"/boot/grub/
948
949        # generate loopback.cfg config file without depending on grub's regexp module
950        # which isn't available in Debian/squeeze
951        echo "## grub2 loopback configuration" > "${BUILD_OUTPUT}"/boot/grub/loopback.cfg
952        echo "source /boot/grub/header.cfg" >> "${BUILD_OUTPUT}"/boot/grub/loopback.cfg
953        for config in "${BUILD_OUTPUT}"/boot/grub/*_default.cfg "${BUILD_OUTPUT}"/boot/grub/*_options.cfg ; do
954          [ -r "$config" ] || continue
955          echo "source ${config##$BUILD_OUTPUT}" >> "${BUILD_OUTPUT}"/boot/grub/loopback.cfg
956        done
957        echo "source /boot/grub/addons.cfg" >> "${BUILD_OUTPUT}"/boot/grub/loopback.cfg
958        echo "source /boot/grub/footer.cfg" >> "${BUILD_OUTPUT}"/boot/grub/loopback.cfg
959
960        # copy grub files from target
961        mkdir -p ${BUILD_OUTPUT}"/boot/grub/i386-pc/
962        cp -a "${CHROOT_OUTPUT}"/usr/lib/grub/*-pc/*.mod "${BUILD_OUTPUT}"/boot/grub/i386-pc/
963        cp -a "${CHROOT_OUTPUT}"/usr/lib/grub/*-pc/*.o "${BUILD_OUTPUT}"/boot/grub/i386-pc/
964        cp -a "${CHROOT_OUTPUT}"/usr/lib/grub/*-pc/*.lst "${BUILD_OUTPUT}"/boot/grub/i386-pc/
965        cp -a "${CHROOT_OUTPUT}"/usr/share/grub/ascii.pf2 "${BUILD_OUTPUT}"/boot/grub/
966        cp -a "${CHROOT_OUTPUT}"/boot/grub/core.img "${BUILD_OUTPUT}"/boot/grub/
967        cp -a "${CHROOT_OUTPUT}"/boot/grub/grub.img "${BUILD_OUTPUT}"/boot/grub/
968
969        if ! [ -d "${TEMPLATE_DIRECTORY}"/GRML ] ; then
970           log    "Error: ${TEMPLATE_DIRECTORY}/GRML does not exist. Exiting."
971           eerror "Error: ${TEMPLATE_DIRECTORY}/GRML does not exist. Exiting." ; eend 1
972           bailout 9
973        fi
974
975        mkdir -p "$BUILD_OUTPUT"/GRML/"${GRML_NAME}"/
976        cp -a ${TEMPLATE_DIRECTORY}/GRML/* "$BUILD_OUTPUT"/GRML/"${GRML_NAME}"/
977
978        # adjust boot splash information:
979        RELEASE_INFO="$GRML_NAME $VERSION - Release Codename $RELEASENAME"
980        RELEASE_INFO="$(cut_string 68 "$RELEASE_INFO")"
981        RELEASE_INFO="$(extend_string_end 68 "$RELEASE_INFO")"
982
983        if [ -r "$BUILD_OUTPUT"/GRML/"${GRML_NAME}"/grml-version ] ; then
984           sed -i "s/%RELEASE_INFO%/$GRML_NAME $VERSION - $RELEASENAME/" "$BUILD_OUTPUT"/GRML/"${GRML_NAME}"/grml-version
985           sed -i "s/%DATE%/$DATE/"                                      "$BUILD_OUTPUT"/GRML/"${GRML_NAME}"/grml-version
986        fi
987
988        # make sure the squashfs filename is set accordingly:
989        SQUASHFS_NAME="$GRML_NAME.squashfs"
990
991        if [ -n "$NO_BOOTID" ] ; then
992           log   'Skipping bootid feature as requested via $NO_BOOTID.'
993           einfo 'Skipping bootid feature as requested via $NO_BOOTID.'
994        else
995           [ -n "$BOOTID" ] || BOOTID="$(echo ${GRML_NAME}${VERSION} | tr -d ',./;\- ')"
996           [ -d "$BUILD_OUTPUT"/conf ] || mkdir "$BUILD_OUTPUT"/conf
997           einfo "Generating /conf/bootid.txt with entry ${BOOTID}."
998           log   "Generating /conf/bootid.txt with entry ${BOOTID}."
999           echo "$BOOTID" > "$BUILD_OUTPUT"/conf/bootid.txt
1000           eend $?
1001        fi
1002
1003        # adjust all variables in the templates with the according distribution information
1004        for file in "${BUILD_OUTPUT}"/boot/isolinux/*.cfg "${BUILD_OUTPUT}"/boot/isolinux/*.msg \
1005                    "${BUILD_OUTPUT}"/boot/grub/* ; do
1006          if [ -r "${file}" ] && [ -f "${file}" ] ; then
1007            sed -i "s/%ARCH%/$ARCH/g"                    "${file}"
1008            sed -i "s/%DATE%/$DATE/g"                    "${file}"
1009            sed -i "s/%DISTRI_INFO%/$DISTRI_INFO/g"      "${file}"
1010            sed -i "s/%DISTRI_NAME%/$DISTRI_NAME/g"      "${file}"
1011            sed -i "s/%DISTRI_SPLASH%/$DISTRI_SPLASH/g"  "${file}"
1012            sed -i "s/%GRML_NAME%/$GRML_NAME/g"          "${file}"
1013            sed -i "s/%SQUASHFS_NAME%/$SQUASHFS_NAME/g"  "${file}"
1014            sed -i "s/%RELEASE_INFO%/$RELEASE_INFO/g"    "${file}"
1015            sed -i "s/%SHORT_NAME%/$SHORT_NAME/g"        "${file}"
1016            sed -i "s/%VERSION%/$VERSION/g"              "${file}"
1017
1018            [ -n "$DEFAULT_BOOTOPTIONS" ] && sed -i "s; boot=live; boot=live $DEFAULT_BOOTOPTIONS;"  "${file}"
1019
1020            if [ -n "$NO_BOOTID" ] ; then
1021               sed -i "s/ bootid=%BOOTID%//g" "${file}" # drop bootid bootoption
1022            else
1023               sed -i "s/%BOOTID%/$BOOTID/g" "${file}" # adjust bootid=... argument
1024            fi
1025          fi
1026        done
1027
1028        for param in ARCH DATE DISTRI_INFO DISTRI_NAME DISTRI_SPLASH GRML_NAME SQUASHFS_NAME \
1029            RELEASE_INFO SHORT_NAME VERSION ; do
1030            for file in $(find "${BUILD_OUTPUT}" -name "*%$param%*") ; do
1031                value="$(eval echo '$'"$param")"
1032                mv ${file} ${file/\%${param}\%/$value}
1033            done
1034        done
1035
1036        # adjust bootsplash accordingly but make sure the string has the according lenght
1037        SQUASHFS_NAME="$(cut_string 20 "$SQUASHFS_NAME")"
1038        SQUASHFS_NAME="$(extend_string_end 20 "$SQUASHFS_NAME")"
1039        for file in f4 f5 ; do
1040           if [ -r "${BUILD_OUTPUT}/boot/isolinux/${file}" ] ; then
1041              sed -i "s/%SQUASHFS_NAME%/$SQUASHFS_NAME/" "${BUILD_OUTPUT}/boot/isolinux/${file}"
1042              sed -i "s/%SQUASHFS_NAME%/$SQUASHFS_NAME/" "${BUILD_OUTPUT}/boot/isolinux/${file}"
1043           fi
1044        done
1045
1046        # generate addon list
1047        rm -f "${BUILD_OUTPUT}/${ADDONS_LIST_FILE}"
1048        for name in "${BUILD_OUTPUT}"/boot/isolinux/addon_*.cfg ; do
1049          include_name=$(basename "$name")
1050          echo "include $include_name"  >> "${BUILD_OUTPUT}/${ADDONS_LIST_FILE}"
1051        done
1052
1053        if ! [ -r "${BUILD_OUTPUT}/boot/isolinux/${DISTRI_NAME}.cfg" ] || [ "$DISTRI_NAME" = "grml" ] ; then
1054           log "including grmlmain.cfg in ${BUILD_OUTPUT}/boot/isolinux/distri.cfg"
1055           echo "include grmlmain.cfg"    >  "${BUILD_OUTPUT}/boot/isolinux/distri.cfg"
1056           echo "include default.cfg"     >  "${BUILD_OUTPUT}/boot/isolinux/grmlmain.cfg"
1057           echo "include menuoptions.cfg" >> "${BUILD_OUTPUT}/boot/isolinux/grmlmain.cfg"
1058           echo "include grml.cfg"        >> "${BUILD_OUTPUT}/boot/isolinux/grmlmain.cfg"
1059
1060           for f in "${BUILD_OUTPUT}"/boot/isolinux/submenu*.cfg ; do
1061             echo "include $(basename $f)"     >> "${BUILD_OUTPUT}/boot/isolinux/grmlmain.cfg"
1062           done
1063
1064           echo "include options.cfg"     >> "${BUILD_OUTPUT}/boot/isolinux/grmlmain.cfg"
1065           if [ ! -n "$NO_ADDONS" ] ; then
1066             echo "include addons.cfg"    >> "${BUILD_OUTPUT}/boot/isolinux/grmlmain.cfg"
1067           fi
1068           echo "include isoprompt.cfg"   >> "${BUILD_OUTPUT}/boot/isolinux/grmlmain.cfg"
1069           echo "include hd.cfg"          >> "${BUILD_OUTPUT}/boot/isolinux/grmlmain.cfg"
1070           echo "include hidden.cfg"      >> "${BUILD_OUTPUT}/boot/isolinux/grmlmain.cfg"
1071        else # assume we are building a custom distribution:
1072           log "File ${BUILD_OUTPUT}/boot/isolinux/${DISTRI_NAME}.cfg found, using it."
1073           einfo "File ${BUILD_OUTPUT}/boot/isolinux/${DISTRI_NAME}.cfg found, using it."
1074           if grep -q "^include ${DISTRI_NAME}.cfg" "${BUILD_OUTPUT}/boot/isolinux/distri.cfg" ; then
1075             log "include for ${DISTRI_NAME}.cfg already present, nothing to do."
1076             eindent
1077             einfo "include for ${DISTRI_NAME}.cfg already present, nothing to do."
1078             eoutdent
1079             eend $?
1080          else
1081             log "including ${DISTRI_NAME}.cfg in ${BUILD_OUTPUT}/boot/isolinux/distri.cfg"
1082             echo "include ${DISTRI_NAME}.cfg" > "${BUILD_OUTPUT}/boot/isolinux/distri.cfg"
1083             [ -n "$NO_ADDONS" ] || echo "include addons.cfg" >> "${BUILD_OUTPUT}/boot/isolinux/distri.cfg"
1084           fi
1085        fi
1086
1087        # use old style console based isolinux method only if requested:
1088        if [[ "${ISOLINUX_METHOD}" == "console" ]] ; then
1089           log 'Using console based isolinux method as requested via $ISOLINUX_METHOD.'
1090           einfo 'Using console based isolinux method as requested via $ISOLINUX_METHOD.'
1091           if grep -q '^include console.cfg' "${BUILD_OUTPUT}/boot/isolinux/distri.cfg" ; then
1092             einfo "include for console.cfg already found, nothing to do."
1093             eend 0
1094           else
1095             log "including console.cfg in ${BUILD_OUTPUT}/boot/isolinux/isolinux.cfg"
1096             einfo "including console.cfg in ${BUILD_OUTPUT}/boot/isolinux/isolinux.cfg"
1097             echo "include console.cfg" >> "${BUILD_OUTPUT}/boot/isolinux/isolinux.cfg"
1098             eend $?
1099           fi
1100        else
1101           log 'Using graphical boot menu.'
1102           if grep -q '^include vesamenu.cfg' "${BUILD_OUTPUT}/boot/isolinux/isolinux.cfg" ; then
1103             log "include for vesamenu.cfg already found, nothing to do."
1104           else
1105             log "including vesamenu.cfg in ${BUILD_OUTPUT}/boot/isolinux/isolinux.cfg"
1106             echo "include vesamenu.cfg" >> "${BUILD_OUTPUT}/boot/isolinux/isolinux.cfg"
1107           fi
1108        fi
1109
1110        if [ -e "$BUILD_OUTPUT"/boot/addons/bsd4grml/boot.6 ]; then
1111           sed -i "s/%RELEASE_INFO%/$GRML_NAME $VERSION - $RELEASENAME/" "$BUILD_OUTPUT"/boot/addons/bsd4grml/boot.6
1112        fi
1113
1114        DPKG_LIST="/var/log/fai/$HOSTNAME/last/dpkg.list" # the dpkg --list output of the chroot
1115        if ! [ -r "$DPKG_LIST" ] ; then
1116           ewarn "$DPKG_LIST could not be read, ignoring to store package information on ISO therefore."
1117        else
1118           einfo "Storing package list information as /GRML/${GRML_NAME}/packages.txt on ISO."
1119           cp "$DPKG_LIST" "${BUILD_OUTPUT}"/GRML/"${GRML_NAME}"/packages.txt
1120           eend $?
1121        fi
1122
1123        # autostart for Windows:
1124        if [ -d "${TEMPLATE_DIRECTORY}/windows/autostart/" ] ; then
1125           cp ${TEMPLATE_DIRECTORY}/windows/autostart/* "$BUILD_OUTPUT"/
1126        fi
1127
1128     FORCE_ISO_REBUILD=true
1129     einfo "Finished execution of stage 'boot'" ; eend 0
1130     fi
1131   fi # BOOTSTRAP_ONLY
1132 else
1133    log    'Error: Unsupported ARCH, sorry. Want to support it? Contribute!'
1134    eerror 'Error: Unsupported ARCH, sorry. Want to support it? Contribute!' ; eend 1
1135    bailout
1136 fi
1137
1138 # support installation of local files into the chroot/ISO
1139 if [ -n "$CHROOT_INSTALL" ] ; then
1140   if ! [ -d "$CHROOT_INSTALL" ] ; then
1141      log "Configuration variable \$CHROOT_INSTALL is set but not a directory; ignoring"
1142      ewarn "Configuration variable \$CHROOT_INSTALL is set but not a directory; ignoring"
1143   else
1144      log "Copying local files to chroot as requested via \$CHROOT_INSTALL"
1145      einfo "Copying local files to chroot as requested via \$CHROOT_INSTALL"
1146      rsync -avz --inplace "$CHROOT_INSTALL"/ "$CHROOT_OUTPUT/"
1147      eend $?
1148      einfo "Make sure to run squashfs stage, otherwise your local files won't be part of the ISO."
1149      FORCE_ISO_REBUILD=true
1150   fi
1151 fi
1152
1153 if [ -f "$BUILD_OUTPUT"/live/${GRML_NAME}.squashfs -a -z "$UPDATE" -a -z "$BUILD_ONLY" -a -z "$BUILD_DIRTY" ] ; then
1154    log   "Skipping stage 'squashfs' as $BUILD_OUTPUT/live exists already."
1155    ewarn "Skipping stage 'squashfs' as $BUILD_OUTPUT/live exists already." ; eend 0
1156 elif [ -n "$SKIP_MKSQUASHFS" ] ; then
1157    log   "Skipping stage 'squashfs' as requested via option -q or -N"
1158    ewarn "Skipping stage 'squashfs' as requested via option -q or -N" ; eend 0
1159 else
1160    mkdir -p "$BUILD_OUTPUT"/live/"${GRML_NAME}"/
1161    # make sure we don't leave (even an empty) base.tgz:
1162    [ -f "$CHROOT_OUTPUT/base.tgz" ] && rm -f "$CHROOT_OUTPUT/base.tgz"
1163
1164    # if unconfigured default to squashfs-tools' mksquashfs binary
1165    if [ -z "$SQUASHFS_BINARY" ] ; then
1166       SQUASHFS_BINARY='mksquashfs'
1167    fi
1168
1169    if which "$SQUASHFS_BINARY" >/dev/null 2>&1 ; then
1170       log    "Using mksquashfs binary ${SQUASHFS_BINARY}"
1171       einfo  "Using mksquashfs binary ${SQUASHFS_BINARY}" ; eend 0
1172    else
1173       log    "Error: mksquashfs binary ($SQUASHFS_BINARY) not found. Exiting."
1174       eerror "Error: mksquashfs binary ($SQUASHFS_BINARY) not found. Exiting." ; eend 1
1175       bailout
1176    fi
1177
1178    # use sane defaults if $SQUASHFS_OPTIONS isn't set
1179    if [ -z "$SQUASHFS_OPTIONS" ] ; then
1180      # use blocksize 256k as this gives best result with regards to time + compression
1181      SQUASHFS_OPTIONS="-b 256k"
1182
1183      # set lzma/xz compression by default, unless -z option has been specified on command line
1184      if [ -z "$SQUASHFS_ZLIB" ] ; then
1185         SQUASHFS_OPTIONS="$SQUASHFS_OPTIONS -comp xz"
1186      else
1187         SQUASHFS_OPTIONS="$SQUASHFS_OPTIONS -comp gzip"
1188      fi
1189    fi
1190
1191    # support exclusion of files via exclude-file:
1192    if [ -n "$SQUASHFS_EXCLUDES_FILE" -a "$SQUASHFS_EXCLUDES_FILE" ] ; then
1193       SQUASHFS_OPTIONS="$SQUASHFS_OPTIONS -ef $SQUASHFS_EXCLUDES_FILE -wildcards"
1194    fi
1195
1196    # get rid of unnecessary files when building grml-small for final release:
1197    if echo "$CLASSES" | grep -q GRML_SMALL ; then
1198       SQUASHFS_OPTIONS="$SQUASHFS_OPTIONS -e initrd.img* vmlinuz*"
1199    fi
1200
1201    # log stuff
1202    SQUASHFS_STDERR="$(mktemp -t grml-live.XXXXXX)"
1203
1204    # informational stuff
1205    [ -n "$SQUASHFS_OPTIONS" ]  && SQUASHFS_INFO_MSG="$SQUASHFS_OPTIONS"
1206    [ -n "$SQUASHFS_INFO_MSG" ] && SQUASHFS_INFO_MSG="using options: $SQUASHFS_INFO_MSG"
1207    einfo "Squashfs build information: running binary $SQUASHFS_BINARY $SQUASHFS_INFO_MSG"
1208
1209    log "$SQUASHFS_BINARY $CHROOT_OUTPUT/ $BUILD_OUTPUT/live/${GRML_NAME}/${GRML_NAME}.squashfs -noappend $SQUASHFS_OPTIONS"
1210
1211    if $SQUASHFS_BINARY $CHROOT_OUTPUT/ $BUILD_OUTPUT/live/"${GRML_NAME}"/"${GRML_NAME}".squashfs \
1212       -noappend $SQUASHFS_OPTIONS 2>"${SQUASHFS_STDERR}" ; then
1213       echo "${GRML_NAME}.squashfs" > $BUILD_OUTPUT/live/"${GRML_NAME}"/filesystem.module
1214       log "Finished execution of stage 'squashfs' [$(date)]"
1215       einfo "Finished execution of stage 'squashfs'" ; eend 0
1216    else
1217       log    "Error: there was a critical error executing stage 'squashfs' [$(date)]:"
1218       log    "$(cat $SQUASHFS_STDERR)"
1219       eerror "Error: there was a critical error executing stage 'squashfs':"
1220       cat    "${SQUASHFS_STDERR}"
1221       eend 1
1222       bailout
1223    fi
1224
1225    FORCE_ISO_REBUILD=true
1226 fi
1227
1228 # create md5sum file:
1229 if [ -z "$BOOTSTRAP_ONLY" ] ; then
1230   ( cd $BUILD_OUTPUT/GRML/"${GRML_NAME}" &&
1231   find ../.. -type f -not -name md5sums -not -name isolinux.bin -exec md5sum {} \; > md5sums )
1232 fi
1233 # }}}
1234
1235 # ISO_OUTPUT - mkisofs {{{
1236 [ -n "$ISO_OUTPUT" ] || ISO_OUTPUT="$OUTPUT/grml_isos"
1237 [ -n "$ISO_NAME" ] || ISO_NAME="${GRML_NAME}_${VERSION}.iso"
1238
1239 if [ "$BOOT_METHOD" = "isolinux" ] ; then
1240    BOOT_ARGS="-no-emul-boot -boot-load-size 4 -boot-info-table -b boot/isolinux/isolinux.bin -c boot/isolinux/boot.cat"
1241 elif [ "$BOOT_METHOD" = "grub2" ] ; then
1242    BOOT_ARGS="-no-emul-boot -boot-load-size 4 -b boot/grub/toriboot.bin"
1243 fi
1244
1245 # Work around http://bts.grml.org/grml/issue945
1246 if [[ $BOOT_METHOD != isolinux && ($HYBRID_METHOD = isohybrid || $HYBRID_METHOD = manifold) ]]; then
1247   log   "Setting HYBRID_METHOD to grub2 as hybrid mode does not work with isohybrid yet."
1248   ewarn "Setting HYBRID_METHOD to grub2 as hybrid mode does not work with isohybrid yet."
1249   HYBRID_METHOD='grub2'
1250   eend 0
1251 fi
1252
1253 if [ -f "${ISO_OUTPUT}/${ISO_NAME}" -a -z "$UPDATE" -a -z "$BUILD_ONLY" -a -z "$BUILD_DIRTY" -a "$FORCE_ISO_REBUILD" = "false" ]  ; then
1254    log   "Skipping stage 'iso build' as $ISO_OUTPUT/${ISO_NAME} exists already."
1255    ewarn "Skipping stage 'iso build' as $ISO_OUTPUT/${ISO_NAME} exists already." ; eend 0
1256 elif [ -n "$SKIP_MKISOFS" ] ; then
1257    log   "Skipping stage 'iso build' as requested via option -n or -N"
1258    ewarn "Skipping stage 'iso build' as requested via option -n or -N" ; eend 0
1259 else
1260    mkdir -p "$ISO_OUTPUT" || bailout 6 "Problem with creating $ISO_OUTPUT for stage 'iso build'"
1261
1262    if $FORCE_ISO_REBUILD && ! [ -f "${ISO_OUTPUT}/${ISO_NAME}" ] ; then
1263       log   "Forcing rebuild of ISO because files on ISO have been modified."
1264       einfo "Forcing rebuild of ISO because files on ISO have been modified."
1265    fi
1266
1267    # support xorriso as well mkisofs and genisoimage
1268    if which xorriso >/dev/null 2>&1 ; then
1269       MKISOFS='xorriso -as mkisofs'
1270     elif which mkisofs >/dev/null 2>&1; then
1271       MKISOFS='mkisofs'
1272    elif which genisoimage >/dev/null 2>&1; then
1273       MKISOFS='genisoimage'
1274    else
1275       log    "Error: neither xorriso nor mkisofs nor genisoimage available - can not create ISO."
1276       eerror "Error: neither xorriso nor mkisofs nor genisoimage available - can not create ISO." ; eend 1
1277       bailout
1278    fi
1279
1280    einfo "Using ${MKISOFS} to build ISO." ;  eend 0
1281    case "${ARCH}-${MKISOFS}" in
1282      # using -eltorito-alt-boot is limited to xorriso for now
1283      amd64-xorriso*)
1284        eindent
1285
1286        if ! dpkg --compare-versions $(dpkg-query -W -f='${Version}\n' xorriso 2>/dev/null) gt-nl 1.1.6-1 ; then
1287          log   "Disabling (U)EFI boot support because xorriso version is too old."
1288          ewarn "Disabling (U)EFI boot support because xorriso version is too old." ; eend 0
1289        else
1290          if [ -r "${BUILD_OUTPUT}"/boot/efi.img ] ; then
1291            einfo "Enabling (U)EFI boot."
1292            log   "Enabling (U)EFI boot."
1293            BOOT_ARGS="$BOOT_ARGS -boot-info-table -eltorito-alt-boot -e boot/efi.img -no-emul-boot"
1294            eend $?
1295          else
1296            log   "Disabling (U)EFI boot support because /boot/efi.img is missing."
1297            ewarn "Disabling (U)EFI boot support because /boot/efi.img is missing." ; eend 0
1298          fi
1299        fi
1300
1301        eoutdent
1302        ;;
1303    esac
1304
1305    CURRENT_DIR=$(pwd)
1306    if cd "$BUILD_OUTPUT" ; then
1307       if [ "$BOOT_METHOD" = "grub2" ]; then
1308          # make a 2048-byte bootsector for El Torito
1309          dd if=/dev/zero of=boot/grub/toriboot.bin bs=512 count=4 2>/dev/null
1310          # those are in 2048-byte sectors, so 1 16 matches 4 63 below
1311          echo 1 16 | mksh /usr/share/grml-live/scripts/bootgrub.mksh -B 11 | \
1312             dd of=boot/grub/toriboot.bin conv=notrunc 2>/dev/null
1313       fi
1314       log "$MKISOFS -V '${GRML_NAME} ${VERSION}' -publisher 'grml-live | grml.org' -l -r -J $BOOT_ARGS -o ${ISO_OUTPUT}/${ISO_NAME} ."
1315       $MKISOFS -V "${GRML_NAME} ${VERSION}" -publisher 'grml-live | grml.org' \
1316               -l -r -J $BOOT_ARGS -no-pad \
1317               -o "${ISO_OUTPUT}/${ISO_NAME}" . ; RC=$?
1318       # both of these need core.img there, so it’s easier to write it here
1319       if [ "$BOOT_METHOD" = "grub2" ] || [ "$HYBRID_METHOD" = "grub2" ]; then
1320          # must be <= 30720 bytes
1321          dd if=boot/grub/core.img of="${ISO_OUTPUT}/${ISO_NAME}" \
1322            conv=notrunc bs=512 seek=4 2>/dev/null
1323       fi
1324
1325       # pad the output ISO to multiples of 256 KiB for partition table support
1326       siz=$($getfilesize "${ISO_OUTPUT}/${ISO_NAME}")
1327       cyls=$((siz / 512 / 32 / 16 + 1))   # C=$cyls H=16 S=32
1328       siz=$((cyls * 16 * 32 * 512))   # size after padding
1329       dd if=/dev/zero bs=1 count=1 seek=$((siz - 1)) \
1330          of="${ISO_OUTPUT}/${ISO_NAME}" 2>/dev/null
1331
1332       # support disabling hybrid ISO image
1333       if [ "$HYBRID_METHOD" = "disable" ] ; then
1334         log   "Skipping creation of hybrid ISO file as requested via HYBRID_METHOD=disable"
1335         einfo "Skipping creation of hybrid ISO file as requested via HYBRID_METHOD=disable"
1336         eend 0
1337       elif [ "$HYBRID_METHOD" = "manifold" ] || [ "$HYBRID_METHOD" = "grub2" ] ; then
1338         # isoinfo is part of both mkisofs and genisoimage so we're good
1339         bootoff=$(isoinfo -l -i "${ISO_OUTPUT}/${ISO_NAME}" | \
1340           sed -n '/^.*\[ *\([0-9]*\)[] ].* ISOLINUX.BIN[;1]* *$/s//\1/p')
1341
1342         if ! [ -r boot/grub/core.img ] ; then
1343           log   "boot/grub/core.img not found, not creating manifold boot ISO file"
1344           ewarn "boot/grub/core.img not found, not creating manifold boot ISO file"
1345         elif [ "${bootoff:-0}" -lt 1 ] ; then
1346           log   "isolinux.bin not found on the ISO file, disabling manifold boot"
1347           ewarn "isolinux.bin not found on the ISO file, disabling manifold boot"
1348         else
1349           if [ "$HYBRID_METHOD" = "grub2" ] ; then
1350             log   "Creating hybrid ISO file with manifold/grub2 method"
1351             einfo "Creating hybrid ISO file with manifold/grub2 method"
1352             # 512 bytes: MBR, partition table, load GRUB 2
1353             echo 4 63 | mksh /usr/share/grml-live/scripts/bootgrub.mksh -A -M 4:0x96 -g $cyls:16:32
1354           else
1355             log   "Creating hybrid ISO file with manifold method"
1356             einfo "Creating hybrid ISO file with manifold method"
1357             # read only one but 2048-byte sized (scale: << 2) sector
1358             echo $bootoff $bootoff | \
1359               mksh /usr/share/grml-live/scripts/bootilnx.mksh -A -M 4:0x96 -g $cyls:16:32 -S 2
1360           fi | dd of="${ISO_OUTPUT}/${ISO_NAME}" conv=notrunc 2>/dev/null
1361           eend $?
1362         fi
1363       elif [ "$HYBRID_METHOD" = "isohybrid" ] ; then
1364         if ! which isohybrid >/dev/null 2>&1 ; then
1365           bailout 12 "isohybrid binary not found - please install syslinux/syslinux-common"
1366         else
1367           log   "Creating hybrid ISO file with isohybrid method"
1368           einfo "Creating hybrid ISO file with isohybrid method"
1369           # Notes for consideration:
1370           # "-entry 4 -type 1c"
1371           # * using 4 as the partition number is supposed to help with BIOSes
1372           #   that only support USB-Zip boot
1373           # * using 1c (i.e. hidden FAT32 LBA), instead of the default 0x17
1374           #   (hidden NTFS, IIRC), as the partition type is sometimes needed
1375           #   to get the BIOS even look at the partition created by isohybrid
1376           if isohybrid --help | grep -q -- --uefi ; then
1377             if echo $CLASSES | grep -qw I386 ; then
1378               log   "Detected uefi support for isohybrid but 32bit systems do not support it, ignoring."
1379               einfo "Detected uefi support for isohybrid but 32bit systems do not support it, ignoring."
1380             else
1381               log   "Detected uefi support for isohybrid, enabling"
1382               einfo "Detected uefi support for isohybrid, enabling"
1383               ISOHYBRID_OPTIONS=--uefi
1384             fi
1385           fi
1386
1387           log "isohybrid $ISOHYBRID_OPTIONS ${ISO_OUTPUT}/${ISO_NAME}"
1388           isohybrid $ISOHYBRID_OPTIONS "${ISO_OUTPUT}/${ISO_NAME}"
1389           eend $?
1390         fi
1391       else
1392         bailout 12 "Unknown HYBRID_METHOD [${HYBRID_METHOD}]. Supported values: disable, isohybrid, grub2, manifold"
1393       fi
1394
1395       # generate md5sum and sha1sum of ISO if we are using class 'RELEASE':
1396       case $CLASSES in *RELEASE*)
1397          [ "$RC" = 0 ] && \
1398          (
1399            if cd $ISO_OUTPUT ; then
1400              md5sum ${ISO_NAME} > ${ISO_NAME}.md5 && \
1401              touch -r ${ISO_NAME} ${ISO_NAME}.md5
1402              sha1sum ${ISO_NAME} > ${ISO_NAME}.sha1 && \
1403              touch -r ${ISO_NAME} ${ISO_NAME}.sha1
1404            fi
1405          )
1406          ;;
1407       esac
1408
1409       cd "$CURRENT_DIR"
1410    fi
1411
1412    if [ "$RC" = 0 ] ; then
1413       log   "Finished execution of stage 'iso build' [$(date)]"
1414       einfo "Finished execution of stage 'iso build'" ; eend 0
1415    else
1416       log    "Error: there was a critical error ($RC) executing stage 'iso build' [$(date)]"
1417       eerror "Error: there was a critical error executing stage 'iso build'" ; eend 1
1418       bailout $RC
1419    fi
1420 fi
1421 # }}}
1422
1423 # netboot package {{{
1424 create_netbootpackage() {
1425   local OUTPUT_FILE="${NETBOOT}/grml_netboot_package_${GRML_NAME}_${VERSION}.tar.bz2"
1426
1427   if [ -f "${OUTPUT_FILE}" -a -z "$UPDATE" -a -z "$BUILD_ONLY" -a -z "$BUILD_DIRTY" ] ; then
1428     log   "Skipping stage 'netboot' as $OUTPUT_FILE exists already."
1429     ewarn "Skipping stage 'netboot' as $OUTPUT_FILE exists already." ; eend 0
1430     return 0
1431   elif [ -n "$SKIP_NETBOOT" ] ; then
1432     log   "Skipping stage 'netboot' as requested via option -Q"
1433     ewarn "Skipping stage 'netboot' as requested via option -Q" ; eend 0
1434     return 0
1435   fi
1436
1437   mkdir -p "$NETBOOT"
1438
1439   if ! [ -r "${CHROOT_OUTPUT}/usr/lib/syslinux/pxelinux.0" ] ; then
1440     ewarn "File /usr/lib/syslinux/pxelinux.0 not found in build chroot." ; eend 0
1441     eindent
1442     einfo "Install syslinux[-common] package in chroot to get a netboot package."
1443     eoutdent
1444     return 0
1445   fi
1446
1447   local OUTPUTDIR="${NETBOOT}/build_tmp"
1448   local WORKING_DIR="${OUTPUTDIR}/grml_netboot_package_${GRML_NAME}_${VERSION}/tftpboot/"
1449
1450   mkdir -p "$WORKING_DIR"
1451
1452   cp "${CHROOT_OUTPUT}"/boot/vmlinuz-*    "$WORKING_DIR"/vmlinuz
1453   cp "${CHROOT_OUTPUT}"/boot/initrd.img-* "$WORKING_DIR"/initrd.img
1454   cp "${CHROOT_OUTPUT}"/usr/lib/syslinux/pxelinux.0 "${WORKING_DIR}/pxelinux.0"
1455
1456   mkdir -p "${WORKING_DIR}/pxelinux.cfg"
1457   if [ -r "${BUILD_OUTPUT}/boot/isolinux/netboot.cfg" ] ; then
1458     cp "${BUILD_OUTPUT}/boot/isolinux/netboot.cfg" "${WORKING_DIR}/pxelinux.cfg/default"
1459   else
1460     log   "File ${BUILD_OUTPUT}/boot/isolinux/netboot.cfg not found."
1461     ewarn "File ${BUILD_OUTPUT}/boot/isolinux/netboot.cfg not found."
1462     eindent
1463     log   "Hint: Are you using custom templates which do not provide netboot.cfg?"
1464     ewarn "Hint: Are you using custom templates which do not provide netboot.cfg?" ; eend 0
1465     eoutdent
1466   fi
1467
1468   if tar -C "$OUTPUTDIR" -jcf "${OUTPUT_FILE}" "grml_netboot_package_${GRML_NAME}_${VERSION}" ; then
1469     (
1470       cd $(dirname "${OUTPUT_FILE}")
1471       sha1sum $(basename "${OUTPUT_FILE}") > "${OUTPUT_FILE}.sha1"
1472     )
1473     einfo "Generated netboot package ${OUTPUT_FILE}" ; eend 0
1474     rm -rf "${OUTPUTDIR}"
1475   else
1476     rm -rf "${OUTPUTDIR}"
1477     eerror "Could not generate netboot package ${OUTPUT_FILE}" ; eend 1
1478     bailout 21
1479   fi
1480 }
1481
1482 create_netbootpackage
1483 # }}}
1484
1485 # log build information to database if grml-live-db is installed and enabled {{{
1486 dpkg_to_db() {
1487 if [ -d /usr/share/grml-live-db ] ; then
1488
1489   # safe defaults
1490   DPKG_LIST="/var/log/fai/$HOSTNAME/last/dpkg.list" # the dpkg --list output of the chroot:
1491   [ -n "$DPKG_DATABASE" ]  || DPKG_DATABASE=/var/log/grml-live.db
1492   [ -n "$DPKG_DBSCRIPT" ]  || DPKG_DBSCRIPT=/usr/share/grml-live-db/scripts/dpkg-to-db
1493   [ -n "$DPKG_DBOPTIONS" ] || DPKG_DBOPTIONS="--database $DPKG_DATABASE --logfile $LOGFILE --flavour $GRML_NAME --dpkg $DPKG_LIST"
1494
1495   if ! [ -x "$DPKG_DBSCRIPT" ] ; then
1496     log "Error: $DPKG_DBSCRIPT is not executable, can not log dpkg information."
1497     eerror "Error: $DPKG_DBSCRIPT is not executable, can not log dpkg information." ; eend 1
1498     bailout 14
1499   fi
1500
1501   # disable by default for now, not sure whether really everyone is using a local db file
1502   #if ! touch "$DPKG_DATABASE" ; then
1503   #  eerror "Error: can not write to ${DPKG_DATABASE}, can not log dpkg information." ; eend 1
1504   #  bailout 14
1505   #fi
1506
1507   if ! [ -r "$DPKG_LIST" ] ; then
1508      log   "Warning: can not read $DPKG_LIST - can not provide information to $DPKG_DBSCRIPT (dirty build?)"
1509      ewarn "Warning: can not read $DPKG_LIST - can not provide information to $DPKG_DBSCRIPT (dirty build?)" ; eend 0
1510   else
1511      einfo "Logging $DPKG_LIST to database $DPKG_DATABASE"
1512      log "Logging $DPKG_LIST to database $DPKG_DATABASE"
1513      log "Executing $DPKG_DBSCRIPT $DPKG_DBOPTIONS"
1514      eindent
1515
1516      if DB_INFO=$("$DPKG_DBSCRIPT" $DPKG_DBOPTIONS 2>&1) ; then
1517        einfo "$DB_INFO"
1518        eend 0
1519      else
1520        eerror "$DB_INFO"
1521        eend 1
1522      fi
1523
1524      eoutdent
1525   fi
1526
1527 fi
1528 }
1529 # }}}
1530
1531 # finalize {{{
1532 [ -n "$start_seconds" ] && SECONDS="$[$(cut -d . -f 1 /proc/uptime)-$start_seconds]" || SECONDS="unknown"
1533 log "Successfully finished execution of $PN [$(date) - running ${SECONDS} seconds]"
1534
1535 dpkg_to_db # make sure we catch the last log line as well, therefore execute between log + einfo
1536
1537 einfo "Successfully finished execution of $PN [$(date) - running ${SECONDS} seconds]" ; eend 0
1538 bailout 0
1539 # }}}
1540
1541 ## END OF FILE #################################################################
1542 # vim:foldmethod=marker ts=2 ft=sh ai expandtab tw=80 sw=2