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