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