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