Remove /var/log/install_packages.list on release. File is emtpy then anyway.
[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 # global variables
27 GRML_LIVE_VERSION='0.14.1'
28 PN="$(basename $0)"
29 CMDLINE="$0 $@"
30 SOURCES_LIST_FILE='/etc/grml/fai/apt/sources.list'
31 ADDONS_LIST_FILE='/boot/isolinux/addons_list.cfg'
32 # }}}
33
34 # usage information {{{
35 usage()
36 {
37   echo "
38 $PN - build process script for generating a (grml based) Linux Live-ISO
39
40 Usage: $PN [options, see as follows]
41
42    -a <architecture>       architecture; available values: i386 and amd64
43    -b                      build the ISO without updating the chroot via FAI
44    -B                      build the ISO without touching the chroot (skips cleanup)
45    -c <classe[s]>          classes to be used for building the ISO via FAI
46    -C <configfile>         configuration file for grml-live
47    -d <date>               use specified date instead of build time as date of release
48    -F                      force execution without prompting
49    -g <grml_name>          set the grml flavour name
50    -h                      display short usage information and exit
51    -i <iso_name>           name of ISO
52    -I <src_directory>      directory which provides files that should become
53                            part of the chroot/ISO
54    -n                      skip generation of ISO
55    -o <output_directory>   main output directory of the build process
56    -q                      skip mksquashfs
57    -r <release_name>       release name
58    -s <suite>              Debian suite; values: etch, lenny, squeeze, sid
59    -t <template_directory> place of the templates
60    -u                      update existing chroot instead of rebuilding it from scratch
61    -v <version_number>     specify version number of the release
62    -V                      increase verbosity in the build process
63    -z                      use ZLIB instead of LZMA/XZ compression
64
65 Usage examples:
66
67     $PN
68     $PN -c GRMLBASE,GRML_MEDIUM,I386 -o /dev/shm/grml
69     $PN -c GRMLBASE,GRML_SMALL,REMOVE_DOCS,I386 -g grml-small -v 1.0
70     $PN -c GRMLBASE,GRML_FULL,I386 -i grml_0.0-1.iso -v 0.0-1
71     $PN -c GRMLBASE,GRML_FULL,I386 -s sid -V -r 'grml-live rocks'
72
73 More details: man grml-live + /usr/share/doc/grml-live/grml-live.html
74               http://grml.org/grml-live/
75
76 Please send your bug reports and feedback to the grml-team: http://grml.org/bugs/
77 "
78 }
79
80 # make sure it's possible to get usage information without being
81 # root or actually executing the script
82 if [ "$1" = '-h' -o "$1" = '--help' ] ; then
83    usage
84    [ "$(id -u 2>/dev/null)" != 0 ] && echo "Please notice that this script requires root permissions."
85    exit 0
86 fi
87 # }}}
88
89 # some runtime checks {{{
90 # we need root permissions for the build-process:
91 if [ "$(id -u 2>/dev/null)" != 0 ] ; then
92    echo "Error: please run this script with uid 0 (root)." >&2
93    exit 1
94 fi
95
96 if [ -r /var/run/fai/FAI_INSTALLATION_IN_PROGRESS ] ; then
97    echo "/usr/sbin/fai already running or was aborted before.">&2
98    echo "You may remove /var/run/fai/FAI_INSTALLATION_IN_PROGRESS and try again.">&2
99    exit 1
100 fi
101
102 # see #449236
103 if [ -r /var/run/fai/fai_softupdate_is_running ] ; then
104    echo "/usr/sbin/fai softupdate already running or was aborted before.">&2
105    echo "You may remove /var/run/fai/fai_softupdate_is_running and try again.">&2
106    exit 1
107 fi
108 # }}}
109
110 # lsb-functions and configuration stuff {{{
111 # make sure they are not set by default
112 VERBOSE=''
113 FORCE=''
114 UPDATE=''
115 BUILD_ONLY=''
116 BUILD_DIRTY=''
117 HOSTNAME=''
118
119 if [ -r /etc/grml/lsb-functions ] ; then
120    . /etc/grml/lsb-functions
121 else
122    einfo()  { echo "  [*] $*" ;}
123    eerror() { echo "  [!] $*">&2 ;}
124    ewarn()  { echo "  [x] $*" ;}
125    eend()   { return 0 ;}
126    eindent()  { return 0 ;}
127    eoutdent() { return 0 ;}
128 fi
129
130 # source main configuration file:
131 LIVE_CONF=/etc/grml/grml-live.conf
132 . $LIVE_CONF
133 # }}}
134
135 # umount all directories {{{
136 umount_all() {
137    # make sure we don't leave any mounts - FAI doesn't remove them always
138    umount $CHROOT_OUTPUT/proc 2>/dev/null || /bin/true
139    umount $CHROOT_OUTPUT/sys  2>/dev/null || /bin/true
140    umount $CHROOT_OUTPUT/dev/pts 2>/dev/null || /bin/true
141    umount $CHROOT_OUTPUT/dev 2>/dev/null || /bin/true
142    [ -n "$MIRROR_DIRECTORY" ] && umount "${CHROOT_OUTPUT}/${MIRROR_DIRECTORY}"
143 }
144 # }}}
145
146 # clean exit {{{
147 bailout() {
148   rm -f /var/run/fai/fai_softupdate_is_running \
149         /var/run/fai/FAI_INSTALLATION_IN_PROGRESS
150   [ -n "$SQUASHFS_STDERR" ]  && rm -rf "$SQUASHFS_STDERR"
151   umount_all
152   [ -n "$1" ] && EXIT="$1" || EXIT="1"
153   [ -n "$2" ] && eerror "$2">&2
154   log "------------------------------------------------------------------------------"
155   exit "$EXIT"
156 }
157 trap bailout 1 2 3 3 6 9 14 15 EXIT
158 # }}}
159
160 # log file stuff {{{
161 [ -n "$LOGFILE" ] || LOGFILE=/var/log/grml-live.log
162 touch $LOGFILE
163 chown root:adm $LOGFILE
164 chmod 664 $LOGFILE
165 # }}}
166
167 # some important functions {{{
168
169 # log output:
170 # usage: log "string to log"
171 log() { echo "$*" >> $LOGFILE ; }
172
173 # cut string at character number int = $1
174 # usage: cut_string 5 "1234567890" will output "12345"
175 cut_string() {
176   [ -n "$2" ] || return 1
177   echo "$2" | head -c "$1"; echo -ne "\n"
178 }
179
180 # prepend int = $1 spaces before string = $2
181 # usage: extend_string_begin 5 "123" will output "  123"
182 extend_string_begin() {
183   [ -n "$2" ] || return 1
184   local COUNT="$(echo $2 | wc -c)"
185   local FILL="$(expr $COUNT - $1)"
186   while [ "$FILL" -gt 1 ] ; do
187     echo -n " "
188     local FILL=$(expr $FILL - 1)
189   done
190   while [ "$FILL" -lt 1 ] ; do
191     echo -n " "
192     local FILL=$(expr $FILL + 1)
193   done
194   echo "$2" | head -c "$1"; echo -ne "\n"
195 }
196
197 # append int = $1 spaces to string = $2
198 # usage: extend_string_begin 5 "123" will output "123  "
199 extend_string_end() {
200   [ -n "$2" ] || return 1
201   echo -n "$2" | head -c "$1"
202   local COUNT="$(echo $2 | wc -c)"
203   local FILL="$(expr $COUNT - $1)"
204   while [ "$FILL" -gt 1 ] ; do
205     echo -n " "
206     local FILL=$(expr $FILL - 1)
207   done
208   while [ "$FILL" -lt 1 ] ; do
209     echo -n " "
210     local FILL=$(expr $FILL + 1)
211   done
212   echo -ne "\n"
213 }
214 # }}}
215
216 # read local (non-packaged) configuration {{{
217 LOCAL_CONFIG=/etc/grml/grml-live.local
218 if [ -r "$LOCAL_CONFIG" ] ; then
219    log "Sourcing $LOCAL_CONFIG"
220    . $LOCAL_CONFIG
221 else
222    log "No $LOCAL_CONFIG found, not sourcing it"
223    LOCAL_CONFIG=''
224 fi
225 # }}}
226
227 # command line parsing {{{
228 while getopts "a:C:c:d:g:i:I:o:r:s:t:v:bBFnquVz" opt; do
229   case "$opt" in
230     a) ARCH="$OPTARG" ;;
231     b) BUILD_ONLY=1 ;;
232     B) BUILD_DIRTY=1 ;;
233     c) CLASSES="$OPTARG" ;;
234     C) CONFIG="$OPTARG" ;;
235     d) DATE="$OPTARG" ;;
236     g) GRML_NAME="$OPTARG" ;;
237     i) ISO_NAME="$OPTARG" ;;
238     I) CHROOT_INSTALL="$OPTARG" ;;
239     n) SKIP_MKISOFS=1 ;;
240     o) OUTPUT="$OPTARG" ;;
241     q) SKIP_MKSQUASHFS=1 ;;
242     r) RELEASENAME="$OPTARG" ;;
243     s) SUITE="$OPTARG" ;;
244     t) TEMPLATE_DIRECTORY="$OPTARG";;
245     v) VERSION="$OPTARG" ;;
246     F) FORCE=1 ;;
247     u) UPDATE=1 ;;
248     V) VERBOSE="-v" ;;
249     z) SQUASHFS_ZLIB=1 ;;
250     ?) echo "invalid option -$OPTARG" >&2; bailout 1 ;;
251   esac
252 done
253 shift $(($OPTIND - 1))  # set ARGV to the first not parsed commandline parameter
254 # }}}
255
256 # assume sane defaults (if not set already) {{{
257 [ -n "$ARCH" ]             || ARCH="$(dpkg --print-architecture)"
258 [ -n "$BOOT_METHOD" ]      || BOOT_METHOD='isolinux'
259 [ -n "$CLASSES" ]          || CLASSES="GRMLBASE,GRML_MEDIUM,I386"
260 [ -n "$DATE" ]             || DATE="$(date +%Y-%m-%d)"
261 [ -n "$DISTRI_INFO" ]      || DISTRI_INFO='Grml - Live Linux for system administrators   '
262 [ -n "$DISTRI_NAME" ]      || DISTRI_NAME="grml"
263 [ -n "$DISTRI_SPLASH" ]    || DISTRI_SPLASH='grml.png'
264 [ -n "$FORCE_ISO_REBUILD" ] || FORCE_ISO_REBUILD="false"
265 [ -n "$GRML_FAI_CONFIG" ]  || GRML_FAI_CONFIG='/etc/grml/fai'
266 [ -n "$GRML_NAME" ]        || GRML_NAME='grml'
267 [ -n "$HOSTNAME" ]         || HOSTNAME='grml'
268 [ -n "$HYBRID_METHOD" ]    || HYBRID_METHOD='manifold'
269 [ -n "$NFSROOT_CONF" ]     || NFSROOT_CONF='/etc/grml/fai/make-fai-nfsroot.conf'
270 [ -n "$RELEASENAME" ]      || RELEASENAME='grml-live rocks'
271 [ -n "$SQUASHFS_EXCLUDES_FILE " ] || SQUASHFS_EXCLUDES_FILE='/etc/grml/fai/squashfs-excludes'
272 [ -n "$SUITE" ]            || SUITE='squeeze'
273 [ -n "$TEMPLATE_DIRECTORY" ] || TEMPLATE_DIRECTORY='/usr/share/grml-live/templates'
274 [ -n "$USERNAME" ]         || USERNAME='grml'
275 [ -n "$VERSION" ]          || VERSION='0.0.1'
276 [ -n "$WINDOWS_BINARIES" ] || WINDOWS_BINARIES='http://the.earth.li/~sgtatham/putty/latest/x86/'
277
278 # output specific stuff, depends on $OUTPUT (iff not set):
279 [ -n "$OUTPUT" ]           || OUTPUT='/grml/grml-live'
280 [ -n "$BUILD_OUTPUT" ]     || BUILD_OUTPUT="$OUTPUT/grml_cd"
281 [ -n "$CHROOT_OUTPUT" ]    || CHROOT_OUTPUT="$OUTPUT/grml_chroot"
282 [ -n "$ISO_OUTPUT" ]       || ISO_OUTPUT="$OUTPUT/grml_isos"
283 # }}}
284
285 # some misc checks before executing FAI {{{
286 [ -n "$CLASSES" ] || bailout 1 "Error: \$CLASSES unset, please set it in $LIVE_CONF or
287 specify it on the command line using the -c option."
288 [ -n "$OUTPUT" ] || bailout 1 "Error: \$OUTPUT unset, please set it in $LIVE_CONF or
289 specify it on the command line using the -o option."
290
291 # trim characters that are known to cause problems inside $GRML_NAME;
292 # for example isolinux does not like '-' inside the directory name
293 [ -n "$GRML_NAME" ] && export SHORT_NAME="$(echo $GRML_NAME | tr -d ',./;\- ')"
294
295 # export variables to have them available in fai scripts:
296 [ -n "$GRML_NAME" ]   && export GRML_NAME="$GRML_NAME"
297 [ -n "$RELEASENAME" ] && export RELEASENAME="$RELEASENAME"
298 # }}}
299
300 # ZERO_LOGFILE - check for backwards compatibility reasons {{{
301 # this was default behaviour until grml-live 0.9.34:
302 if [ -n "$ZERO_LOGFILE" ] ; then
303    PRESERVE_LOGFILE='' # make sure it's cleaned then
304    ewarn "Please consider disabling the \$ZERO_LOGFILE option as grml-live clears..."
305    ewarn "... the logfile $LOGFILE by default (unless \$PRESERVE_LOGFILE is set) nowadays."
306    eend 0
307 fi
308 # }}}
309
310 # ask user whether the setup is ok {{{
311 if [ -z "$FORCE" ] ; then
312    echo
313    echo "${PN} [${GRML_LIVE_VERSION}]: check your configuration (or use -F to force execution):"
314    echo
315    echo "  FAI classes:       $CLASSES"
316    [ -r "$LOCAL_CONFIG" ]        && echo "  Local config:      /etc/grml/grml-live.local"
317    [ -n "$CONFIG" ]              && echo "  Configuration:     $CONFIG"
318    echo "  main directory:    $OUTPUT"
319    [ -n "$CHROOT_OUTPUT" ]       && echo "  Chroot target:     $CHROOT_OUTPUT"
320    [ -n "$BUILD_OUTPUT" ]        && echo "  Build target:      $BUILD_OUTPUT"
321    [ -n "$ISO_OUTPUT" ]          && echo "  ISO target:        $ISO_OUTPUT"
322    [ -n "$GRML_NAME" ]           && echo "  Grml name:         $GRML_NAME"
323    [ -n "$RELEASENAME" ]         && echo "  Release name:      $RELEASENAME"
324    [ -n "$DATE" ]                && echo "  Build date:        $DATE"
325    [ -n "$VERSION" ]             && echo "  Grml version:      $VERSION"
326    [ -n "$SUITE" ]               && echo "  Debian suite:      $SUITE"
327    [ -n "$ARCH" ]                && echo "  Architecture:      $ARCH"
328    [ -n "$BOOT_METHOD" ]         && echo "  Boot method:       $BOOT_METHOD"
329    [ -n "$HYBRID_METHOD" ]       && echo "  Hybrid method:     $HYBRID_METHOD"
330    [ -n "$TEMPLATE_DIRECTORY" ]  && echo "  Template files:    $TEMPLATE_DIRECTORY"
331    [ -n "$CHROOT_INSTALL" ]      && echo "  Install files from directory to chroot:  $CHROOT_INSTALL"
332    [ -n "$BOOTID" ]              && echo "  Boot identifier:   $BOOTID"
333    [ -n "$NO_BOOTID" ]           && echo "  Skipping bootid feature."
334    [ -n "$DEFAULT_BOOTOPTIONS" ] && echo "  Adding default bootoptions: \"$DEFAULT_BOOTOPTIONS\""
335    [ -n "$FAI_ARGS" ]            && echo "  Additional arguments for FAI: $FAI_ARGS"
336    [ -n "$LOGFILE" ]             && echo "  Logging to file:   $LOGFILE"
337    [ -n "$SQUASHFS_ZLIB" ]       && echo "  Using ZLIB (instead of LZMA/XZ) compression."
338    [ -n "$SQUASHFS_OPTIONS" ]    && echo "  Using SQUASHFS_OPTIONS ${SQUASHFS_OPTIONS}"
339    [ -n "$VERBOSE" ]             && echo "  Using VERBOSE mode."
340    [ -n "$UPDATE" ]              && echo "  Executing UPDATE instead of fresh installation."
341    [ -n "$SKIP_MKSQUASHFS" ]     && echo "  Skipping creation of SQUASHFS file."
342    [ -n "$SKIP_MKISOFS" ]        && echo "  Skipping creation of ISO file."
343    [ -n "$BUILD_ONLY" ]          && echo "  Executing BUILD_ONLY instead of fresh installation or UPDATE."
344    [ -n "$BUILD_DIRTY" ]         && echo "  Executing BUILD_DIRTY to leave chroot untouched."
345    echo
346    echo -n "Is this ok for you? [y/N] "
347    read a
348    if ! [ "$a" = 'y' -o "$a" = 'Y' ] ; then
349       bailout 1 "Exiting as requested."
350    fi
351    echo
352 fi
353 # }}}
354
355 # clean/zero/remove logfiles {{{
356
357 if [ -n "$PRESERVE_LOGFILE" ] ; then
358    echo "Preserving logfile $LOGFILE as requested via \$PRESERVE_LOGFILE"
359 else
360    # make sure it is empty (as it is e.g. appended to grml-live-db)
361    echo -n > $LOGFILE
362 fi
363
364 if [ -n "$ZERO_FAI_LOGFILE" ] ; then
365    if [ -d /var/log/fai/"$HOSTNAME" ] ; then
366       rm -rf /var/log/fai/"$HOSTNAME"/"$(readlink /var/log/fai/"$HOSTNAME"/last)"
367       rm -rf /var/log/fai/"$HOSTNAME"/"$(readlink /var/log/fai/"$HOSTNAME"/last-dirinstall)"
368       rm -rf /var/log/fai/"$HOSTNAME"/"$(readlink /var/log/fai/"$HOSTNAME"/last-softupdate)"
369       rm -f /var/log/fai/"$HOSTNAME"/last \
370             /var/log/fai/"$HOSTNAME"/last-dirinstall \
371             /var/log/fai/"$HOSTNAME"/last-softupdate
372    fi
373 fi
374 # }}}
375
376 # source config and startup {{{
377 if [ -n "$CONFIG" ] ; then
378    if ! [ -f "$CONFIG" ] ; then
379       log    "Error: $CONFIG could not be read. Exiting. [$(date)]"
380       eerror "Error: $CONFIG could not be read. Exiting." ; eend 1
381       bailout 1
382    else
383       log "Sourcing $CONFIG"
384       . $CONFIG
385    fi
386 fi
387
388 start_seconds=$(cut -d . -f 1 /proc/uptime)
389 log "------------------------------------------------------------------------------"
390 log "Starting grml-live [${GRML_LIVE_VERSION}] run on $(date)"
391 log "Executed grml-live command line:"
392 log "$CMDLINE"
393
394 einfo "Logging actions to logfile $LOGFILE"
395 # }}}
396
397 # on-the-fly configuration {{{
398 if [ -n "$MIRROR_DIRECTORY" ] ; then
399    if ! [ -d "$MIRROR_DIRECTORY/debian" ] ; then
400       log    "Error: $MIRROR_DIRECTORY/debian does not seem to exist. Exiting. [$(date)]"
401       eerror "Error: $MIRROR_DIRECTORY/debian does not seem to exist. Exiting." ; eend 1
402       bailout 1
403    fi
404    cat > "$SOURCES_LIST_FILE" << EOF
405 # NOTE: This file is *NOT* meant for manual customisation! This file is
406 # modified by grml-live and any changes might be overridden.
407 # You might consider using GRML_LIVE_SOURCES in /etc/grml/grml-live.conf*
408 # or FAI's fcopy command with /etc/grml/fai/config/files instead!
409 EOF
410    echo "$MIRROR_SOURCES" >> "$SOURCES_LIST_FILE"
411    if [ -n "$GRML_LIVE_SOURCES" ] ; then
412       echo "$GRML_LIVE_SOURCES" >> "$SOURCES_LIST_FILE"
413    fi
414 elif [ -n "$GRML_LIVE_SOURCES" ] ; then
415    cat > "$SOURCES_LIST_FILE" << EOF
416 # NOTE: This file is *NOT* meant for manual customisation! This file is
417 # modified by grml-live and any changes might be overridden.
418 # You might consider using GRML_LIVE_SOURCES in /etc/grml/grml-live.conf*
419 # or FAI's fcopy command with /etc/grml/fai/config/files instead!
420 EOF
421    echo "$GRML_LIVE_SOURCES" >> "$SOURCES_LIST_FILE"
422 fi
423
424 if [ -n "$FAI_DEBOOTSTRAP" ] ; then
425    sed "s#^FAI_DEBOOTSTRAP=.*#FAI_DEBOOTSTRAP=\"$FAI_DEBOOTSTRAP\"#" "$NFSROOT_CONF" | sponge "$NFSROOT_CONF"
426 fi
427
428 # does this suck? YES!
429 # /usr/share/debootstrap/scripts/unstable does not exist, instead use 'sid':
430 case $SUITE in
431    unstable) SUITE='sid' ;;
432    # make sure that we *NEVER* write any broken suite name to sources.list,
433    # otherwise we won't be able to adjust it one next (correct) execution
434    stable)   ;;
435    testing)  ;;
436    etch)     ;;
437    lenny)    ;;
438    squeeze)  ;;
439    wheezy)   ;;
440    sid)      ;;
441    *) echo "Sorry, $SUITE is not a valid Debian suite, exiting.">&2; bailout 1 ;;
442 esac
443
444 DIST=" etch\| stable\| lenny\| squeeze\| wheezy\| testing\| sid\| unstable"
445 sed "s/\(^deb .\+\)\([ \t]*\)\($DIST\)\([ \t]*\)\(main \)/\1 \2$SUITE\4\5/" "$SOURCES_LIST_FILE" | sponge "$SOURCES_LIST_FILE"
446 for file in "$LIVE_CONF" "$CONFIG" "$LOCAL_CONFIG" ; do
447     if [ -n "$file" ] ; then
448        sed "s/^SUITE=.*/SUITE=\"$SUITE\"/" $file | sponge $file
449        sed "s/\(^deb .\+\)\([ \t]*\)\($DIST\)\([ \t]*\)\(main \)/\1 \2$SUITE\4\5/" "$file" | sponge "$file"
450     fi
451 done
452
453 # notice: activate grml-live pool only if we are building against unstable:
454 if grep -qwe unstable -qwe sid "$SOURCES_LIST_FILE" ; then
455    grep -q 'grml-live.*main' "$SOURCES_LIST_FILE" || \
456    grep grml-stable "$SOURCES_LIST_FILE" | \
457         sed 's/grml-stable/grml-live/' >> "$SOURCES_LIST_FILE"
458 else
459    grep -q 'grml-live.*main' "$SOURCES_LIST_FILE" && \
460    sed 's/.*grml-live.*/# removed grml-live repository/' "$SOURCES_LIST_FILE" | sponge "$SOURCES_LIST_FILE"
461 fi
462
463 for file in "$LIVE_CONF" "$CONFIG" "$LOCAL_CONFIG" "$NFSROOT_CONF" ; do
464     if [ -n "$file" ] ; then
465        sed "s|^FAI_DEBOOTSTRAP=\"[a-z]* |FAI_DEBOOTSTRAP=\"$SUITE |" "$file" | sponge "$file"
466     fi
467 done
468
469 # validate whether the specified architecture class matches the
470 # architecture (option), otherwise installation of kernel will fail
471 if echo $CLASSES | grep -qi i386 ; then
472    if ! [[ "$ARCH" == "i386" ]] ; then
473       log    "Error: You specified the I386 class but are trying to build something else (AMD64?)."
474       eerror "Error: You specified the I386 class but are trying to build something else (AMD64?)."
475       eerror "Tip:   Either invoke grml-live with '-a i386' or adjust the architecture class. Exiting."
476       eend 1
477       bailout
478    fi
479 elif echo $CLASSES | grep -qi amd64 ; then
480    if ! [[ "$ARCH" == "amd64" ]] ; then
481       log    "Error: You specified the AMD64 class but are trying to build something else (I386?)."
482       eerror "Error: You specified the AMD64 class but are trying to build something else (I386?)."
483       eerror "Tip:   Either invoke grml-live with '-a amd64' or adjust the architecture class. Exiting."
484       eend 1
485       bailout
486    fi
487 fi
488
489 if grep -q -- 'FAI_DEBOOTSTRAP_OPTS.*--arch' "$NFSROOT_CONF" ; then
490    sed "s/--arch [a-z0-9]* /--arch $ARCH /" "$NFSROOT_CONF" | sponge "$NFSROOT_CONF"
491 else
492    sed "s|^FAI_DEBOOTSTRAP_OPTS=\"\(.*\)|FAI_DEBOOTSTRAP_OPTS=\"--arch $ARCH \1|" "$NFSROOT_CONF" | sponge "$NFSROOT_CONF"
493 fi
494 # }}}
495
496 # CHROOT_OUTPUT - execute FAI {{{
497 if [ -n "$BUILD_DIRTY" ]; then
498    log   "Skipping stage 'fai' as requested via option -B"
499    ewarn "Skipping stage 'fai' as requested via option -B" ; eend 0
500 else
501    [ -n "$CHROOT_OUTPUT" ] || CHROOT_OUTPUT="$OUTPUT/grml_chroot"
502
503    # provide inform fai about the ISO we build
504    [ -d "$CHROOT_OUTPUT/etc/" ] || mkdir -p "$CHROOT_OUTPUT/etc/"
505    echo '# This file has been generated by grml-live.' > "$CHROOT_OUTPUT/etc/grml_live_version"
506    [ -n "$GRML_LIVE_VERSION" ] && echo "GRML_LIVE_VERSION=$GRML_LIVE_VERSION" >> "$CHROOT_OUTPUT/etc/grml_live_version"
507    [ -n "$SUITE" ] && echo "SUITE=$SUITE" >> "$CHROOT_OUTPUT/etc/grml_live_version"
508
509    if [ -n "$UPDATE" -o -n "$BUILD_ONLY" ] ; then
510       FAI_ACTION=softupdate
511    else
512       FAI_ACTION=dirinstall
513    fi
514
515    if [ -n "$UPDATE" -o -n "$BUILD_ONLY" ] ; then
516       if ! [ -r "$CHROOT_OUTPUT/etc/debian_version" ] ; then
517          log    "Error: does not look like you have a working chroot. Updating/building not possible."
518          eerror "Error: does not look like you have a working chroot. Updating/building not possible. (Drop -u/-b option?)"
519          eend 1
520          bailout 20
521       fi
522    fi
523
524    if [ -d "$CHROOT_OUTPUT/bin" -a -z "$UPDATE" -a -z "$BUILD_ONLY" ] ; then
525       log   "Skipping stage 'fai dirinstall' as $CHROOT_OUTPUT exists already."
526       ewarn "Skipping stage 'fai dirinstall' as $CHROOT_OUTPUT exists already." ; eend 0
527    else
528       mkdir -p "$CHROOT_OUTPUT" || bailout 5 "Problem with creating $CHROOT_OUTPUT for FAI"
529
530       if [ -n "${MIRROR_DIRECTORY}" ] ; then
531          mkdir -p "${CHROOT_OUTPUT}/${MIRROR_DIRECTORY}"
532          mount --bind "${MIRROR_DIRECTORY}" "${CHROOT_OUTPUT}/${MIRROR_DIRECTORY}"
533       fi
534
535       log "Executed FAI command line:"
536       log "BUILD_ONLY=$BUILD_ONLY fai $VERBOSE -C $GRML_FAI_CONFIG -c$CLASSES -u $HOSTNAME $FAI_ACTION $CHROOT_OUTPUT $FAI_ARGS"
537       BUILD_ONLY="$BUILD_ONLY" fai $VERBOSE -C "$GRML_FAI_CONFIG" -c"$CLASSES" -u \
538       "$HOSTNAME" $FAI_ACTION "$CHROOT_OUTPUT" $FAI_ARGS | tee -a $LOGFILE
539       RC="$PIPESTATUS" # notice: bash-only
540
541       FORCE_ISO_REBUILD=true
542
543       if [ "$RC" != 0 ] ; then
544          log    "Error: critical error while executing fai [exit code ${RC}]. Exiting."
545          eerror "Error: critical error while executing fai [exit code ${RC}]. Exiting." ; eend 1
546          bailout 1
547       else
548          log "Setting /etc/grml_version to $GRML_NAME $VERSION Release Codename $RELEASENAME [$DATE]"
549          echo "$GRML_NAME $VERSION Release Codename $RELEASENAME [$DATE]" > $CHROOT_OUTPUT/etc/grml_version
550          chmod 644 $CHROOT_OUTPUT/etc/grml_version
551          einfo "Rebuilding initramfs"
552          # make sure new /etc/grml_version reaches the initramfs:
553          # chroot $CHROOT_OUTPUT update-initramfs -u -t => might break when using kernel-package :(
554          chroot $CHROOT_OUTPUT update-initramfs -u -k all
555          eend $?
556       fi
557
558       # Remove all FAI logs from chroot if class RELEASE is used:
559       if [ -f "$CHROOT_OUTPUT"/etc/grml_fai_release ] ; then
560          rm -rf "$CHROOT_OUTPUT"/var/log/fai/*
561          rm -f "$CHROOT_OUTPUT"/var/log/install_packages.list
562       fi
563
564       umount_all
565
566       # notice: 'fai dirinstall' does not seem to exit appropriate, so:
567       ERROR=''
568       CHECKLOG=/var/log/fai/$HOSTNAME/last
569       if [ -r "$CHECKLOG/software.log" ] ; then
570          # 1 errors during executing of commands
571          grep 'dpkg: error processing' $CHECKLOG/software.log >> $LOGFILE && ERROR=1
572          grep 'E: Method http has died unexpectedly!' $CHECKLOG/software.log >> $LOGFILE && ERROR=2
573          grep 'ERROR: chroot' $CHECKLOG/software.log >> $LOGFILE && ERROR=3
574          grep 'E: Failed to fetch' $CHECKLOG/software.log >> $LOGFILE && ERROR=4
575          grep 'Unable to write mmap - msync (28 No space left on device)' $CHECKLOG/software.log >> $LOGFILE && ERROR=5
576       fi
577
578       if [ -r "$CHECKLOG/shell.log" ] ; then
579          grep 'FAILED with exit code' $CHECKLOG/shell.log >> $LOGFILE && ERROR=6
580       fi
581
582       if [ -n "$ERROR" ] ; then
583          log    "Error: there was a critical error [${ERROR}] during execution of stage 'fai dirinstall' [$(date)]"
584          eerror "Error: there was a critical error during execution of stage 'fai dirinstall'"
585          eerror "Note:  check out ${CHECKLOG}/ for details. [exit ${ERROR}]"
586          eend 1
587          bailout 1
588       else
589          log "Finished execution of stage 'fai dirinstall' [$(date)]"
590          einfo "Finished execution of stage 'fai dirinstall'"
591       fi
592
593       einfo "Find FAI build logs at $(readlink -f /var/log/fai/$HOSTNAME/last)"
594       log   "Find FAI build logs at $(readlink -f /var/log/fai/$HOSTNAME/last)"
595       eend 0
596    fi
597 fi # BUILD_DIRTY?
598 # }}}
599
600 # package validator {{{
601 CHECKLOG=/var/log/fai/$HOSTNAME/last
602 # package validator
603 if [ -r "$CHECKLOG/package_errors.log" ] && grep -q '[a-z]' "$CHECKLOG/package_errors.log" ; then
604
605    if [ -n "$EXIT_ON_MISSING_PACKAGES" -a -z "$BUILD_DIRTY" ] ; then
606       eerror "The following packages were requested for installation but could not be processed:"
607       cat $CHECKLOG/package_errors.log
608       eerror "... exiting as requested via \$EXIT_ON_MISSING_PACKAGES."
609       eend 1
610       bailout 13
611    else
612       ewarn "The following packages were requested for installation but could not be processed:"
613       cat $CHECKLOG/package_errors.log
614       eend 0
615    fi
616 fi
617 # }}}
618
619 # BUILD_OUTPUT - execute arch specific stuff and squashfs {{{
620 [ -n "$BUILD_OUTPUT" ] || BUILD_OUTPUT="$OUTPUT/grml_cd"
621 mkdir -p "$BUILD_OUTPUT" || bailout 6 "Problem with creating $BUILD_OUTPUT for stage ARCH"
622
623 # i386:
624 if [ "$ARCH" = i386 ] || [ "$ARCH" = amd64 ] ; then
625    if [ -d "$BUILD_OUTPUT"/boot/isolinux -a -z "$UPDATE" -a -z "$BUILD_ONLY" ] ; then
626       log   "Skipping stage 'boot' as $BUILD_OUTPUT/boot/isolinux exists already."
627       ewarn "Skipping stage 'boot' as $BUILD_OUTPUT/boot/isolinux exists already." ; eend 0
628    else
629       # booting stuff:
630       [ -d "$BUILD_OUTPUT"/boot/isolinux ] || mkdir -p "$BUILD_OUTPUT"/boot/isolinux
631       [ -d "$BUILD_OUTPUT"/boot/"${SHORT_NAME}" ] || mkdir -p "$BUILD_OUTPUT"/boot/"${SHORT_NAME}"
632
633       if [ -z "$NO_ADDONS" ] ; then
634          [ -d "$BUILD_OUTPUT"/boot/addons   ] || mkdir -p "$BUILD_OUTPUT"/boot/addons
635          if [ -r "$TEMPLATE_DIRECTORY"/boot/addons/memtest ] ; then
636             log "Installing $TEMPLATE_DIRECTORY/boot/addons/memtest"
637             cp "$TEMPLATE_DIRECTORY"/boot/addons/memtest "$BUILD_OUTPUT"/boot/addons/memtest
638          elif [ -r /boot/memtest86+.bin ] ; then
639             log "Installing /boot/memtest86+.bin"
640             cp /boot/memtest86+.bin "$BUILD_OUTPUT"/boot/addons/memtest
641          else
642             ewarn "No memtest binary found (either install package grml-live-addons or memtest86+), skipping."
643             log "No memtest binary found (either install package grml-live-addons or memtest86+), skipping."
644             eend 0
645          fi
646       fi
647
648       # if we don't have an initrd we a) can't boot and b) there was an error
649       # during build, so check for the file:
650       INITRD="$(ls $CHROOT_OUTPUT/boot/initrd* 2>/dev/null| grep -v '.bak$' | sort -r | head -1)"
651       if [ -n "$INITRD" ] ; then
652          cp $INITRD "$BUILD_OUTPUT"/boot/"${SHORT_NAME}"/initrd.gz
653          find $CHROOT_OUTPUT/boot/ -name initrd\*.bak -exec rm {} \;
654       else
655          log    "Error: No initrd found inside $CHROOT_OUTPUT/boot/ - Exiting"
656          eerror "Error: No initrd found inside $CHROOT_OUTPUT/boot/ - Exiting" ; eend 1
657          bailout 10
658       fi
659
660       KERNEL_IMAGE="$(ls $CHROOT_OUTPUT/boot/vmlinuz* 2>/dev/null | sort -r | head -1)"
661       if [ -n "$KERNEL_IMAGE" ] ; then
662          cp "$KERNEL_IMAGE" "$BUILD_OUTPUT"/boot/"${SHORT_NAME}"/linux26
663       else
664          log    "Error: No kernel found inside $CHROOT_OUTPUT/boot/ - Exiting"
665          eerror "Error: No kernel found inside $CHROOT_OUTPUT/boot/ - Exiting" ; eend 1
666          bailout 11
667       fi
668
669       [ -n "$TEMPLATE_DIRECTORY" ] || TEMPLATE_DIRECTORY='/usr/share/grml-live/templates'
670       if ! [ -d "${TEMPLATE_DIRECTORY}"/boot ] ; then
671          log    "Error: ${TEMPLATE_DIRECTORY}/boot does not exist. Exiting."
672          eerror "Error: ${TEMPLATE_DIRECTORY}/boot does not exist. Exiting." ; eend 1
673          bailout 8
674       fi
675
676       # *always* copy files to output directory so the variables
677       # get adjusted according to the build
678       cp ${TEMPLATE_DIRECTORY}/boot/isolinux/*  "$BUILD_OUTPUT"/boot/isolinux/
679
680       if [ -n "$NO_ADDONS" ] ; then
681          log   "Skipping installation of boot addons as requested via \$NO_ADDONS."
682          einfo "Skipping installation of boot addons as requested via \$NO_ADDONS."; eend 0
683       else
684          if ! [ -d "$TEMPLATE_DIRECTORY"/boot/addons ] ; then
685            log   "Boot addons not found, skipping therefore. (Consider installing package grml-live-addons)"
686            ewarn "Boot addons not found, skipping therefore. (Consider installing package grml-live-addons)" ; eend 0
687          else
688            # copy only files so we can handle bsd4grml on its own
689            for file in ${TEMPLATE_DIRECTORY}/boot/addons/* ; do
690              test -f $file && cp $file "$BUILD_OUTPUT"/boot/addons/
691            done
692
693            if [ -n "$NO_ADDONS_BSD4GRML" ] ; then
694               log   "Skipping installation of bsd4grml as requested via \$NO_ADDONS_BSD4GRML."
695               einfo "Skipping installation of bsd4grml as requested via \$NO_ADDONS_BSD4GRML."; eend 0
696            else
697               if [ -d "$TEMPLATE_DIRECTORY"/boot/addons/bsd4grml ] ; then
698                 cp -a ${TEMPLATE_DIRECTORY}/boot/addons/bsd4grml "$BUILD_OUTPUT"/boot/addons/
699               else
700                 log   "bsd4grml addon not found, skipping therefore."
701                 ewarn "bsd4grml addon not found, skipping therefore." ; eend 0
702               fi
703            fi
704
705          fi # no "$TEMPLATE_DIRECTORY"/boot/addons
706       fi # NO_ADDONS
707
708       if ! [ -d ${TEMPLATE_DIRECTORY}/boot/grub ] ; then
709          log   "grub templates do not exist, skipping therefore."
710          ewarn "grub templates do not exist, skipping therefore." ; eend 0
711       else
712          if ! [ -d "${BUILD_OUTPUT}/boot/grub" ] ; then
713             cp -a ${TEMPLATE_DIRECTORY}/boot/grub  "$BUILD_OUTPUT"/boot/
714          fi
715
716          # make sure we have recent template files available, otherwise updating
717          # the strings like $GRML_NAME and $VERSION might be out of date
718          cp ${TEMPLATE_DIRECTORY}/boot/grub/* "$BUILD_OUTPUT"/boot/grub/
719       fi
720
721       if ! [ -d "${TEMPLATE_DIRECTORY}"/GRML ] ; then
722          log    "Error: ${TEMPLATE_DIRECTORY}/GRML does not exist. Exiting."
723          eerror "Error: ${TEMPLATE_DIRECTORY}/GRML does not exist. Exiting." ; eend 1
724          bailout 9
725       fi
726
727       [ -d "$BUILD_OUTPUT"/GRML ] || mkdir "$BUILD_OUTPUT"/GRML
728       cp -a ${TEMPLATE_DIRECTORY}/GRML/* "$BUILD_OUTPUT"/GRML/
729
730       # adjust boot splash information:
731       RELEASE_INFO="$GRML_NAME $VERSION - Release Codename $RELEASENAME"
732       RELEASE_INFO="$(cut_string 68 "$RELEASE_INFO")"
733       RELEASE_INFO="$(extend_string_end 68 "$RELEASE_INFO")"
734
735       if [ -r "$BUILD_OUTPUT"/GRML/grml-version ] ; then
736          sed -i "s/%RELEASE_INFO%/$GRML_NAME $VERSION - $RELEASENAME/" "$BUILD_OUTPUT"/GRML/grml-version
737          sed -i "s/%DATE%/$DATE/"                                      "$BUILD_OUTPUT"/GRML/grml-version
738       fi
739
740       # make sure the squashfs filename is set accordingly:
741       SQUASHFS_NAME="$GRML_NAME.squashfs"
742
743       if [ -n "$NO_BOOTID" ] ; then
744          log   'Skipping bootid feature as requested via $NO_BOOTID.'
745          einfo 'Skipping bootid feature as requested via $NO_BOOTID.'
746       else
747          [ -n "$BOOTID" ] || BOOTID="$(echo ${GRML_NAME}${VERSION} | tr -d ',./;\- ')"
748          [ -d "$BUILD_OUTPUT"/conf ] || mkdir "$BUILD_OUTPUT"/conf
749          einfo "Generating /conf/bootid.txt with entry ${BOOTID}."
750          log   "Generating /conf/bootid.txt with entry ${BOOTID}."
751          echo "$BOOTID" > "$BUILD_OUTPUT"/conf/bootid.txt
752          eend $?
753       fi
754
755       # adjust all variables in the templates with the according distribution information
756       for file in "${BUILD_OUTPUT}"/boot/isolinux/*.cfg "${BUILD_OUTPUT}"/boot/isolinux/*.msg \
757                   "${BUILD_OUTPUT}"/boot/grub/* ; do
758         if [ -r "${file}" ] ; then
759           sed -i "s/%ARCH%/$ARCH/g"                    "${file}"
760           sed -i "s/%DATE%/$DATE/g"                    "${file}"
761           sed -i "s/%DISTRI_INFO%/$DISTRI_INFO/g"      "${file}"
762           sed -i "s/%DISTRI_NAME%/$DISTRI_NAME/g"      "${file}"
763           sed -i "s/%DISTRI_SPLASH%/$DISTRI_SPLASH/g"  "${file}"
764           sed -i "s/%GRML_NAME%/$GRML_NAME/g"          "${file}"
765           sed -i "s/%SQUASHFS_NAME%/$SQUASHFS_NAME/g"  "${file}"
766           sed -i "s/%RELEASE_INFO%/$RELEASE_INFO/g"    "${file}"
767           sed -i "s/%SHORT_NAME%/$SHORT_NAME/g"        "${file}"
768           sed -i "s/%VERSION%/$VERSION/g"              "${file}"
769
770           [ -n "$DEFAULT_BOOTOPTIONS" ] && sed -i "s/ boot=live/ boot=live $DEFAULT_BOOTOPTIONS/"  "${file}"
771
772           if [ -n "$NO_BOOTID" ] ; then
773              sed -i "s/ bootid=%BOOTID%//g" "${file}" # drop bootid bootoption
774           else
775              sed -i "s/%BOOTID%/$BOOTID/g" "${file}" # adjust bootid=... argument
776           fi
777         fi
778       done
779
780       # adjust bootsplash accordingly but make sure the string has the according lenght
781       SQUASHFS_NAME="$(cut_string 20 "$SQUASHFS_NAME")"
782       SQUASHFS_NAME="$(extend_string_end 20 "$SQUASHFS_NAME")"
783       for file in f4 f5 ; do
784          if [ -r "${BUILD_OUTPUT}/boot/isolinux/${file}" ] ; then
785             sed -i "s/%SQUASHFS_NAME%/$SQUASHFS_NAME/" "${BUILD_OUTPUT}/boot/isolinux/${file}"
786             sed -i "s/%SQUASHFS_NAME%/$SQUASHFS_NAME/" "${BUILD_OUTPUT}/boot/isolinux/${file}"
787          fi
788       done
789
790       # generate addon list
791       rm "${BUILD_OUTPUT}/${ADDONS_LIST_FILE}"
792       for name in "${BUILD_OUTPUT}"/boot/isolinux/addon_*.cfg ; do
793         include_name=$(basename "$name")
794         echo "include $include_name"  >> "${BUILD_OUTPUT}/${ADDONS_LIST_FILE}"
795       done
796
797       if ! [ -r "${BUILD_OUTPUT}/boot/isolinux/${DISTRI_NAME}.cfg" ] || [ "$DISTRI_NAME" = "grml" ] ; then
798          log "including grmlmain.cfg in ${BUILD_OUTPUT}/boot/isolinux/distri.cfg"
799          echo "include grmlmain.cfg"    >  "${BUILD_OUTPUT}/boot/isolinux/distri.cfg"
800          echo "include default.cfg"     >  "${BUILD_OUTPUT}/boot/isolinux/grmlmain.cfg"
801          echo "include menuoptions.cfg" >> "${BUILD_OUTPUT}/boot/isolinux/grmlmain.cfg"
802          echo "include grml.cfg"        >> "${BUILD_OUTPUT}/boot/isolinux/grmlmain.cfg"
803
804          for f in "${BUILD_OUTPUT}"/boot/isolinux/submenu*.cfg ; do
805            echo "include $(basename $f)"     >> "${BUILD_OUTPUT}/boot/isolinux/grmlmain.cfg"
806          done
807
808          echo "include options.cfg"     >> "${BUILD_OUTPUT}/boot/isolinux/grmlmain.cfg"
809          if [ ! -n "$NO_ADDONS" ] ; then
810            echo "include addons.cfg"    >> "${BUILD_OUTPUT}/boot/isolinux/grmlmain.cfg"
811          fi
812          echo "include isoprompt.cfg"   >> "${BUILD_OUTPUT}/boot/isolinux/grmlmain.cfg"
813          echo "include hd.cfg"          >> "${BUILD_OUTPUT}/boot/isolinux/grmlmain.cfg"
814          echo "include hidden.cfg"      >> "${BUILD_OUTPUT}/boot/isolinux/grmlmain.cfg"
815       else # assume we are building a custom distribution:
816          log "File ${BUILD_OUTPUT}/boot/isolinux/${DISTRI_NAME}.cfg found, using it."
817          einfo "File ${BUILD_OUTPUT}/boot/isolinux/${DISTRI_NAME}.cfg found, using it."
818          if grep -q "^include ${DISTRI_NAME}.cfg" "${BUILD_OUTPUT}/boot/isolinux/distri.cfg" ; then
819            log "include for ${DISTRI_NAME}.cfg already present, nothing to do."
820            eindent
821            einfo "include for ${DISTRI_NAME}.cfg already present, nothing to do."
822            eoutdent
823            eend $?
824         else
825            log "including ${DISTRI_NAME}.cfg in ${BUILD_OUTPUT}/boot/isolinux/distri.cfg"
826            echo "include ${DISTRI_NAME}.cfg" > "${BUILD_OUTPUT}/boot/isolinux/distri.cfg"
827            [ -n "$NO_ADDONS" ] || echo "include addons.cfg" >> "${BUILD_OUTPUT}/boot/isolinux/distri.cfg"
828          fi
829       fi
830
831       # use old style console based isolinux method only if requested:
832       if [[ "${ISOLINUX_METHOD}" == "console" ]] ; then
833          log 'Using console based isolinux method as requested via $ISOLINUX_METHOD.'
834          einfo 'Using console based isolinux method as requested via $ISOLINUX_METHOD.'
835          if grep -q '^include console.cfg' "${BUILD_OUTPUT}/boot/isolinux/distri.cfg" ; then
836            einfo "include for console.cfg already foud, nothing to do."
837            eend 0
838          else
839            log "including console.cfg in ${BUILD_OUTPUT}/boot/isolinux/isolinux.cfg"
840            einfo "including console.cfg in ${BUILD_OUTPUT}/boot/isolinux/isolinux.cfg"
841            echo "include console.cfg" >> "${BUILD_OUTPUT}/boot/isolinux/isolinux.cfg"
842            eend $?
843          fi
844       else
845          log 'Using graphical boot menu.'
846          if grep -q '^include vesamenu.cfg' "${BUILD_OUTPUT}/boot/isolinux/isolinux.cfg" ; then
847            log "include for vesamenu.cfg already foud, nothing to do."
848          else
849            log "including vesamenu.cfg in ${BUILD_OUTPUT}/boot/isolinux/isolinux.cfg"
850            echo "include vesamenu.cfg" >> "${BUILD_OUTPUT}/boot/isolinux/isolinux.cfg"
851          fi
852       fi
853
854       # jump back to grub from bsd4grml (/boot/grub/stage2):
855       GRUB_LEGACY=stage2
856
857       if [ -e "$BUILD_OUTPUT"/boot/addons/bsd4grml/boot.6 ]; then
858          if [ -e "$BUILD_OUTPUT"/boot/grub/core.img ]; then
859             GRUB_VERSION=2
860          else
861             GRUB_VERSION=1
862          fi
863
864          for file in "$BUILD_OUTPUT"/boot/addons/bsd4grml/boot.6 \
865                      "$BUILD_OUTPUT"/boot/addons/bsd4grml/boot.cfg \
866                      "$BUILD_OUTPUT"/boot/isolinux/*.cfg \
867                      "$BUILD_OUTPUT"/boot/grub/grub.cfg \
868                      "$BUILD_OUTPUT"/boot/grub/menu.lst ; do
869              if [ -e "$file" ] ; then
870                sed -i -e "s!%GRUB_VERSION%!$GRUB_VERSION!g" \
871                       -e "s!%GRUB_LEGACY%!$GRUB_LEGACY!g" "$file"
872              fi
873          done
874
875          sed -i "s/%RELEASE_INFO%/$GRML_NAME $VERSION - $RELEASENAME/" "$BUILD_OUTPUT"/boot/addons/bsd4grml/boot.6
876       fi
877
878       if [ -e "$BUILD_OUTPUT"/boot/grub/$GRUB_LEGACY ]; then
879          sed -i "s/%GRUB_LEGACY%/$GRUB_LEGACY/g" "$BUILD_OUTPUT"/boot/grub/menu.lst
880          sed -i "s/%GRUB_LEGACY%/$GRUB_LEGACY/g" "$BUILD_OUTPUT"/boot/grub/grub.cfg
881       elif [ -e "$BUILD_OUTPUT"/boot/grub/menu.lst -a -e "$BUILD_OUTPUT"/boot/grub/grub.cfg ] ; then
882          sed -i "/%GRUB_LEGACY%/d" "$BUILD_OUTPUT"/boot/grub/menu.lst
883          sed -i "/%GRUB_LEGACY%/d" "$BUILD_OUTPUT"/boot/grub/grub.cfg
884       fi
885
886       DPKG_LIST="/var/log/fai/$HOSTNAME/last/dpkg.list" # the dpkg --list output of the chroot
887       if ! [ -r "$DPKG_LIST" ] ; then
888          ewarn "$DPKG_LIST could not be read, ignoring to store package information on ISO therefore."
889       else
890          einfo "Storing package list information as /GRML/${GRML_NAME}-packages.txt on ISO."
891          cp "$DPKG_LIST" "${BUILD_OUTPUT}/GRML/${GRML_NAME}-packages.txt"
892          eend $?
893       fi
894
895       # autostart for Windows:
896       if [ -d "${TEMPLATE_DIRECTORY}/windows/autostart/" ] ; then
897          cp ${TEMPLATE_DIRECTORY}/windows/autostart/* "$BUILD_OUTPUT"/
898       fi
899
900       # windows-binaries:
901       if [ -n "$NO_WINDOWS_BINARIES" ] ; then
902          log   "Skipping download of windows binaries as requested via \$NO_WINDOWS_BINARIES."
903          einfo "Skipping download of windows binaries as requested via \$NO_WINDOWS_BINARIES."
904          eend 0
905       else
906          if [ -f "$BUILD_OUTPUT"/windows/putty.exe ] ; then
907             log   "Skipping stage 'WINDOWS_BINARIES' as $BUILD_OUTPUT/windows exists already."
908             ewarn "Skipping stage 'WINDOWS_BINARIES' as $BUILD_OUTPUT/windows exists already." ; eend 0
909          else
910             if ! [ -d "$BUILD_OUTPUT"/windows ] ; then
911                mkdir "$BUILD_OUTPUT"/windows
912                ( cd "$BUILD_OUTPUT"/windows
913                  for file in pageant plink pscp psftp putty puttygen ; do
914                     wget -O ${file}.exe ${WINDOWS_BINARIES}/${file}.exe
915                     md5sum ${file}.exe > ${file}.exe.md5
916                  done )
917             fi
918
919             log "Finished execution of stage 'WINDOWS_BINARIES' [$(date)]"
920             einfo "Finished execution of stage 'WINDOWS_BINARIES'" ; eend 0
921          fi
922       fi
923
924    FORCE_ISO_REBUILD=true
925    einfo "Finished execution of stage 'boot'" ; eend 0
926    fi
927 else
928    log    'Error: Unsupported ARCH, sorry. Want to support it? Contribute!'
929    eerror 'Error: Unsupported ARCH, sorry. Want to support it? Contribute!' ; eend 1
930    bailout
931 fi
932
933 # support installation of local files into the chroot/ISO
934 if [ -n "$CHROOT_INSTALL" ] ; then
935   if ! [ -d "$CHROOT_INSTALL" ] ; then
936      log "Configuration variable \$CHROOT_INSTALL is set but not a directory; ignoring"
937      ewarn "Configuration variable \$CHROOT_INSTALL is set but not a directory; ignoring"
938   else
939      log "Copying local files to chroot as requested via \$CHROOT_INSTALL"
940      einfo "Copying local files to chroot as requested via \$CHROOT_INSTALL"
941      rsync -avz --inplace "$CHROOT_INSTALL"/ "$CHROOT_OUTPUT/"
942      eend $?
943      einfo "Make sure to run squashfs stage, otherwise your local files won't be part of the ISO."
944      FORCE_ISO_REBUILD=true
945   fi
946 fi
947
948 if [ -f "$BUILD_OUTPUT"/live/${GRML_NAME}.squashfs -a -z "$UPDATE" -a -z "$BUILD_ONLY" -a -z "$BUILD_DIRTY" ] ; then
949    log   "Skipping stage 'squashfs' as $BUILD_OUTPUT/live exists already."
950    ewarn "Skipping stage 'squashfs' as $BUILD_OUTPUT/live exists already." ; eend 0
951 elif [ -n "$SKIP_MKSQUASHFS" ] ; then
952    log   "Skipping stage 'squashfs' as requested via option -q"
953    ewarn "Skipping stage 'squashfs' as requested via option -q" ; eend 0
954 else
955    [ -d "$BUILD_OUTPUT"/live ] || mkdir "$BUILD_OUTPUT"/live
956    # make sure we don't leave (even an empty) base.tgz:
957    [ -f "$CHROOT_OUTPUT/base.tgz" ] && rm -f "$CHROOT_OUTPUT/base.tgz"
958
959    # if unconfigured default to squashfs-tools' mksquashfs binary
960    if [ -z "$SQUASHFS_BINARY" ] ; then
961       SQUASHFS_BINARY='mksquashfs'
962    fi
963
964    if which "$SQUASHFS_BINARY" >/dev/null 2>&1 ; then
965       log    "Using mksquashfs binary ${SQUASHFS_BINARY}"
966       einfo  "Using mksquashfs binary ${SQUASHFS_BINARY}" ; eend 0
967    else
968       log    "Error: mksquashfs binary ($SQUASHFS_BINARY) not found. Exiting."
969       eerror "Error: mksquashfs binary ($SQUASHFS_BINARY) not found. Exiting." ; eend 1
970       bailout
971    fi
972
973    # use sane defaults if $SQUASHFS_OPTIONS isn't set
974    if [ -z "$SQUASHFS_OPTIONS" ] ; then
975      # use blocksize 256k as this gives best result with regards to time + compression
976      SQUASHFS_OPTIONS="-b 256k"
977
978      # set lzma/xz compression by default, unless -z option has been specified on command line
979      if [ -z "$SQUASHFS_ZLIB" ] ; then
980         SQUASHFS_OPTIONS="$SQUASHFS_OPTIONS -comp xz"
981      else
982         SQUASHFS_OPTIONS="$SQUASHFS_OPTIONS -comp gzip"
983      fi
984    fi
985
986    # support exclusion of files via exclude-file:
987    if [ -n "$SQUASHFS_EXCLUDES_FILE" -a "$SQUASHFS_EXCLUDES_FILE" ] ; then
988       SQUASHFS_OPTIONS="$SQUASHFS_OPTIONS -ef $SQUASHFS_EXCLUDES_FILE"
989    fi
990
991    # get rid of unnecessary files when building grml-small for final release:
992    if echo "$CLASSES" | grep -q GRML_SMALL ; then
993       SQUASHFS_OPTIONS="$SQUASHFS_OPTIONS -e initrd.img* vmlinuz*"
994    fi
995
996    # log stuff
997    SQUASHFS_STDERR="$(mktemp -t grml-live.XXXXXX)"
998
999    # informational stuff
1000    [ -n "$SQUASHFS_OPTIONS" ]  && SQUASHFS_INFO_MSG="$SQUASHFS_OPTIONS"
1001    [ -n "$SQUASHFS_INFO_MSG" ] && SQUASHFS_INFO_MSG="using options: $SQUASHFS_INFO_MSG"
1002    einfo "Squashfs build information: running binary $SQUASHFS_BINARY $SQUASHFS_INFO_MSG"
1003
1004    log "$SQUASHFS_BINARY $CHROOT_OUTPUT/* $BUILD_OUTPUT/live/${GRML_NAME}.squashfs -noappend $SQUASHFS_OPTIONS"
1005
1006    if $SQUASHFS_BINARY $CHROOT_OUTPUT/* $BUILD_OUTPUT/live/"${GRML_NAME}".squashfs \
1007       -noappend $SQUASHFS_OPTIONS 2>"${SQUASHFS_STDERR}" ; then
1008       echo "${GRML_NAME}.squashfs" > $BUILD_OUTPUT/live/filesystem.module
1009       log "Finished execution of stage 'squashfs' [$(date)]"
1010       einfo "Finished execution of stage 'squashfs'" ; eend 0
1011    else
1012       log    "Error: there was a critical error executing stage 'squashfs' [$(date)]:"
1013       log    "$(cat $SQUASHFS_STDERR)"
1014       eerror "Error: there was a critical error executing stage 'squashfs':"
1015       cat    "${SQUASHFS_STDERR}"
1016       eend 1
1017       bailout
1018    fi
1019
1020    FORCE_ISO_REBUILD=true
1021 fi
1022
1023 # create md5sum file:
1024 ( cd $BUILD_OUTPUT/GRML &&
1025 find .. -type f -not -name md5sums -not -name isolinux.bin -exec md5sum {} \; > md5sums )
1026 # }}}
1027
1028 # ISO_OUTPUT - mkisofs {{{
1029 [ -n "$ISO_OUTPUT" ] || ISO_OUTPUT="$OUTPUT/grml_isos"
1030 [ -n "$ISO_NAME" ] || ISO_NAME="${GRML_NAME}_${VERSION}.iso"
1031
1032 if [ "$BOOT_METHOD" = "isolinux" ] ; then
1033    BOOT_ARGS="-no-emul-boot -boot-load-size 4 -boot-info-table -b boot/isolinux/isolinux.bin -c boot/isolinux/boot.cat"
1034 elif [ "$BOOT_METHOD" = "grub" ] ; then
1035    BOOT_ARGS="-no-emul-boot -boot-load-size 4 -boot-info-table -b boot/grub/stage2"
1036 elif [ "$BOOT_METHOD" = "grub2" ] ; then
1037    BOOT_ARGS="-no-emul-boot -boot-load-size 4 -b boot/grub/toriboot.bin"
1038 fi
1039
1040 # Just until http://bts.grml.org/grml/issue945 has been resolved.
1041 # HYBRID_METHOD defaults to manifold, so make sure the default works OOTB.
1042 if [[ $BOOT_METHOD != isolinux && ($HYBRID_METHOD = isohybrid || $HYBRID_METHOD = manifold) ]]; then
1043   log   "Setting HYBRID_METHOD to grub2 as hybrid mode does not work with isohybrid yet."
1044   ewarn "Setting HYBRID_METHOD to grub2 as hybrid mode does not work with isohybrid yet."
1045   HYBRID_METHOD='grub2'
1046   eend 0
1047 fi
1048
1049 if [ -f "${ISO_OUTPUT}/${ISO_NAME}" -a -z "$UPDATE" -a -z "$BUILD_ONLY" -a -z "$BUILD_DIRTY" -a "$FORCE_ISO_REBUILD" = "false" ]  ; then
1050    log   "Skipping stage 'iso build' as $ISO_OUTPUT/${ISO_NAME} exists already."
1051    ewarn "Skipping stage 'iso build' as $ISO_OUTPUT/${ISO_NAME} exists already." ; eend 0
1052 elif [ -n "$SKIP_MKISOFS" ] ; then
1053    log   "Skipping stage 'iso build' as requested via option -n"
1054    ewarn "Skipping stage 'iso build' as requested via option -n" ; eend 0
1055 else
1056    mkdir -p "$ISO_OUTPUT" || bailout 6 "Problem with creating $ISO_OUTPUT for stage 'iso build'"
1057
1058    if $FORCE_ISO_REBUILD && ! [ -f "${ISO_OUTPUT}/${ISO_NAME}" ] ; then
1059       log   "Forcing rebuild of ISO because files on ISO have been modified."
1060       einfo "Forcing rebuild of ISO because files on ISO have been modified."
1061    fi
1062
1063    # support mkisofs as well as genisoimage
1064    if which mkisofs >/dev/null 2>&1; then
1065       MKISOFS='mkisofs'
1066    elif which genisoimage >/dev/null 2>&1; then
1067       MKISOFS='genisoimage'
1068    else
1069       log    "Error: neither mkisofs nor genisoimage available - can not create ISO."
1070       eerror "Error: neither mkisofs nor genisoimage available - can not create ISO." ; eend 1
1071       bailout
1072    fi
1073
1074    CURRENT_DIR=$(pwd)
1075    if cd "$BUILD_OUTPUT" ; then
1076       if [ "$BOOT_METHOD" = "grub2" ]; then
1077          # make a 2048-byte bootsector for El Torito
1078          dd if=/dev/zero of=boot/grub/toriboot.bin bs=512 count=4 2>/dev/null
1079          # those are in 2048-byte sectors, so 1 16 matches 4 63 below
1080          echo 1 16 | mksh /usr/share/grml-live/scripts/bootgrub.mksh -B 11 | \
1081             dd of=boot/grub/toriboot.bin conv=notrunc 2>/dev/null
1082       fi
1083       log "$MKISOFS -V '${GRML_NAME} ${VERSION}' -publisher 'grml-live | grml.org' -l -r -J $BOOT_ARGS -o ${ISO_OUTPUT}/${ISO_NAME} ."
1084       "$MKISOFS" -V "${GRML_NAME} ${VERSION}" -publisher 'grml-live | grml.org' \
1085               -l -r -J $BOOT_ARGS -no-pad \
1086               -o "${ISO_OUTPUT}/${ISO_NAME}" . ; RC=$?
1087       # both of these need core.img there, so it’s easier to write it here
1088       if [ "$BOOT_METHOD" = "grub2" ] || [ "$HYBRID_METHOD" = "grub2" ]; then
1089          # must be <= 30720 bytes
1090          dd if=boot/grub/core.img of="${ISO_OUTPUT}/${ISO_NAME}" \
1091            conv=notrunc bs=512 seek=4 2>/dev/null
1092       fi
1093
1094       # pad the output ISO to multiples of 256 KiB for partition table support
1095       siz=$($getfilesize "${ISO_OUTPUT}/${ISO_NAME}")
1096       cyls=$((siz / 512 / 32 / 16 + 1))   # C=$cyls H=16 S=32
1097       siz=$((cyls * 16 * 32 * 512))   # size after padding
1098       dd if=/dev/zero bs=1 count=1 seek=$((siz - 1)) \
1099          of="${ISO_OUTPUT}/${ISO_NAME}" 2>/dev/null
1100
1101       # support disabling hybrid ISO image
1102       if [ "$HYBRID_METHOD" = "disable" ] ; then\
1103          log   "Skipping creation of hybrid ISO file as requested via HYBRID_METHOD=disable"
1104          einfo "Skipping creation of hybrid ISO file as requested via HYBRID_METHOD=disable"
1105          eend 0
1106       # use isohybrid only on request
1107       elif [ "$HYBRID_METHOD" = "isohybrid" ] ; then
1108          if ! which isohybrid >/dev/null 2>&1 ; then
1109            bailout 12 "isohybrid binary not found - please install syslinux/syslinux-common"
1110          else
1111            log "Creating hybrid ISO file with isohybrid method"
1112            einfo "Creating hybrid ISO file with isohybrid method"
1113            # Notes for consideration:
1114            # "-entry 4 -type 1c"
1115            # * using 4 as the partition number is supposed to help with BIOSes
1116            #   that only support USB-Zip boot
1117            # * using 1c (i.e. hidden FAT32 LBA), instead of the default 0x17
1118            #   (hidden NTFS, IIRC), as the partition type is sometimes needed
1119            #   to get the BIOS even look at the partition created by isohybrid
1120            isohybrid "${ISO_OUTPUT}/${ISO_NAME}"
1121            eend $?
1122          fi
1123       # by default use our manifold boot method:
1124       else
1125          # isoinfo is part of both mkisofs and genisoimage so we're good
1126          bootoff=$(isoinfo -l -i "${ISO_OUTPUT}/${ISO_NAME}" | \
1127            sed -n '/^.*\[ *\([0-9]*\)[] ].* ISOLINUX.BIN;1 *$/s//\1/p')
1128          if ! [ -r boot/grub/core.img ] ; then
1129            ewarn "boot/grub/core.img not found, not creating manifold boot ISO file"
1130          elif [ "${bootoff:-0}" -lt 1 ] ; then
1131            ewarn "isolinux.bin not found on the ISO file, disabling manifold boot"
1132          else
1133            log "Creating hybrid ISO file with manifold method"
1134            einfo "Creating hybrid ISO file with manifold method"
1135            if [ "$HYBRID_METHOD" = "grub2" ] ; then
1136                # 512 bytes: MBR, partition table, load GRUB 2
1137                echo 4 63 | mksh /usr/share/grml-live/scripts/bootgrub.mksh -A -M 4:0x96 -g $cyls:16:32
1138            else
1139               # read only one but 2048-byte sized (scale: << 2) sector
1140               echo $bootoff $bootoff | \
1141                  mksh /usr/share/grml-live/scripts/bootilnx.mksh -A -M 4:0x96 -g $cyls:16:32 -S 2
1142            fi | dd of="${ISO_OUTPUT}/${ISO_NAME}" conv=notrunc 2>/dev/null
1143            eend $?
1144          fi
1145       fi
1146
1147       # generate md5sum and sha1sum of ISO if we are using class 'RELEASE':
1148       case $CLASSES in *RELEASE*)
1149          [ "$RC" = 0 ] && \
1150          (
1151            if cd $ISO_OUTPUT ; then
1152              md5sum ${ISO_NAME} > ${ISO_NAME}.md5 && \
1153              touch -r ${ISO_NAME} ${ISO_NAME}.md5
1154              sha1sum ${ISO_NAME} > ${ISO_NAME}.sha1 && \
1155              touch -r ${ISO_NAME} ${ISO_NAME}.sha1
1156            fi
1157          )
1158          ;;
1159       esac
1160
1161       cd "$CURRENT_DIR"
1162    fi
1163
1164    if [ "$RC" = 0 ] ; then
1165       log   "Finished execution of stage 'iso build' [$(date)]"
1166       einfo "Finished execution of stage 'iso build'" ; eend 0
1167    else
1168       log    "Error: there was a critical error ($RC) executing stage 'iso build' [$(date)]"
1169       eerror "Error: there was a critical error executing stage 'iso build'" ; eend 1
1170       bailout $RC
1171    fi
1172 fi
1173 # }}}
1174
1175 # log build information to database if grml-live-db is installed and enabled {{{
1176 dpkg_to_db() {
1177 if [ -d /usr/share/grml-live-db ] ; then
1178
1179   # safe defaults
1180   DPKG_LIST="/var/log/fai/$HOSTNAME/last/dpkg.list" # the dpkg --list output of the chroot:
1181   [ -n "$DPKG_DATABASE" ]  || DPKG_DATABASE=/var/log/grml-live.db
1182   [ -n "$DPKG_DBSCRIPT" ]  || DPKG_DBSCRIPT=/usr/share/grml-live-db/scripts/dpkg-to-db
1183   [ -n "$DPKG_DBOPTIONS" ] || DPKG_DBOPTIONS="--database $DPKG_DATABASE --logfile $LOGFILE --flavour $GRML_NAME --dpkg $DPKG_LIST"
1184
1185   if ! [ -x "$DPKG_DBSCRIPT" ] ; then
1186     log "Error: $DPKG_DBSCRIPT is not executable, can not log dpkg information."
1187     eerror "Error: $DPKG_DBSCRIPT is not executable, can not log dpkg information." ; eend 1
1188     bailout 14
1189   fi
1190
1191   # disable by default for now, not sure whether really everyone is using a local db file
1192   #if ! touch "$DPKG_DATABASE" ; then
1193   #  eerror "Error: can not write to ${DPKG_DATABASE}, can not log dpkg information." ; eend 1
1194   #  bailout 14
1195   #fi
1196
1197   if ! [ -r "$DPKG_LIST" ] ; then
1198      log   "Warning: can not read $DPKG_LIST - can not provide information to $DPKG_DBSCRIPT (dirty build?)"
1199      ewarn "Warning: can not read $DPKG_LIST - can not provide information to $DPKG_DBSCRIPT (dirty build?)" ; eend 0
1200   else
1201      einfo "Logging $DPKG_LIST to database $DPKG_DATABASE"
1202      log "Logging $DPKG_LIST to database $DPKG_DATABASE"
1203      log "Executing $DPKG_DBSCRIPT $DPKG_DBOPTIONS"
1204      eindent
1205
1206      if DB_INFO=$("$DPKG_DBSCRIPT" $DPKG_DBOPTIONS 2>&1) ; then
1207        einfo "$DB_INFO"
1208        eend 0
1209      else
1210        eerror "$DB_INFO"
1211        eend 1
1212      fi
1213
1214      eoutdent
1215   fi
1216
1217 fi
1218 }
1219 # }}}
1220
1221 # finalize {{{
1222 [ -n "$start_seconds" ] && SECONDS="$[$(cut -d . -f 1 /proc/uptime)-$start_seconds]" || SECONDS="unknown"
1223 log "Successfully finished execution of $PN [$(date) - running ${SECONDS} seconds]"
1224
1225 dpkg_to_db # make sure we catch the last log line as well, therefore execute between log + einfo
1226
1227 einfo "Successfully finished execution of $PN [$(date) - running ${SECONDS} seconds]" ; eend 0
1228 bailout 0
1229 # }}}
1230
1231 ## END OF FILE #################################################################
1232 # vim:foldmethod=marker ts=2 ft=sh ai expandtab tw=80 sw=3