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