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