Rework resolv.conf handling, error out on wrong symlinks. [Closes: issue984]
[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.14.0'
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/XZ 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=1 ;;
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/XZ) 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    wheezy)   ;;
428    sid)      ;;
429    *) echo "Sorry, $SUITE is not a valid Debian suite, exiting.">&2; bailout 1 ;;
430 esac
431
432 DIST=" etch\| stable\| lenny\| squeeze\| wheezy\| testing\| sid\| unstable"
433 sed "s/\(^deb .\+\)\([ \t]*\)\($DIST\)\([ \t]*\)\(main \)/\1 \2$SUITE\4\5/" "$SOURCES_LIST_FILE" | sponge "$SOURCES_LIST_FILE"
434 for file in "$LIVE_CONF" "$CONFIG" "$LOCAL_CONFIG" ; do
435     if [ -n "$file" ] ; then
436        sed "s/^SUITE=.*/SUITE=\"$SUITE\"/" $file | sponge $file
437        sed "s/\(^deb .\+\)\([ \t]*\)\($DIST\)\([ \t]*\)\(main \)/\1 \2$SUITE\4\5/" "$file" | sponge "$file"
438     fi
439 done
440
441 # notice: activate grml-live pool only if we are building against unstable:
442 if grep -qwe unstable -qwe sid "$SOURCES_LIST_FILE" ; then
443    grep -q 'grml-live.*main' "$SOURCES_LIST_FILE" || \
444    grep grml-stable "$SOURCES_LIST_FILE" | \
445         sed 's/grml-stable/grml-live/' >> "$SOURCES_LIST_FILE"
446 else
447    grep -q 'grml-live.*main' "$SOURCES_LIST_FILE" && \
448    sed 's/.*grml-live.*/# removed grml-live repository/' "$SOURCES_LIST_FILE" | sponge "$SOURCES_LIST_FILE"
449 fi
450
451 for file in "$LIVE_CONF" "$CONFIG" "$LOCAL_CONFIG" "$NFSROOT_CONF" ; do
452     if [ -n "$file" ] ; then
453        sed "s|^FAI_DEBOOTSTRAP=\"[a-z]* |FAI_DEBOOTSTRAP=\"$SUITE |" "$file" | sponge "$file"
454     fi
455 done
456
457 # validate whether the specified architecture class matches the
458 # architecture (option), otherwise installation of kernel will fail
459 if echo $CLASSES | grep -qi i386 ; then
460    if ! [[ "$ARCH" == "i386" ]] ; then
461       log    "Error: You specified the I386 class but are trying to build something else (AMD64?)."
462       eerror "Error: You specified the I386 class but are trying to build something else (AMD64?)."
463       eerror "Tip:   Either invoke grml-live with '-a i386' or adjust the architecture class. Exiting."
464       eend 1
465       bailout
466    fi
467 elif echo $CLASSES | grep -qi amd64 ; then
468    if ! [[ "$ARCH" == "amd64" ]] ; then
469       log    "Error: You specified the AMD64 class but are trying to build something else (I386?)."
470       eerror "Error: You specified the AMD64 class but are trying to build something else (I386?)."
471       eerror "Tip:   Either invoke grml-live with '-a amd64' or adjust the architecture class. Exiting."
472       eend 1
473       bailout
474    fi
475 fi
476
477 if grep -q -- 'FAI_DEBOOTSTRAP_OPTS.*--arch' "$NFSROOT_CONF" ; then
478    sed "s/--arch [a-z0-9]* /--arch $ARCH /" "$NFSROOT_CONF" | sponge "$NFSROOT_CONF"
479 else
480    sed "s|^FAI_DEBOOTSTRAP_OPTS=\"\(.*\)|FAI_DEBOOTSTRAP_OPTS=\"--arch $ARCH \1|" "$NFSROOT_CONF" | sponge "$NFSROOT_CONF"
481 fi
482 # }}}
483
484 # CHROOT_OUTPUT - execute FAI {{{
485 if [ -n "$BUILD_DIRTY" ]; then
486    log   "Skipping stage 'fai' as requested via option -B"
487    ewarn "Skipping stage 'fai' as requested via option -B" ; eend 0
488 else
489    [ -n "$CHROOT_OUTPUT" ] || CHROOT_OUTPUT="$OUTPUT/grml_chroot"
490
491    # provide inform fai about the ISO we build
492    [ -d "$CHROOT_OUTPUT/etc/" ] || mkdir -p "$CHROOT_OUTPUT/etc/"
493    echo '# This file has been generated by grml-live.' > "$CHROOT_OUTPUT/etc/grml_live_version"
494    [ -n "$GRML_LIVE_VERSION" ] && echo "GRML_LIVE_VERSION=$GRML_LIVE_VERSION" >> "$CHROOT_OUTPUT/etc/grml_live_version"
495    [ -n "$SUITE" ] && echo "SUITE=$SUITE" >> "$CHROOT_OUTPUT/etc/grml_live_version"
496
497    if [ -n "$UPDATE" -o -n "$BUILD_ONLY" ] ; then
498       FAI_ACTION=softupdate
499    else
500       FAI_ACTION=dirinstall
501    fi
502
503    if [ -n "$UPDATE" -o -n "$BUILD_ONLY" ] ; then
504       if ! [ -r "$CHROOT_OUTPUT/etc/debian_version" ] ; then
505          log    "Error: does not look like you have a working chroot. Updating/building not possible."
506          eerror "Error: does not look like you have a working chroot. Updating/building not possible. (Drop -u/-b option?)"
507          eend 1
508          bailout 20
509       fi
510    fi
511
512    if [ -d "$CHROOT_OUTPUT/bin" -a -z "$UPDATE" -a -z "$BUILD_ONLY" ] ; then
513       log   "Skipping stage 'fai dirinstall' as $CHROOT_OUTPUT exists already."
514       ewarn "Skipping stage 'fai dirinstall' as $CHROOT_OUTPUT exists already." ; eend 0
515    else
516       mkdir -p "$CHROOT_OUTPUT" || bailout 5 "Problem with creating $CHROOT_OUTPUT for FAI"
517
518       if [ -n "${MIRROR_DIRECTORY}" ] ; then
519          mkdir -p "${CHROOT_OUTPUT}/${MIRROR_DIRECTORY}"
520          mount --bind "${MIRROR_DIRECTORY}" "${CHROOT_OUTPUT}/${MIRROR_DIRECTORY}"
521       fi
522
523       log "Executed FAI command line:"
524       log "BUILD_ONLY=$BUILD_ONLY fai $VERBOSE -C $GRML_FAI_CONFIG -c$CLASSES -u $HOSTNAME $FAI_ACTION $CHROOT_OUTPUT $FAI_ARGS"
525       BUILD_ONLY="$BUILD_ONLY" fai $VERBOSE -C "$GRML_FAI_CONFIG" -c"$CLASSES" -u \
526       "$HOSTNAME" $FAI_ACTION "$CHROOT_OUTPUT" $FAI_ARGS | tee -a $LOGFILE
527       RC="$PIPESTATUS" # notice: bash-only
528
529       FORCE_ISO_REBUILD=true
530
531       if [ "$RC" != 0 ] ; then
532          log    "Error: critical error while executing fai [exit code ${RC}]. Exiting."
533          eerror "Error: critical error while executing fai [exit code ${RC}]. Exiting." ; eend 1
534          bailout 1
535       else
536          log "Setting /etc/grml_version to $GRML_NAME $VERSION Release Codename $RELEASENAME [$DATE]"
537          echo "$GRML_NAME $VERSION Release Codename $RELEASENAME [$DATE]" > $CHROOT_OUTPUT/etc/grml_version
538          chmod 644 $CHROOT_OUTPUT/etc/grml_version
539          einfo "Rebuilding initramfs"
540          # make sure new /etc/grml_version reaches the initramfs:
541          # chroot $CHROOT_OUTPUT update-initramfs -u -t => might break when using kernel-package :(
542          chroot $CHROOT_OUTPUT update-initramfs -u -k all
543          eend $?
544       fi
545
546       # Remove all FAI logs from chroot if class RELEASE is used:
547       if [ -f "$CHROOT_OUTPUT"/etc/grml_fai_release ] ; then
548          rm -rf "$CHROOT_OUTPUT"/var/log/fai/*
549       fi
550
551       # make sure we don't leave any mounts - FAI doesn't remove them always
552       umount $CHROOT_OUTPUT/proc 2>/dev/null || /bin/true
553       umount $CHROOT_OUTPUT/sys  2>/dev/null || /bin/true
554       umount $CHROOT_OUTPUT/dev/pts 2>/dev/null || /bin/true
555       umount $CHROOT_OUTPUT/dev 2>/dev/null || /bin/true
556
557       [ -n "$MIRROR_DIRECTORY" ] && umount "${CHROOT_OUTPUT}/${MIRROR_DIRECTORY}"
558
559       # notice: 'fai dirinstall' does not seem to exit appropriate, so:
560       ERROR=''
561       CHECKLOG=/var/log/fai/$HOSTNAME/last
562       if [ -r "$CHECKLOG/software.log" ] ; then
563          # 1 errors during executing of commands
564          grep 'dpkg: error processing' $CHECKLOG/software.log >> $LOGFILE && ERROR=1
565          grep 'E: Method http has died unexpectedly!' $CHECKLOG/software.log >> $LOGFILE && ERROR=2
566          grep 'ERROR: chroot' $CHECKLOG/software.log >> $LOGFILE && ERROR=3
567          grep 'E: Failed to fetch' $CHECKLOG/software.log >> $LOGFILE && ERROR=4
568          grep 'Unable to write mmap - msync (28 No space left on device)' $CHECKLOG/software.log >> $LOGFILE && ERROR=5
569       fi
570
571       if [ -r "$CHECKLOG/shell.log" ] ; then
572          grep 'FAILED with exit code' $CHECKLOG/shell.log >> $LOGFILE && ERROR=6
573       fi
574
575       if [ -n "$ERROR" ] ; then
576          log    "Error: there was a critical error [${ERROR}] during execution of stage 'fai dirinstall' [$(date)]"
577          eerror "Error: there was a critical error during execution of stage 'fai dirinstall'"
578          eerror "Note:  check out ${CHECKLOG}/ for details. [exit ${ERROR}]"
579          eend 1
580          bailout 1
581       else
582          log "Finished execution of stage 'fai dirinstall' [$(date)]"
583          einfo "Finished execution of stage 'fai dirinstall'"
584       fi
585
586       einfo "Find FAI build logs at $(readlink -f /var/log/fai/$HOSTNAME/last)"
587       log   "Find FAI build logs at $(readlink -f /var/log/fai/$HOSTNAME/last)"
588       eend 0
589    fi
590 fi # BUILD_DIRTY?
591 # }}}
592
593 # package validator {{{
594 CHECKLOG=/var/log/fai/$HOSTNAME/last
595 # package validator
596 if [ -r "$CHECKLOG/package_errors.log" ] && grep -q '[a-z]' "$CHECKLOG/package_errors.log" ; then
597
598    if [ -n "$EXIT_ON_MISSING_PACKAGES" -a -z "$BUILD_DIRTY" ] ; then
599       eerror "The following packages were requested for installation but could not be processed:"
600       cat $CHECKLOG/package_errors.log
601       eerror "... exiting as requested via \$EXIT_ON_MISSING_PACKAGES."
602       eend 1
603       bailout 13
604    else
605       ewarn "The following packages were requested for installation but could not be processed:"
606       cat $CHECKLOG/package_errors.log
607       eend 0
608    fi
609 fi
610 # }}}
611
612 # BUILD_OUTPUT - execute arch specific stuff and squashfs {{{
613 [ -n "$BUILD_OUTPUT" ] || BUILD_OUTPUT="$OUTPUT/grml_cd"
614 mkdir -p "$BUILD_OUTPUT" || bailout 6 "Problem with creating $BUILD_OUTPUT for stage ARCH"
615
616 # i386:
617 if [ "$ARCH" = i386 ] || [ "$ARCH" = amd64 ] ; then
618    if [ -d "$BUILD_OUTPUT"/boot/isolinux -a -z "$UPDATE" -a -z "$BUILD_ONLY" ] ; then
619       log   "Skipping stage 'boot' as $BUILD_OUTPUT/boot/isolinux exists already."
620       ewarn "Skipping stage 'boot' as $BUILD_OUTPUT/boot/isolinux exists already." ; eend 0
621    else
622       # booting stuff:
623       [ -d "$BUILD_OUTPUT"/boot/isolinux ] || mkdir -p "$BUILD_OUTPUT"/boot/isolinux
624       [ -d "$BUILD_OUTPUT"/boot/"${SHORT_NAME}" ] || mkdir -p "$BUILD_OUTPUT"/boot/"${SHORT_NAME}"
625
626       if [ -z "$NO_ADDONS" ] ; then
627          [ -d "$BUILD_OUTPUT"/boot/addons   ] || mkdir -p "$BUILD_OUTPUT"/boot/addons
628          if [ -r "$TEMPLATE_DIRECTORY"/boot/addons/memtest ] ; then
629             log "Installing $TEMPLATE_DIRECTORY/boot/addons/memtest"
630             cp "$TEMPLATE_DIRECTORY"/boot/addons/memtest "$BUILD_OUTPUT"/boot/addons/memtest
631          elif [ -r /boot/memtest86+.bin ] ; then
632             log "Installing /boot/memtest86+.bin"
633             cp /boot/memtest86+.bin "$BUILD_OUTPUT"/boot/addons/memtest
634          else
635             ewarn "No memtest binary found (either install package grml-live-addons or memtest86+), skipping."
636             log "No memtest binary found (either install package grml-live-addons or memtest86+), skipping."
637             eend 0
638          fi
639       fi
640
641       # if we don't have an initrd we a) can't boot and b) there was an error
642       # during build, so check for the file:
643       INITRD="$(ls $CHROOT_OUTPUT/boot/initrd* 2>/dev/null| grep -v '.bak$' | sort -r | head -1)"
644       if [ -n "$INITRD" ] ; then
645          cp $INITRD "$BUILD_OUTPUT"/boot/"${SHORT_NAME}"/initrd.gz
646          find $CHROOT_OUTPUT/boot/ -name initrd\*.bak -exec rm {} \;
647       else
648          log    "Error: No initrd found inside $CHROOT_OUTPUT/boot/ - Exiting"
649          eerror "Error: No initrd found inside $CHROOT_OUTPUT/boot/ - Exiting" ; eend 1
650          bailout 10
651       fi
652
653       KERNEL_IMAGE="$(ls $CHROOT_OUTPUT/boot/vmlinuz* 2>/dev/null | sort -r | head -1)"
654       if [ -n "$KERNEL_IMAGE" ] ; then
655          cp "$KERNEL_IMAGE" "$BUILD_OUTPUT"/boot/"${SHORT_NAME}"/linux26
656       else
657          log    "Error: No kernel found inside $CHROOT_OUTPUT/boot/ - Exiting"
658          eerror "Error: No kernel found inside $CHROOT_OUTPUT/boot/ - Exiting" ; eend 1
659          bailout 11
660       fi
661
662       [ -n "$TEMPLATE_DIRECTORY" ] || TEMPLATE_DIRECTORY='/usr/share/grml-live/templates'
663       if ! [ -d "${TEMPLATE_DIRECTORY}"/boot ] ; then
664          log    "Error: ${TEMPLATE_DIRECTORY}/boot does not exist. Exiting."
665          eerror "Error: ${TEMPLATE_DIRECTORY}/boot does not exist. Exiting." ; eend 1
666          bailout 8
667       fi
668
669       # *always* copy files to output directory so the variables
670       # get adjusted according to the build
671       cp ${TEMPLATE_DIRECTORY}/boot/isolinux/*  "$BUILD_OUTPUT"/boot/isolinux/
672
673       if [ -n "$NO_ADDONS" ] ; then
674          log   "Skipping installation of boot addons as requested via \$NO_ADDONS."
675          einfo "Skipping installation of boot addons as requested via \$NO_ADDONS."; eend 0
676       else
677          if ! [ -d "$TEMPLATE_DIRECTORY"/boot/addons ] ; then
678            log   "Boot addons not found, skipping therefore. (Consider installing package grml-live-addons)"
679            ewarn "Boot addons not found, skipping therefore. (Consider installing package grml-live-addons)" ; eend 0
680          else
681            # copy only files so we can handle bsd4grml on its own
682            for file in ${TEMPLATE_DIRECTORY}/boot/addons/* ; do
683              test -f $file && cp $file "$BUILD_OUTPUT"/boot/addons/
684            done
685
686            if [ -n "$NO_ADDONS_BSD4GRML" ] ; then
687               log   "Skipping installation of bsd4grml as requested via \$NO_ADDONS_BSD4GRML."
688               einfo "Skipping installation of bsd4grml as requested via \$NO_ADDONS_BSD4GRML."; eend 0
689            else
690               if [ -d "$TEMPLATE_DIRECTORY"/boot/addons/bsd4grml ] ; then
691                 cp -a ${TEMPLATE_DIRECTORY}/boot/addons/bsd4grml "$BUILD_OUTPUT"/boot/addons/
692               else
693                 log   "bsd4grml addon not found, skipping therefore."
694                 ewarn "bsd4grml addon not found, skipping therefore." ; eend 0
695               fi
696            fi
697
698          fi # no "$TEMPLATE_DIRECTORY"/boot/addons
699       fi # NO_ADDONS
700
701       if ! [ -d ${TEMPLATE_DIRECTORY}/boot/grub ] ; then
702          log   "grub templates do not exist, skipping therefore."
703          ewarn "grub templates do not exist, skipping therefore." ; eend 0
704       else
705          if ! [ -d "${BUILD_OUTPUT}/boot/grub" ] ; then
706             cp -a ${TEMPLATE_DIRECTORY}/boot/grub  "$BUILD_OUTPUT"/boot/
707          fi
708
709          # make sure we have recent template files available, otherwise updating
710          # the strings like $GRML_NAME and $VERSION might be out of date
711          cp ${TEMPLATE_DIRECTORY}/boot/grub/* "$BUILD_OUTPUT"/boot/grub/
712       fi
713
714       if ! [ -d "${TEMPLATE_DIRECTORY}"/GRML ] ; then
715          log    "Error: ${TEMPLATE_DIRECTORY}/GRML does not exist. Exiting."
716          eerror "Error: ${TEMPLATE_DIRECTORY}/GRML does not exist. Exiting." ; eend 1
717          bailout 9
718       fi
719
720       [ -d "$BUILD_OUTPUT"/GRML ] || mkdir "$BUILD_OUTPUT"/GRML
721       cp -a ${TEMPLATE_DIRECTORY}/GRML/* "$BUILD_OUTPUT"/GRML/
722
723       # adjust boot splash information:
724       RELEASE_INFO="$GRML_NAME $VERSION - Release Codename $RELEASENAME"
725       RELEASE_INFO="$(cut_string 68 "$RELEASE_INFO")"
726       RELEASE_INFO="$(extend_string_end 68 "$RELEASE_INFO")"
727
728       if [ -r "$BUILD_OUTPUT"/GRML/grml-version ] ; then
729          sed -i "s/%RELEASE_INFO%/$GRML_NAME $VERSION - $RELEASENAME/" "$BUILD_OUTPUT"/GRML/grml-version
730          sed -i "s/%DATE%/$DATE/"                                      "$BUILD_OUTPUT"/GRML/grml-version
731       fi
732
733       # make sure the squashfs filename is set accordingly:
734       SQUASHFS_NAME="$GRML_NAME.squashfs"
735
736       if [ -n "$NO_BOOTID" ] ; then
737          log   'Skipping bootid feature as requested via $NO_BOOTID.'
738          einfo 'Skipping bootid feature as requested via $NO_BOOTID.'
739       else
740          [ -n "$BOOTID" ] || BOOTID="$(echo ${GRML_NAME}${VERSION} | tr -d ',./;\- ')"
741          [ -d "$BUILD_OUTPUT"/conf ] || mkdir "$BUILD_OUTPUT"/conf
742          einfo "Generating /conf/bootid.txt with entry ${BOOTID}."
743          log   "Generating /conf/bootid.txt with entry ${BOOTID}."
744          echo "$BOOTID" > "$BUILD_OUTPUT"/conf/bootid.txt
745          eend $?
746       fi
747
748       # adjust all variables in the templates with the according distribution information
749       for file in "${BUILD_OUTPUT}"/boot/isolinux/*.cfg "${BUILD_OUTPUT}"/boot/isolinux/*.msg \
750                   "${BUILD_OUTPUT}"/boot/grub/* ; do
751         if [ -r "${file}" ] ; then
752           sed -i "s/%ARCH%/$ARCH/g"                    "${file}"
753           sed -i "s/%DATE%/$DATE/g"                    "${file}"
754           sed -i "s/%DISTRI_INFO%/$DISTRI_INFO/g"      "${file}"
755           sed -i "s/%DISTRI_NAME%/$DISTRI_NAME/g"      "${file}"
756           sed -i "s/%DISTRI_SPLASH%/$DISTRI_SPLASH/g"  "${file}"
757           sed -i "s/%GRML_NAME%/$GRML_NAME/g"          "${file}"
758           sed -i "s/%SQUASHFS_NAME%/$SQUASHFS_NAME/g"  "${file}"
759           sed -i "s/%RELEASE_INFO%/$RELEASE_INFO/g"    "${file}"
760           sed -i "s/%SHORT_NAME%/$SHORT_NAME/g"        "${file}"
761           sed -i "s/%VERSION%/$VERSION/g"              "${file}"
762
763           [ -n "$DEFAULT_BOOTOPTIONS" ] && sed -i "s/ boot=live/ boot=live $DEFAULT_BOOTOPTIONS/"  "${file}"
764
765           if [ -n "$NO_BOOTID" ] ; then
766              sed -i "s/ bootid=%BOOTID%//g" "${file}" # drop bootid bootoption
767           else
768              sed -i "s/%BOOTID%/$BOOTID/g" "${file}" # adjust bootid=... argument
769           fi
770         fi
771       done
772
773       # adjust bootsplash accordingly but make sure the string has the according lenght
774       SQUASHFS_NAME="$(cut_string 20 "$SQUASHFS_NAME")"
775       SQUASHFS_NAME="$(extend_string_end 20 "$SQUASHFS_NAME")"
776       for file in f4 f5 ; do
777          if [ -r "${BUILD_OUTPUT}/boot/isolinux/${file}" ] ; then
778             sed -i "s/%SQUASHFS_NAME%/$SQUASHFS_NAME/" "${BUILD_OUTPUT}/boot/isolinux/${file}"
779             sed -i "s/%SQUASHFS_NAME%/$SQUASHFS_NAME/" "${BUILD_OUTPUT}/boot/isolinux/${file}"
780          fi
781       done
782
783       # generate addon list
784       rm "${BUILD_OUTPUT}/${ADDONS_LIST_FILE}"
785       for name in "${BUILD_OUTPUT}"/boot/isolinux/addon_*.cfg ; do
786         include_name=$(basename "$name")
787         echo "include $include_name"  >> "${BUILD_OUTPUT}/${ADDONS_LIST_FILE}"
788       done
789
790       if ! [ -r "${BUILD_OUTPUT}/boot/isolinux/${DISTRI_NAME}.cfg" ] || [ "$DISTRI_NAME" = "grml" ] ; then
791          log "including grmlmain.cfg in ${BUILD_OUTPUT}/boot/isolinux/distri.cfg"
792          echo "include grmlmain.cfg"    >  "${BUILD_OUTPUT}/boot/isolinux/distri.cfg"
793          echo "include default.cfg"     >  "${BUILD_OUTPUT}/boot/isolinux/grmlmain.cfg"
794          echo "include menuoptions.cfg" >> "${BUILD_OUTPUT}/boot/isolinux/grmlmain.cfg"
795          echo "include grml.cfg"        >> "${BUILD_OUTPUT}/boot/isolinux/grmlmain.cfg"
796
797          for f in "${BUILD_OUTPUT}"/boot/isolinux/submenu*.cfg ; do
798            echo "include $(basename $f)"     >> "${BUILD_OUTPUT}/boot/isolinux/grmlmain.cfg"
799          done
800
801          echo "include options.cfg"     >> "${BUILD_OUTPUT}/boot/isolinux/grmlmain.cfg"
802          if [ ! -n "$NO_ADDONS" ] ; then
803            echo "include addons.cfg"    >> "${BUILD_OUTPUT}/boot/isolinux/grmlmain.cfg"
804          fi
805          echo "include isoprompt.cfg"   >> "${BUILD_OUTPUT}/boot/isolinux/grmlmain.cfg"
806          echo "include hd.cfg"          >> "${BUILD_OUTPUT}/boot/isolinux/grmlmain.cfg"
807          echo "include hidden.cfg"      >> "${BUILD_OUTPUT}/boot/isolinux/grmlmain.cfg"
808       else # assume we are building a custom distribution:
809          log "File ${BUILD_OUTPUT}/boot/isolinux/${DISTRI_NAME}.cfg found, using it."
810          einfo "File ${BUILD_OUTPUT}/boot/isolinux/${DISTRI_NAME}.cfg found, using it."
811          if grep -q "^include ${DISTRI_NAME}.cfg" "${BUILD_OUTPUT}/boot/isolinux/distri.cfg" ; then
812            log "include for ${DISTRI_NAME}.cfg already present, nothing to do."
813            eindent
814            einfo "include for ${DISTRI_NAME}.cfg already present, nothing to do."
815            eoutdent
816            eend $?
817         else
818            log "including ${DISTRI_NAME}.cfg in ${BUILD_OUTPUT}/boot/isolinux/distri.cfg"
819            echo "include ${DISTRI_NAME}.cfg" > "${BUILD_OUTPUT}/boot/isolinux/distri.cfg"
820            [ -n "$NO_ADDONS" ] || echo "include addons.cfg" >> "${BUILD_OUTPUT}/boot/isolinux/distri.cfg"
821          fi
822       fi
823
824       # use old style console based isolinux method only if requested:
825       if [[ "${ISOLINUX_METHOD}" == "console" ]] ; then
826          log 'Using console based isolinux method as requested via $ISOLINUX_METHOD.'
827          einfo 'Using console based isolinux method as requested via $ISOLINUX_METHOD.'
828          if grep -q '^include console.cfg' "${BUILD_OUTPUT}/boot/isolinux/distri.cfg" ; then
829            einfo "include for console.cfg already foud, nothing to do."
830            eend 0
831          else
832            log "including console.cfg in ${BUILD_OUTPUT}/boot/isolinux/isolinux.cfg"
833            einfo "including console.cfg in ${BUILD_OUTPUT}/boot/isolinux/isolinux.cfg"
834            echo "include console.cfg" >> "${BUILD_OUTPUT}/boot/isolinux/isolinux.cfg"
835            eend $?
836          fi
837       else
838          log 'Using graphical boot menu.'
839          if grep -q '^include vesamenu.cfg' "${BUILD_OUTPUT}/boot/isolinux/isolinux.cfg" ; then
840            log "include for vesamenu.cfg already foud, nothing to do."
841          else
842            log "including vesamenu.cfg in ${BUILD_OUTPUT}/boot/isolinux/isolinux.cfg"
843            echo "include vesamenu.cfg" >> "${BUILD_OUTPUT}/boot/isolinux/isolinux.cfg"
844          fi
845       fi
846
847       # jump back to grub from bsd4grml (/boot/grub/stage2):
848       GRUB_LEGACY=stage2
849
850       if [ -e "$BUILD_OUTPUT"/boot/addons/bsd4grml/boot.6 ]; then
851          if [ -e "$BUILD_OUTPUT"/boot/grub/core.img ]; then
852             GRUB_VERSION=2
853          else
854             GRUB_VERSION=1
855          fi
856
857          for file in "$BUILD_OUTPUT"/boot/addons/bsd4grml/boot.6 \
858                      "$BUILD_OUTPUT"/boot/addons/bsd4grml/boot.cfg \
859                      "$BUILD_OUTPUT"/boot/isolinux/*.cfg \
860                      "$BUILD_OUTPUT"/boot/grub/grub.cfg \
861                      "$BUILD_OUTPUT"/boot/grub/menu.lst ; do
862              if [ -e "$file" ] ; then
863                sed -i -e "s!%GRUB_VERSION%!$GRUB_VERSION!g" \
864                       -e "s!%GRUB_LEGACY%!$GRUB_LEGACY!g" "$file"
865              fi
866          done
867
868          sed -i "s/%RELEASE_INFO%/$GRML_NAME $VERSION - $RELEASENAME/" "$BUILD_OUTPUT"/boot/addons/bsd4grml/boot.6
869       fi
870
871       if [ -e "$BUILD_OUTPUT"/boot/grub/$GRUB_LEGACY ]; then
872          sed -i "s/%GRUB_LEGACY%/$GRUB_LEGACY/g" "$BUILD_OUTPUT"/boot/grub/menu.lst
873          sed -i "s/%GRUB_LEGACY%/$GRUB_LEGACY/g" "$BUILD_OUTPUT"/boot/grub/grub.cfg
874       elif [ -e "$BUILD_OUTPUT"/boot/grub/menu.lst -a -e "$BUILD_OUTPUT"/boot/grub/grub.cfg ] ; then
875          sed -i "/%GRUB_LEGACY%/d" "$BUILD_OUTPUT"/boot/grub/menu.lst
876          sed -i "/%GRUB_LEGACY%/d" "$BUILD_OUTPUT"/boot/grub/grub.cfg
877       fi
878
879       DPKG_LIST="/var/log/fai/$HOSTNAME/last/dpkg.list" # the dpkg --list output of the chroot
880       if ! [ -r "$DPKG_LIST" ] ; then
881          ewarn "$DPKG_LIST could not be read, ignoring to store package information on ISO therefore."
882       else
883          einfo "Storing package list information as /GRML/${GRML_NAME}-packages.txt on ISO."
884          cp "$DPKG_LIST" "${BUILD_OUTPUT}/GRML/${GRML_NAME}-packages.txt"
885          eend $?
886       fi
887
888       # autostart for Windows:
889       if [ -d "${TEMPLATE_DIRECTORY}/windows/autostart/" ] ; then
890          cp ${TEMPLATE_DIRECTORY}/windows/autostart/* "$BUILD_OUTPUT"/
891       fi
892
893       # windows-binaries:
894       if [ -n "$NO_WINDOWS_BINARIES" ] ; then
895          log   "Skipping download of windows binaries as requested via \$NO_WINDOWS_BINARIES."
896          einfo "Skipping download of windows binaries as requested via \$NO_WINDOWS_BINARIES."
897          eend 0
898       else
899          if [ -f "$BUILD_OUTPUT"/windows/putty.exe ] ; then
900             log   "Skipping stage 'WINDOWS_BINARIES' as $BUILD_OUTPUT/windows exists already."
901             ewarn "Skipping stage 'WINDOWS_BINARIES' as $BUILD_OUTPUT/windows exists already." ; eend 0
902          else
903             if ! [ -d "$BUILD_OUTPUT"/windows ] ; then
904                mkdir "$BUILD_OUTPUT"/windows
905                ( cd "$BUILD_OUTPUT"/windows
906                  for file in pageant plink pscp psftp putty puttygen ; do
907                     wget -O ${file}.exe ${WINDOWS_BINARIES}/${file}.exe
908                     md5sum ${file}.exe > ${file}.exe.md5
909                  done )
910             fi
911
912             log "Finished execution of stage 'WINDOWS_BINARIES' [$(date)]"
913             einfo "Finished execution of stage 'WINDOWS_BINARIES'" ; eend 0
914          fi
915       fi
916
917    FORCE_ISO_REBUILD=true
918    einfo "Finished execution of stage 'boot'" ; eend 0
919    fi
920 else
921    log    'Error: Unsupported ARCH, sorry. Want to support it? Contribute!'
922    eerror 'Error: Unsupported ARCH, sorry. Want to support it? Contribute!' ; eend 1
923    bailout
924 fi
925
926 # support installation of local files into the chroot/ISO
927 if [ -n "$CHROOT_INSTALL" ] ; then
928   if ! [ -d "$CHROOT_INSTALL" ] ; then
929      log "Configuration variable \$CHROOT_INSTALL is set but not a directory; ignoring"
930      ewarn "Configuration variable \$CHROOT_INSTALL is set but not a directory; ignoring"
931   else
932      log "Copying local files to chroot as requested via \$CHROOT_INSTALL"
933      einfo "Copying local files to chroot as requested via \$CHROOT_INSTALL"
934      rsync -avz --inplace "$CHROOT_INSTALL"/ "$CHROOT_OUTPUT/"
935      eend $?
936      einfo "Make sure to run squashfs stage, otherwise your local files won't be part of the ISO."
937      FORCE_ISO_REBUILD=true
938   fi
939 fi
940
941 if [ -f "$BUILD_OUTPUT"/live/${GRML_NAME}.squashfs -a -z "$UPDATE" -a -z "$BUILD_ONLY" -a -z "$BUILD_DIRTY" ] ; then
942    log   "Skipping stage 'squashfs' as $BUILD_OUTPUT/live exists already."
943    ewarn "Skipping stage 'squashfs' as $BUILD_OUTPUT/live exists already." ; eend 0
944 elif [ -n "$SKIP_MKSQUASHFS" ] ; then
945    log   "Skipping stage 'squashfs' as requested via option -q"
946    ewarn "Skipping stage 'squashfs' as requested via option -q" ; eend 0
947 else
948    [ -d "$BUILD_OUTPUT"/live ] || mkdir "$BUILD_OUTPUT"/live
949    # make sure we don't leave (even an empty) base.tgz:
950    [ -f "$CHROOT_OUTPUT/base.tgz" ] && rm -f "$CHROOT_OUTPUT/base.tgz"
951
952    # if unconfigured default to squashfs-tools' mksquashfs binary
953    if [ -z "$SQUASHFS_BINARY" ] ; then
954       SQUASHFS_BINARY='mksquashfs'
955    fi
956
957    if which "$SQUASHFS_BINARY" >/dev/null 2>&1 ; then
958       log    "Using mksquashfs binary ${SQUASHFS_BINARY}"
959       einfo  "Using mksquashfs binary ${SQUASHFS_BINARY}" ; eend 0
960    else
961       log    "Error: mksquashfs binary ($SQUASHFS_BINARY) not found. Exiting."
962       eerror "Error: mksquashfs binary ($SQUASHFS_BINARY) not found. Exiting." ; eend 1
963       bailout
964    fi
965
966    # use sane defaults if $SQUASHFS_OPTIONS isn't set
967    if [ -z "$SQUASHFS_OPTIONS" ] ; then
968      # use blocksize 256k as this gives best result with regards to time + compression
969      SQUASHFS_OPTIONS="-b 256k"
970
971      # set lzma/xz compression by default, unless -z option has been specified on command line
972      if [ -z "$SQUASHFS_ZLIB" ] ; then
973         SQUASHFS_OPTIONS="$SQUASHFS_OPTIONS -comp xz"
974      else
975         SQUASHFS_OPTIONS="$SQUASHFS_OPTIONS -comp gzip"
976      fi
977    fi
978
979    # support exclusion of files via exclude-file:
980    if [ -n "$SQUASHFS_EXCLUDES_FILE" -a "$SQUASHFS_EXCLUDES_FILE" ] ; then
981       SQUASHFS_OPTIONS="$SQUASHFS_OPTIONS -ef $SQUASHFS_EXCLUDES_FILE"
982    fi
983
984    # get rid of unnecessary files when building grml-small for final release:
985    if echo "$CLASSES" | grep -q GRML_SMALL ; then
986       SQUASHFS_OPTIONS="$SQUASHFS_OPTIONS -e initrd.img* vmlinuz*"
987    fi
988
989    # log stuff
990    SQUASHFS_STDERR="$(mktemp -t grml-live.XXXXXX)"
991
992    # informational stuff
993    [ -n "$SQUASHFS_OPTIONS" ]  && SQUASHFS_INFO_MSG="$SQUASHFS_OPTIONS"
994    [ -n "$SQUASHFS_INFO_MSG" ] && SQUASHFS_INFO_MSG="using options: $SQUASHFS_INFO_MSG"
995    einfo "Squashfs build information: running binary $SQUASHFS_BINARY $SQUASHFS_INFO_MSG"
996
997    log "$SQUASHFS_BINARY $CHROOT_OUTPUT/* $BUILD_OUTPUT/live/${GRML_NAME}.squashfs -noappend $SQUASHFS_OPTIONS"
998
999    if $SQUASHFS_BINARY $CHROOT_OUTPUT/* $BUILD_OUTPUT/live/"${GRML_NAME}".squashfs \
1000       -noappend $SQUASHFS_OPTIONS 2>"${SQUASHFS_STDERR}" ; then
1001       echo "${GRML_NAME}.squashfs" > $BUILD_OUTPUT/live/filesystem.module
1002       log "Finished execution of stage 'squashfs' [$(date)]"
1003       einfo "Finished execution of stage 'squashfs'" ; eend 0
1004    else
1005       log    "Error: there was a critical error executing stage 'squashfs' [$(date)]:"
1006       log    "$(cat $SQUASHFS_STDERR)"
1007       eerror "Error: there was a critical error executing stage 'squashfs':"
1008       cat    "${SQUASHFS_STDERR}"
1009       eend 1
1010       bailout
1011    fi
1012
1013    FORCE_ISO_REBUILD=true
1014 fi
1015
1016 # create md5sum file:
1017 ( cd $BUILD_OUTPUT/GRML &&
1018 find .. -type f -not -name md5sums -not -name isolinux.bin -exec md5sum {} \; > md5sums )
1019 # }}}
1020
1021 # ISO_OUTPUT - mkisofs {{{
1022 [ -n "$ISO_OUTPUT" ] || ISO_OUTPUT="$OUTPUT/grml_isos"
1023 [ -n "$ISO_NAME" ] || ISO_NAME="${GRML_NAME}_${VERSION}.iso"
1024
1025 if [ "$BOOT_METHOD" = "isolinux" ] ; then
1026    BOOT_ARGS="-no-emul-boot -boot-load-size 4 -boot-info-table -b boot/isolinux/isolinux.bin -c boot/isolinux/boot.cat"
1027 elif [ "$BOOT_METHOD" = "grub" ] ; then
1028    BOOT_ARGS="-no-emul-boot -boot-load-size 4 -boot-info-table -b boot/grub/stage2"
1029 elif [ "$BOOT_METHOD" = "grub2" ] ; then
1030    BOOT_ARGS="-no-emul-boot -boot-load-size 4 -b boot/grub/toriboot.bin"
1031 fi
1032
1033 # Just until http://bts.grml.org/grml/issue945 has been resolved.
1034 # HYBRID_METHOD defaults to manifold, so make sure the default works OOTB.
1035 if [[ $BOOT_METHOD != isolinux && ($HYBRID_METHOD = isohybrid || $HYBRID_METHOD = manifold) ]]; then
1036   log   "Setting HYBRID_METHOD to grub2 as hybrid mode does not work with isohybrid yet."
1037   ewarn "Setting HYBRID_METHOD to grub2 as hybrid mode does not work with isohybrid yet."
1038   HYBRID_METHOD='grub2'
1039   eend 0
1040 fi
1041
1042 if [ -f "${ISO_OUTPUT}/${ISO_NAME}" -a -z "$UPDATE" -a -z "$BUILD_ONLY" -a -z "$BUILD_DIRTY" -a "$FORCE_ISO_REBUILD" = "false" ]  ; then
1043    log   "Skipping stage 'iso build' as $ISO_OUTPUT/${ISO_NAME} exists already."
1044    ewarn "Skipping stage 'iso build' as $ISO_OUTPUT/${ISO_NAME} exists already." ; eend 0
1045 elif [ -n "$SKIP_MKISOFS" ] ; then
1046    log   "Skipping stage 'iso build' as requested via option -n"
1047    ewarn "Skipping stage 'iso build' as requested via option -n" ; eend 0
1048 else
1049    mkdir -p "$ISO_OUTPUT" || bailout 6 "Problem with creating $ISO_OUTPUT for stage 'iso build'"
1050
1051    if $FORCE_ISO_REBUILD && ! [ -f "${ISO_OUTPUT}/${ISO_NAME}" ] ; then
1052       log   "Forcing rebuild of ISO because files on ISO have been modified."
1053       einfo "Forcing rebuild of ISO because files on ISO have been modified."
1054    fi
1055
1056    # support mkisofs as well as genisoimage
1057    if which mkisofs >/dev/null 2>&1; then
1058       MKISOFS='mkisofs'
1059    elif which genisoimage >/dev/null 2>&1; then
1060       MKISOFS='genisoimage'
1061    else
1062       log    "Error: neither mkisofs nor genisoimage available - can not create ISO."
1063       eerror "Error: neither mkisofs nor genisoimage available - can not create ISO." ; eend 1
1064       bailout
1065    fi
1066
1067    CURRENT_DIR=$(pwd)
1068    if cd "$BUILD_OUTPUT" ; then
1069       if [ "$BOOT_METHOD" = "grub2" ]; then
1070          # make a 2048-byte bootsector for El Torito
1071          dd if=/dev/zero of=boot/grub/toriboot.bin bs=512 count=4 2>/dev/null
1072          # those are in 2048-byte sectors, so 1 16 matches 4 63 below
1073          echo 1 16 | mksh /usr/share/grml-live/scripts/bootgrub.mksh -B 11 | \
1074             dd of=boot/grub/toriboot.bin conv=notrunc 2>/dev/null
1075       fi
1076       log "$MKISOFS -V '${GRML_NAME} ${VERSION}' -publisher 'grml-live | grml.org' -l -r -J $BOOT_ARGS -o ${ISO_OUTPUT}/${ISO_NAME} ."
1077       "$MKISOFS" -V "${GRML_NAME} ${VERSION}" -publisher 'grml-live | grml.org' \
1078               -l -r -J $BOOT_ARGS -no-pad \
1079               -o "${ISO_OUTPUT}/${ISO_NAME}" . ; RC=$?
1080       # both of these need core.img there, so it’s easier to write it here
1081       if [ "$BOOT_METHOD" = "grub2" ] || [ "$HYBRID_METHOD" = "grub2" ]; then
1082          # must be <= 30720 bytes
1083          dd if=boot/grub/core.img of="${ISO_OUTPUT}/${ISO_NAME}" \
1084            conv=notrunc bs=512 seek=4 2>/dev/null
1085       fi
1086
1087       # pad the output ISO to multiples of 256 KiB for partition table support
1088       siz=$($getfilesize "${ISO_OUTPUT}/${ISO_NAME}")
1089       cyls=$((siz / 512 / 32 / 16 + 1))   # C=$cyls H=16 S=32
1090       siz=$((cyls * 16 * 32 * 512))   # size after padding
1091       dd if=/dev/zero bs=1 count=1 seek=$((siz - 1)) \
1092          of="${ISO_OUTPUT}/${ISO_NAME}" 2>/dev/null
1093
1094       # support disabling hybrid ISO image
1095       if [ "$HYBRID_METHOD" = "disable" ] ; then\
1096          log   "Skipping creation of hybrid ISO file as requested via HYBRID_METHOD=disable"
1097          einfo "Skipping creation of hybrid ISO file as requested via HYBRID_METHOD=disable"
1098          eend 0
1099       # use isohybrid only on request
1100       elif [ "$HYBRID_METHOD" = "isohybrid" ] ; then
1101          if ! which isohybrid >/dev/null 2>&1 ; then
1102            bailout 12 "isohybrid binary not found - please install syslinux/syslinux-common"
1103          else
1104            log "Creating hybrid ISO file with isohybrid method"
1105            einfo "Creating hybrid ISO file with isohybrid method"
1106            # Notes for consideration:
1107            # "-entry 4 -type 1c"
1108            # * using 4 as the partition number is supposed to help with BIOSes
1109            #   that only support USB-Zip boot
1110            # * using 1c (i.e. hidden FAT32 LBA), instead of the default 0x17
1111            #   (hidden NTFS, IIRC), as the partition type is sometimes needed
1112            #   to get the BIOS even look at the partition created by isohybrid
1113            isohybrid "${ISO_OUTPUT}/${ISO_NAME}"
1114            eend $?
1115          fi
1116       # by default use our manifold boot method:
1117       else
1118          # isoinfo is part of both mkisofs and genisoimage so we're good
1119          bootoff=$(isoinfo -l -i "${ISO_OUTPUT}/${ISO_NAME}" | \
1120            sed -n '/^.*\[ *\([0-9]*\)[] ].* ISOLINUX.BIN;1 *$/s//\1/p')
1121          if ! [ -r boot/grub/core.img ] ; then
1122            ewarn "boot/grub/core.img not found, not creating manifold boot ISO file"
1123          elif [ "${bootoff:-0}" -lt 1 ] ; then
1124            ewarn "isolinux.bin not found on the ISO file, disabling manifold boot"
1125          else
1126            log "Creating hybrid ISO file with manifold method"
1127            einfo "Creating hybrid ISO file with manifold method"
1128            if [ "$HYBRID_METHOD" = "grub2" ] ; then
1129                # 512 bytes: MBR, partition table, load GRUB 2
1130                echo 4 63 | mksh /usr/share/grml-live/scripts/bootgrub.mksh -A -M 4:0x96 -g $cyls:16:32
1131            else
1132               # read only one but 2048-byte sized (scale: << 2) sector
1133               echo $bootoff $bootoff | \
1134                  mksh /usr/share/grml-live/scripts/bootilnx.mksh -A -M 4:0x96 -g $cyls:16:32 -S 2
1135            fi | dd of="${ISO_OUTPUT}/${ISO_NAME}" conv=notrunc 2>/dev/null
1136            eend $?
1137          fi
1138       fi
1139
1140       # generate md5sum and sha1sum of ISO if we are using class 'RELEASE':
1141       case $CLASSES in *RELEASE*)
1142          [ "$RC" = 0 ] && \
1143          (
1144            if cd $ISO_OUTPUT ; then
1145              md5sum ${ISO_NAME} > ${ISO_NAME}.md5 && \
1146              touch -r ${ISO_NAME} ${ISO_NAME}.md5
1147              sha1sum ${ISO_NAME} > ${ISO_NAME}.sha1 && \
1148              touch -r ${ISO_NAME} ${ISO_NAME}.sha1
1149            fi
1150          )
1151          ;;
1152       esac
1153
1154       cd "$CURRENT_DIR"
1155    fi
1156
1157    if [ "$RC" = 0 ] ; then
1158       log   "Finished execution of stage 'iso build' [$(date)]"
1159       einfo "Finished execution of stage 'iso build'" ; eend 0
1160    else
1161       log    "Error: there was a critical error ($RC) executing stage 'iso build' [$(date)]"
1162       eerror "Error: there was a critical error executing stage 'iso build'" ; eend 1
1163       bailout $RC
1164    fi
1165 fi
1166 # }}}
1167
1168 # log build information to database if grml-live-db is installed and enabled {{{
1169 dpkg_to_db() {
1170 if [ -d /usr/share/grml-live-db ] ; then
1171
1172   # safe defaults
1173   DPKG_LIST="/var/log/fai/$HOSTNAME/last/dpkg.list" # the dpkg --list output of the chroot:
1174   [ -n "$DPKG_DATABASE" ]  || DPKG_DATABASE=/var/log/grml-live.db
1175   [ -n "$DPKG_DBSCRIPT" ]  || DPKG_DBSCRIPT=/usr/share/grml-live-db/scripts/dpkg-to-db
1176   [ -n "$DPKG_DBOPTIONS" ] || DPKG_DBOPTIONS="--database $DPKG_DATABASE --logfile $LOGFILE --flavour $GRML_NAME --dpkg $DPKG_LIST"
1177
1178   if ! [ -x "$DPKG_DBSCRIPT" ] ; then
1179     log "Error: $DPKG_DBSCRIPT is not executable, can not log dpkg information."
1180     eerror "Error: $DPKG_DBSCRIPT is not executable, can not log dpkg information." ; eend 1
1181     bailout 14
1182   fi
1183
1184   # disable by default for now, not sure whether really everyone is using a local db file
1185   #if ! touch "$DPKG_DATABASE" ; then
1186   #  eerror "Error: can not write to ${DPKG_DATABASE}, can not log dpkg information." ; eend 1
1187   #  bailout 14
1188   #fi
1189
1190   if ! [ -r "$DPKG_LIST" ] ; then
1191      log   "Warning: can not read $DPKG_LIST - can not provide information to $DPKG_DBSCRIPT (dirty build?)"
1192      ewarn "Warning: can not read $DPKG_LIST - can not provide information to $DPKG_DBSCRIPT (dirty build?)" ; eend 0
1193   else
1194      einfo "Logging $DPKG_LIST to database $DPKG_DATABASE"
1195      log "Logging $DPKG_LIST to database $DPKG_DATABASE"
1196      log "Executing $DPKG_DBSCRIPT $DPKG_DBOPTIONS"
1197      eindent
1198
1199      if DB_INFO=$("$DPKG_DBSCRIPT" $DPKG_DBOPTIONS 2>&1) ; then
1200        einfo "$DB_INFO"
1201        eend 0
1202      else
1203        eerror "$DB_INFO"
1204        eend 1
1205      fi
1206
1207      eoutdent
1208   fi
1209
1210 fi
1211 }
1212 # }}}
1213
1214 # finalize {{{
1215 [ -n "$start_seconds" ] && SECONDS="$[$(cut -d . -f 1 /proc/uptime)-$start_seconds]" || SECONDS="unknown"
1216 log "Successfully finished execution of $PN [$(date) - running ${SECONDS} seconds]"
1217
1218 dpkg_to_db # make sure we catch the last log line as well, therefore execute between log + einfo
1219
1220 einfo "Successfully finished execution of $PN [$(date) - running ${SECONDS} seconds]" ; eend 0
1221 bailout 0
1222 # }}}
1223
1224 ## END OF FILE #################################################################
1225 # vim:foldmethod=marker ts=2 ft=sh ai expandtab tw=80 sw=3