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