fix SQUASHFS_EXCLUDES_FILE default not being set
[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 ADDONS_LIST_FILE='/boot/isolinux/addons_list.cfg'
31 # }}}
32
33 # usage information {{{
34 usage()
35 {
36   echo "
37 $PN - build process script for generating a (grml based) Linux Live-ISO
38
39 Usage: $PN [options, see as follows]
40
41    -a <architecture>       architecture; available values: i386 and amd64
42    -b                      build the ISO without updating the chroot via FAI
43    -B                      build the ISO without touching the chroot (skips cleanup)
44    -c <classe[s]>          classes to be used for building the ISO via FAI
45    -C <configfile>         configuration file for grml-live
46    -d <date>               use specified date instead of build time as date of release
47    -D <configdir>          use specified configuration directory instead of /etc/grml/fai
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: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     D) GRML_FAI_CONFIG="$OPTARG" ;;
240     g) GRML_NAME="$OPTARG" ;;
241     i) ISO_NAME="$OPTARG" ;;
242     I) CHROOT_INSTALL="$OPTARG" ;;
243     n) SKIP_MKISOFS=1 ;;
244     N) BOOTSTRAP_ONLY=1; SKIP_MKISOFS=1; SKIP_MKSQUASHFS=1 ;;
245     o) OUTPUT="$OPTARG" ;;
246     q) SKIP_MKSQUASHFS=1 ;;
247     r) RELEASENAME="$OPTARG" ;;
248     s) SUITE="$OPTARG" ;;
249     t) TEMPLATE_DIRECTORY="$OPTARG";;
250     v) VERSION="$OPTARG" ;;
251     F) FORCE=1 ;;
252     u) UPDATE=1 ;;
253     V) VERBOSE="-v" ;;
254     z) SQUASHFS_ZLIB=1 ;;
255     ?) echo "invalid option -$OPTARG" >&2; bailout 1 ;;
256   esac
257 done
258 shift $(($OPTIND - 1))  # set ARGV to the first not parsed commandline parameter
259 # }}}
260
261 # assume sane defaults (if not set already) {{{
262 [ -n "$ARCH" ]                    || ARCH="$(dpkg --print-architecture)"
263 [ -n "$BOOT_METHOD" ]             || BOOT_METHOD='isolinux'
264 [ -n "$CLASSES" ]                 || CLASSES="GRMLBASE,GRML_MEDIUM,I386"
265 [ -n "$DATE" ]                    || DATE="$(date +%Y-%m-%d)"
266 [ -n "$DISTRI_INFO" ]             || DISTRI_INFO='Grml - Live Linux for system administrators   '
267 [ -n "$DISTRI_NAME" ]             || DISTRI_NAME="grml"
268 [ -n "$DISTRI_SPLASH" ]           || DISTRI_SPLASH='grml.png'
269 [ -n "$FORCE_ISO_REBUILD" ]       || FORCE_ISO_REBUILD="false"
270 [ -n "$GRML_FAI_CONFIG" ]         || GRML_FAI_CONFIG='/etc/grml/fai'
271 [ -n "$GRML_NAME" ]               || GRML_NAME='grml'
272 [ -n "$HOSTNAME" ]                || HOSTNAME='grml'
273 [ -n "$HYBRID_METHOD" ]           || HYBRID_METHOD='manifold'
274 [ -n "$NFSROOT_CONF" ]            || NFSROOT_CONF="${GRML_FAI_CONFIG}/make-fai-nfsroot.conf"
275 [ -n "$RELEASENAME" ]             || RELEASENAME='grml-live rocks'
276 [ -n "$SOURCES_LIST_INPUT" ]      || SOURCES_LIST_INPUT="${GRML_FAI_CONFIG}/apt/sources.list"
277 [ -n "$SOURCES_LIST_OUTPUT" ]     || SOURCES_LIST_OUTPUT="${GRML_FAI_CONFIG}/config/files/etc/apt/sources.list/GRMLBASE"
278 [ -n "$SQUASHFS_EXCLUDES_FILE" ]  || SQUASHFS_EXCLUDES_FILE="${GRML_FAI_CONFIG}/config/grml/squashfs-excludes"
279 [ -n "$SUITE" ]                   || SUITE='squeeze'
280 [ -n "$TEMPLATE_DIRECTORY" ]      || TEMPLATE_DIRECTORY='/usr/share/grml-live/templates'
281 [ -n "$USERNAME" ]                || USERNAME='grml'
282 [ -n "$VERSION" ]                 || VERSION='0.0.1'
283
284 # output specific stuff, depends on $OUTPUT (iff not set):
285 [ -n "$OUTPUT" ]           || OUTPUT='/grml/grml-live'
286 [ -n "$BUILD_OUTPUT" ]     || BUILD_OUTPUT="$OUTPUT/grml_cd"
287 [ -n "$CHROOT_OUTPUT" ]    || CHROOT_OUTPUT="$OUTPUT/grml_chroot"
288 [ -n "$ISO_OUTPUT" ]       || ISO_OUTPUT="$OUTPUT/grml_isos"
289 # }}}
290
291 # some misc checks before executing FAI {{{
292 [ -n "$CLASSES" ] || bailout 1 "Error: \$CLASSES unset, please set it in $LIVE_CONF or
293 specify it on the command line using the -c option."
294 [ -n "$OUTPUT" ] || bailout 1 "Error: \$OUTPUT unset, please set it in $LIVE_CONF or
295 specify it on the command line using the -o option."
296
297 # trim characters that are known to cause problems inside $GRML_NAME;
298 # for example isolinux does not like '-' inside the directory name
299 [ -n "$GRML_NAME" ] && export SHORT_NAME="$(echo $GRML_NAME | tr -d ',./;\- ')"
300
301 # export variables to have them available in fai scripts:
302 [ -n "$GRML_NAME" ]   && export GRML_NAME="$GRML_NAME"
303 [ -n "$RELEASENAME" ] && export RELEASENAME="$RELEASENAME"
304 # }}}
305
306 # ZERO_LOGFILE - check for backwards compatibility reasons {{{
307 # this was default behaviour until grml-live 0.9.34:
308 if [ -n "$ZERO_LOGFILE" ] ; then
309    PRESERVE_LOGFILE='' # make sure it's cleaned then
310    ewarn "Please consider disabling the \$ZERO_LOGFILE option as grml-live clears..."
311    ewarn "... the logfile $LOGFILE by default (unless \$PRESERVE_LOGFILE is set) nowadays."
312    eend 0
313 fi
314 # }}}
315
316 # ask user whether the setup is ok {{{
317 if [ -z "$FORCE" ] ; then
318    echo
319    echo "${PN} [${GRML_LIVE_VERSION}]: check your configuration (or use -F to force execution):"
320    echo
321    echo "  FAI classes:       $CLASSES"
322    [ -r "$LOCAL_CONFIG" ]        && echo "  Local config:      /etc/grml/grml-live.local"
323    [ -n "$CONFIG" ]              && echo "  Configuration:     $CONFIG"
324    [ -n "$GRML_FAI_CONFIG" ]     && echo "  Config directory:  $GRML_FAI_CONFIG"
325    echo "  main directory:    $OUTPUT"
326    [ -n "$CHROOT_OUTPUT" ]       && echo "  Chroot target:     $CHROOT_OUTPUT"
327    [ -n "$BUILD_OUTPUT" ]        && echo "  Build target:      $BUILD_OUTPUT"
328    [ -n "$ISO_OUTPUT" ]          && echo "  ISO target:        $ISO_OUTPUT"
329    [ -n "$GRML_NAME" ]           && echo "  Grml name:         $GRML_NAME"
330    [ -n "$RELEASENAME" ]         && echo "  Release name:      $RELEASENAME"
331    [ -n "$DATE" ]                && echo "  Build date:        $DATE"
332    [ -n "$VERSION" ]             && echo "  Grml version:      $VERSION"
333    [ -n "$SUITE" ]               && echo "  Debian suite:      $SUITE"
334    [ -n "$ARCH" ]                && echo "  Architecture:      $ARCH"
335    [ -n "$BOOT_METHOD" ]         && echo "  Boot method:       $BOOT_METHOD"
336    [ -n "$HYBRID_METHOD" ]       && echo "  Hybrid method:     $HYBRID_METHOD"
337    [ -n "$TEMPLATE_DIRECTORY" ]  && echo "  Template files:    $TEMPLATE_DIRECTORY"
338    [ -n "$CHROOT_INSTALL" ]      && echo "  Install files from directory to chroot:  $CHROOT_INSTALL"
339    [ -n "$BOOTID" ]              && echo "  Boot identifier:   $BOOTID"
340    [ -n "$NO_BOOTID" ]           && echo "  Skipping bootid feature."
341    [ -n "$DEFAULT_BOOTOPTIONS" ] && echo "  Adding default bootoptions: \"$DEFAULT_BOOTOPTIONS\""
342    [ -n "$FAI_ARGS" ]            && echo "  Additional arguments for FAI: $FAI_ARGS"
343    [ -n "$LOGFILE" ]             && echo "  Logging to file:   $LOGFILE"
344    [ -n "$SQUASHFS_ZLIB" ]       && echo "  Using ZLIB (instead of LZMA/XZ) compression."
345    [ -n "$SQUASHFS_OPTIONS" ]    && echo "  Using SQUASHFS_OPTIONS ${SQUASHFS_OPTIONS}"
346    [ -n "$VERBOSE" ]             && echo "  Using VERBOSE mode."
347    [ -n "$UPDATE" ]              && echo "  Executing UPDATE instead of fresh installation."
348    if [ -n "$BOOTSTRAP_ONLY" ] ; then
349      echo "  Bootstrapping only and not building (files for) ISO."
350    else
351      [ -n "$SKIP_MKSQUASHFS" ]     && echo "  Skipping creation of SQUASHFS file."
352      [ -n "$SKIP_MKISOFS" ]        && echo "  Skipping creation of ISO file."
353      [ -n "$BUILD_ONLY" ]          && echo "  Executing BUILD_ONLY instead of fresh installation or UPDATE."
354      [ -n "$BUILD_DIRTY" ]         && echo "  Executing BUILD_DIRTY to leave chroot untouched."
355    fi
356    echo
357    echo -n "Is this ok for you? [y/N] "
358    read a
359    if ! [ "$a" = 'y' -o "$a" = 'Y' ] ; then
360       bailout 1 "Exiting as requested."
361    fi
362    echo
363 fi
364 # }}}
365
366 # clean/zero/remove logfiles {{{
367
368 if [ -n "$PRESERVE_LOGFILE" ] ; then
369    echo "Preserving logfile $LOGFILE as requested via \$PRESERVE_LOGFILE"
370 else
371    # make sure it is empty (as it is e.g. appended to grml-live-db)
372    echo -n > $LOGFILE
373 fi
374
375 if [ -n "$ZERO_FAI_LOGFILE" ] ; then
376    if [ -d /var/log/fai/"$HOSTNAME" ] ; then
377       rm -rf /var/log/fai/"$HOSTNAME"/"$(readlink /var/log/fai/"$HOSTNAME"/last)"
378       rm -rf /var/log/fai/"$HOSTNAME"/"$(readlink /var/log/fai/"$HOSTNAME"/last-dirinstall)"
379       rm -rf /var/log/fai/"$HOSTNAME"/"$(readlink /var/log/fai/"$HOSTNAME"/last-softupdate)"
380       rm -f /var/log/fai/"$HOSTNAME"/last \
381             /var/log/fai/"$HOSTNAME"/last-dirinstall \
382             /var/log/fai/"$HOSTNAME"/last-softupdate
383    fi
384 fi
385 # }}}
386
387 # source config and startup {{{
388 if [ -n "$CONFIG" ] ; then
389    if ! [ -f "$CONFIG" ] ; then
390       log    "Error: $CONFIG could not be read. Exiting. [$(date)]"
391       eerror "Error: $CONFIG could not be read. Exiting." ; eend 1
392       bailout 1
393    else
394       log "Sourcing $CONFIG"
395       . $CONFIG
396    fi
397 fi
398
399 start_seconds=$(cut -d . -f 1 /proc/uptime)
400 log "------------------------------------------------------------------------------"
401 log "Starting grml-live [${GRML_LIVE_VERSION}] run on $(date)"
402 log "Executed grml-live command line:"
403 log "$CMDLINE"
404
405 einfo "Logging actions to logfile $LOGFILE"
406 # }}}
407
408 # on-the-fly configuration {{{
409 mkdir -p "$(dirname $SOURCES_LIST_OUTPUT)" # might not be present in -D config space
410
411 cat > "$SOURCES_LIST_OUTPUT" << EOF
412 # NOTE: This file is *NOT* meant for manual customisation! This file is
413 # modified by grml-live and any changes will be overridden.
414 # You might consider using GRML_LIVE_SOURCES in /etc/grml/grml-live.conf*
415 # or FAI's fcopy command with ${GRML_FAI_CONFIG}/config/files instead!
416 EOF
417
418 if [ -n "$MIRROR_DIRECTORY" ] ; then
419   if ! [ -d "$MIRROR_DIRECTORY/debian" ] ; then
420     log    "Error: $MIRROR_DIRECTORY/debian does not seem to exist. Exiting. [$(date)]"
421     eerror "Error: $MIRROR_DIRECTORY/debian does not seem to exist. Exiting." ; eend 1
422     bailout 1
423   fi
424   echo "$MIRROR_SOURCES" >> "$SOURCES_LIST_OUTPUT"
425 fi
426
427 if [ -n "$GRML_LIVE_SOURCES" ] ; then
428   echo "$GRML_LIVE_SOURCES" >> "$SOURCES_LIST_OUTPUT"
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_INPUT" | sponge "$SOURCES_LIST_OUTPUT"
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_INPUT" ; then
462    grep -q 'grml-live.*main' "$SOURCES_LIST_OUTPUT" || \
463    grep grml-stable "$SOURCES_LIST_OUTPUT" | \
464         sed 's/grml-stable/grml-live/' >> "$SOURCES_LIST_OUTPUT"
465 else
466    grep -q 'grml-live.*main' "$SOURCES_LIST_INPUT" && \
467    sed 's/.*grml-live.*/# removed grml-live repository/' "$SOURCES_LIST_INPUT" | sponge "$SOURCES_LIST_OUTPUT"
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 -s file:///$GRML_FAI_CONFIG/config -c$CLASSES -u $HOSTNAME $FAI_ACTION $CHROOT_OUTPUT $FAI_ARGS"
548       BUILD_ONLY="$BUILD_ONLY" BOOTSTRAP_ONLY="$BOOTSTRAP_ONLY" fai $VERBOSE \
549                   -C "$GRML_FAI_CONFIG" -s "file:///$GRML_FAI_CONFIG/config" -c"$CLASSES" \
550                   -u "$HOSTNAME" "$FAI_ACTION" "$CHROOT_OUTPUT" $FAI_ARGS | tee -a $LOGFILE
551       RC="$PIPESTATUS" # notice: bash-only
552
553       rm -f "$CHROOT_OUTPUT/etc/dpkg/dpkg.cfg.d/unsafe-io"
554
555       FORCE_ISO_REBUILD=true
556
557       if [ "$RC" != 0 ] ; then
558          log    "Error: critical error while executing fai [exit code ${RC}]. Exiting."
559          eerror "Error: critical error while executing fai [exit code ${RC}]. Exiting." ; eend 1
560          bailout 1
561       else
562          einfo "Setting /etc/grml_version to $GRML_NAME $VERSION Release Codename $RELEASENAME [$DATE]"
563          log   "Setting /etc/grml_version to $GRML_NAME $VERSION Release Codename $RELEASENAME [$DATE]"
564          echo "$GRML_NAME $VERSION Release Codename $RELEASENAME [$DATE]" > $CHROOT_OUTPUT/etc/grml_version
565          chmod 644 $CHROOT_OUTPUT/etc/grml_version
566          einfo "Rebuilding initramfs"
567          # make sure new /etc/grml_version reaches initramfs, iterate over all
568          # present kernel versions (note: we can't really handle more than one
569          # kernel version anyway right now)
570          # chroot $CHROOT_OUTPUT update-initramfs -u -t => might break when using kernel-package :(
571          for initrd in "$(basename $CHROOT_OUTPUT/boot/vmlinuz-*)" ; do
572            if ! chroot $CHROOT_OUTPUT update-initramfs -k "${initrd##vmlinuz-}" -c ; then
573              einfo "Creating fresh initrd did not work, trying update instead:"
574              log   "Creating fresh initrd did not work, trying update instead:"
575              chroot $CHROOT_OUTPUT update-initramfs -k "${initrd##vmlinuz-}" -u
576            fi
577          done
578          eend $?
579       fi
580
581       # Remove all FAI logs from chroot if class RELEASE is used:
582       if [ -f "$CHROOT_OUTPUT"/etc/grml_fai_release ] ; then
583          rm -rf "$CHROOT_OUTPUT"/var/log/fai/*
584          rm -f "$CHROOT_OUTPUT"/var/log/install_packages.list
585       fi
586
587       umount_all
588
589       # notice: 'fai dirinstall' does not seem to exit appropriate, so:
590       ERROR=''
591       CHECKLOG=/var/log/fai/$HOSTNAME/last
592       if [ -r "$CHECKLOG/software.log" ] ; then
593          # 1 errors during executing of commands
594          grep 'dpkg: error processing' $CHECKLOG/software.log >> $LOGFILE && ERROR=1
595          grep 'E: Method http has died unexpectedly!' $CHECKLOG/software.log >> $LOGFILE && ERROR=2
596          grep 'ERROR: chroot' $CHECKLOG/software.log >> $LOGFILE && ERROR=3
597          grep 'E: Failed to fetch' $CHECKLOG/software.log >> $LOGFILE && ERROR=4
598          grep 'Unable to write mmap - msync (28 No space left on device)' $CHECKLOG/software.log >> $LOGFILE && ERROR=5
599       fi
600
601       if [ -r "$CHECKLOG/shell.log" ] ; then
602          grep 'FAILED with exit code' $CHECKLOG/shell.log >> $LOGFILE && ERROR=6
603       fi
604
605       if [ -n "$ERROR" ] ; then
606          log    "Error: there was a critical error [${ERROR}] during execution of stage 'fai dirinstall' [$(date)]"
607          eerror "Error: there was a critical error during execution of stage 'fai dirinstall'"
608          eerror "Note:  check out ${CHECKLOG}/ for details. [exit ${ERROR}]"
609          eend 1
610          bailout 1
611       else
612          log "Finished execution of stage 'fai dirinstall' [$(date)]"
613          einfo "Finished execution of stage 'fai dirinstall'"
614       fi
615
616       einfo "Find FAI build logs at $(readlink -f /var/log/fai/$HOSTNAME/last)"
617       log   "Find FAI build logs at $(readlink -f /var/log/fai/$HOSTNAME/last)"
618       eend 0
619    fi
620 fi # BUILD_DIRTY?
621 # }}}
622
623 # package validator {{{
624 CHECKLOG=/var/log/fai/$HOSTNAME/last
625 # package validator
626 if [ -r "$CHECKLOG/package_errors.log" ] && grep -q '[a-z]' "$CHECKLOG/package_errors.log" ; then
627
628    if [ -n "$EXIT_ON_MISSING_PACKAGES" -a -z "$BUILD_DIRTY" ] ; then
629       eerror "The following packages were requested for installation but could not be processed:"
630       cat $CHECKLOG/package_errors.log
631       eerror "... exiting as requested via \$EXIT_ON_MISSING_PACKAGES."
632       eend 1
633       bailout 13
634    else
635       ewarn "The following packages were requested for installation but could not be processed:"
636       cat $CHECKLOG/package_errors.log
637       eend 0
638    fi
639 fi
640 # }}}
641
642 # BUILD_OUTPUT - execute arch specific stuff and squashfs {{{
643 [ -n "$BUILD_OUTPUT" ] || BUILD_OUTPUT="$OUTPUT/grml_cd"
644 mkdir -p "$BUILD_OUTPUT" || bailout 6 "Problem with creating $BUILD_OUTPUT for stage ARCH"
645
646 # prepare ISO
647 if [ "$ARCH" = i386 ] || [ "$ARCH" = amd64 ] ; then
648   if [ -n "$BOOTSTRAP_ONLY" ] ; then
649      log   "Skipping stage 'boot' as building with bootstrap only."
650      ewarn "Skipping stage 'boot' as building with bootstrap only." ; eend 0
651   else
652     if [ -d "$BUILD_OUTPUT"/boot/isolinux -a -z "$UPDATE" -a -z "$BUILD_ONLY" ] ; then
653        log   "Skipping stage 'boot' as $BUILD_OUTPUT/boot/isolinux exists already."
654        ewarn "Skipping stage 'boot' as $BUILD_OUTPUT/boot/isolinux exists already." ; eend 0
655     else
656        # booting stuff:
657        [ -d "$BUILD_OUTPUT"/boot/isolinux ] || mkdir -p "$BUILD_OUTPUT"/boot/isolinux
658        [ -d "$BUILD_OUTPUT"/boot/"${SHORT_NAME}" ] || mkdir -p "$BUILD_OUTPUT"/boot/"${SHORT_NAME}"
659
660        if [ -z "$NO_ADDONS" ] ; then
661           [ -d "$BUILD_OUTPUT"/boot/addons   ] || mkdir -p "$BUILD_OUTPUT"/boot/addons
662           if [ -r "$TEMPLATE_DIRECTORY"/boot/addons/memtest ] ; then
663              log "Installing $TEMPLATE_DIRECTORY/boot/addons/memtest"
664              cp "$TEMPLATE_DIRECTORY"/boot/addons/memtest "$BUILD_OUTPUT"/boot/addons/memtest
665           elif [ -r /boot/memtest86+.bin ] ; then
666              log "Installing /boot/memtest86+.bin"
667              cp /boot/memtest86+.bin "$BUILD_OUTPUT"/boot/addons/memtest
668           else
669              ewarn "No memtest binary found (either install package grml-live-addons or memtest86+), skipping."
670              log "No memtest binary found (either install package grml-live-addons or memtest86+), skipping."
671              eend 0
672           fi
673        fi
674
675        # if we don't have an initrd we a) can't boot and b) there was an error
676        # during build, so check for the file:
677        INITRD="$(ls $CHROOT_OUTPUT/boot/initrd* 2>/dev/null| grep -v '.bak$' | sort -r | head -1)"
678        if [ -n "$INITRD" ] ; then
679           cp $INITRD "$BUILD_OUTPUT"/boot/"${SHORT_NAME}"/initrd.gz
680           find $CHROOT_OUTPUT/boot/ -name initrd\*.bak -exec rm {} \;
681        else
682           log    "Error: No initrd found inside $CHROOT_OUTPUT/boot/ - Exiting"
683           eerror "Error: No initrd found inside $CHROOT_OUTPUT/boot/ - Exiting" ; eend 1
684           bailout 10
685        fi
686
687        KERNEL_IMAGE="$(ls $CHROOT_OUTPUT/boot/vmlinuz* 2>/dev/null | sort -r | head -1)"
688        if [ -n "$KERNEL_IMAGE" ] ; then
689           cp "$KERNEL_IMAGE" "$BUILD_OUTPUT"/boot/"${SHORT_NAME}"/linux26
690        else
691           log    "Error: No kernel found inside $CHROOT_OUTPUT/boot/ - Exiting"
692           eerror "Error: No kernel found inside $CHROOT_OUTPUT/boot/ - Exiting" ; eend 1
693           bailout 11
694        fi
695
696        [ -n "$TEMPLATE_DIRECTORY" ] || TEMPLATE_DIRECTORY='/usr/share/grml-live/templates'
697        if ! [ -d "${TEMPLATE_DIRECTORY}"/boot ] ; then
698           log    "Error: ${TEMPLATE_DIRECTORY}/boot does not exist. Exiting."
699           eerror "Error: ${TEMPLATE_DIRECTORY}/boot does not exist. Exiting." ; eend 1
700           bailout 8
701        fi
702
703        # *always* copy files to output directory so the variables
704        # get adjusted according to the build
705        cp ${TEMPLATE_DIRECTORY}/boot/isolinux/*  "$BUILD_OUTPUT"/boot/isolinux/
706
707        if [ -n "$NO_ADDONS" ] ; then
708           log   "Skipping installation of boot addons as requested via \$NO_ADDONS."
709           einfo "Skipping installation of boot addons as requested via \$NO_ADDONS."; eend 0
710        else
711           if ! [ -d "$TEMPLATE_DIRECTORY"/boot/addons ] ; then
712             log   "Boot addons not found, skipping therefore. (Consider installing package grml-live-addons)"
713             ewarn "Boot addons not found, skipping therefore. (Consider installing package grml-live-addons)" ; eend 0
714           else
715             # copy only files so we can handle bsd4grml on its own
716             for file in ${TEMPLATE_DIRECTORY}/boot/addons/* ; do
717               test -f $file && cp $file "$BUILD_OUTPUT"/boot/addons/
718             done
719
720             if [ -n "$NO_ADDONS_BSD4GRML" ] ; then
721                log   "Skipping installation of bsd4grml as requested via \$NO_ADDONS_BSD4GRML."
722                einfo "Skipping installation of bsd4grml as requested via \$NO_ADDONS_BSD4GRML."; eend 0
723             else
724                if [ -d "$TEMPLATE_DIRECTORY"/boot/addons/bsd4grml ] ; then
725                  cp -a ${TEMPLATE_DIRECTORY}/boot/addons/bsd4grml "$BUILD_OUTPUT"/boot/addons/
726                else
727                  log   "bsd4grml addon not found, skipping therefore."
728                  ewarn "bsd4grml addon not found, skipping therefore." ; eend 0
729                fi
730             fi
731
732           fi # no "$TEMPLATE_DIRECTORY"/boot/addons
733        fi # NO_ADDONS
734
735        if ! [ -d ${TEMPLATE_DIRECTORY}/boot/grub ] ; then
736           log   "grub templates do not exist, skipping therefore."
737           ewarn "grub templates do not exist, skipping therefore." ; eend 0
738        else
739           if ! [ -d "${BUILD_OUTPUT}/boot/grub" ] ; then
740              cp -a ${TEMPLATE_DIRECTORY}/boot/grub  "$BUILD_OUTPUT"/boot/
741           fi
742
743           # make sure we have recent template files available, otherwise updating
744           # the strings like $GRML_NAME and $VERSION might be out of date
745           cp ${TEMPLATE_DIRECTORY}/boot/grub/* "$BUILD_OUTPUT"/boot/grub/
746        fi
747
748        if ! [ -d "${TEMPLATE_DIRECTORY}"/GRML ] ; then
749           log    "Error: ${TEMPLATE_DIRECTORY}/GRML does not exist. Exiting."
750           eerror "Error: ${TEMPLATE_DIRECTORY}/GRML does not exist. Exiting." ; eend 1
751           bailout 9
752        fi
753
754        [ -d "$BUILD_OUTPUT"/GRML ] || mkdir "$BUILD_OUTPUT"/GRML
755        cp -a ${TEMPLATE_DIRECTORY}/GRML/* "$BUILD_OUTPUT"/GRML/
756
757        # adjust boot splash information:
758        RELEASE_INFO="$GRML_NAME $VERSION - Release Codename $RELEASENAME"
759        RELEASE_INFO="$(cut_string 68 "$RELEASE_INFO")"
760        RELEASE_INFO="$(extend_string_end 68 "$RELEASE_INFO")"
761
762        if [ -r "$BUILD_OUTPUT"/GRML/grml-version ] ; then
763           sed -i "s/%RELEASE_INFO%/$GRML_NAME $VERSION - $RELEASENAME/" "$BUILD_OUTPUT"/GRML/grml-version
764           sed -i "s/%DATE%/$DATE/"                                      "$BUILD_OUTPUT"/GRML/grml-version
765        fi
766
767        # make sure the squashfs filename is set accordingly:
768        SQUASHFS_NAME="$GRML_NAME.squashfs"
769
770        if [ -n "$NO_BOOTID" ] ; then
771           log   'Skipping bootid feature as requested via $NO_BOOTID.'
772           einfo 'Skipping bootid feature as requested via $NO_BOOTID.'
773        else
774           [ -n "$BOOTID" ] || BOOTID="$(echo ${GRML_NAME}${VERSION} | tr -d ',./;\- ')"
775           [ -d "$BUILD_OUTPUT"/conf ] || mkdir "$BUILD_OUTPUT"/conf
776           einfo "Generating /conf/bootid.txt with entry ${BOOTID}."
777           log   "Generating /conf/bootid.txt with entry ${BOOTID}."
778           echo "$BOOTID" > "$BUILD_OUTPUT"/conf/bootid.txt
779           eend $?
780        fi
781
782        # adjust all variables in the templates with the according distribution information
783        for file in "${BUILD_OUTPUT}"/boot/isolinux/*.cfg "${BUILD_OUTPUT}"/boot/isolinux/*.msg \
784                    "${BUILD_OUTPUT}"/boot/grub/* ; do
785          if [ -r "${file}" ] ; then
786            sed -i "s/%ARCH%/$ARCH/g"                    "${file}"
787            sed -i "s/%DATE%/$DATE/g"                    "${file}"
788            sed -i "s/%DISTRI_INFO%/$DISTRI_INFO/g"      "${file}"
789            sed -i "s/%DISTRI_NAME%/$DISTRI_NAME/g"      "${file}"
790            sed -i "s/%DISTRI_SPLASH%/$DISTRI_SPLASH/g"  "${file}"
791            sed -i "s/%GRML_NAME%/$GRML_NAME/g"          "${file}"
792            sed -i "s/%SQUASHFS_NAME%/$SQUASHFS_NAME/g"  "${file}"
793            sed -i "s/%RELEASE_INFO%/$RELEASE_INFO/g"    "${file}"
794            sed -i "s/%SHORT_NAME%/$SHORT_NAME/g"        "${file}"
795            sed -i "s/%VERSION%/$VERSION/g"              "${file}"
796
797            [ -n "$DEFAULT_BOOTOPTIONS" ] && sed -i "s/ boot=live/ boot=live $DEFAULT_BOOTOPTIONS/"  "${file}"
798
799            if [ -n "$NO_BOOTID" ] ; then
800               sed -i "s/ bootid=%BOOTID%//g" "${file}" # drop bootid bootoption
801            else
802               sed -i "s/%BOOTID%/$BOOTID/g" "${file}" # adjust bootid=... argument
803            fi
804          fi
805        done
806
807        # adjust bootsplash accordingly but make sure the string has the according lenght
808        SQUASHFS_NAME="$(cut_string 20 "$SQUASHFS_NAME")"
809        SQUASHFS_NAME="$(extend_string_end 20 "$SQUASHFS_NAME")"
810        for file in f4 f5 ; do
811           if [ -r "${BUILD_OUTPUT}/boot/isolinux/${file}" ] ; then
812              sed -i "s/%SQUASHFS_NAME%/$SQUASHFS_NAME/" "${BUILD_OUTPUT}/boot/isolinux/${file}"
813              sed -i "s/%SQUASHFS_NAME%/$SQUASHFS_NAME/" "${BUILD_OUTPUT}/boot/isolinux/${file}"
814           fi
815        done
816
817        # generate addon list
818        rm "${BUILD_OUTPUT}/${ADDONS_LIST_FILE}"
819        for name in "${BUILD_OUTPUT}"/boot/isolinux/addon_*.cfg ; do
820          include_name=$(basename "$name")
821          echo "include $include_name"  >> "${BUILD_OUTPUT}/${ADDONS_LIST_FILE}"
822        done
823
824        if ! [ -r "${BUILD_OUTPUT}/boot/isolinux/${DISTRI_NAME}.cfg" ] || [ "$DISTRI_NAME" = "grml" ] ; then
825           log "including grmlmain.cfg in ${BUILD_OUTPUT}/boot/isolinux/distri.cfg"
826           echo "include grmlmain.cfg"    >  "${BUILD_OUTPUT}/boot/isolinux/distri.cfg"
827           echo "include default.cfg"     >  "${BUILD_OUTPUT}/boot/isolinux/grmlmain.cfg"
828           echo "include menuoptions.cfg" >> "${BUILD_OUTPUT}/boot/isolinux/grmlmain.cfg"
829           echo "include grml.cfg"        >> "${BUILD_OUTPUT}/boot/isolinux/grmlmain.cfg"
830
831           for f in "${BUILD_OUTPUT}"/boot/isolinux/submenu*.cfg ; do
832             echo "include $(basename $f)"     >> "${BUILD_OUTPUT}/boot/isolinux/grmlmain.cfg"
833           done
834
835           echo "include options.cfg"     >> "${BUILD_OUTPUT}/boot/isolinux/grmlmain.cfg"
836           if [ ! -n "$NO_ADDONS" ] ; then
837             echo "include addons.cfg"    >> "${BUILD_OUTPUT}/boot/isolinux/grmlmain.cfg"
838           fi
839           echo "include isoprompt.cfg"   >> "${BUILD_OUTPUT}/boot/isolinux/grmlmain.cfg"
840           echo "include hd.cfg"          >> "${BUILD_OUTPUT}/boot/isolinux/grmlmain.cfg"
841           echo "include hidden.cfg"      >> "${BUILD_OUTPUT}/boot/isolinux/grmlmain.cfg"
842        else # assume we are building a custom distribution:
843           log "File ${BUILD_OUTPUT}/boot/isolinux/${DISTRI_NAME}.cfg found, using it."
844           einfo "File ${BUILD_OUTPUT}/boot/isolinux/${DISTRI_NAME}.cfg found, using it."
845           if grep -q "^include ${DISTRI_NAME}.cfg" "${BUILD_OUTPUT}/boot/isolinux/distri.cfg" ; then
846             log "include for ${DISTRI_NAME}.cfg already present, nothing to do."
847             eindent
848             einfo "include for ${DISTRI_NAME}.cfg already present, nothing to do."
849             eoutdent
850             eend $?
851          else
852             log "including ${DISTRI_NAME}.cfg in ${BUILD_OUTPUT}/boot/isolinux/distri.cfg"
853             echo "include ${DISTRI_NAME}.cfg" > "${BUILD_OUTPUT}/boot/isolinux/distri.cfg"
854             [ -n "$NO_ADDONS" ] || echo "include addons.cfg" >> "${BUILD_OUTPUT}/boot/isolinux/distri.cfg"
855           fi
856        fi
857
858        # use old style console based isolinux method only if requested:
859        if [[ "${ISOLINUX_METHOD}" == "console" ]] ; then
860           log 'Using console based isolinux method as requested via $ISOLINUX_METHOD.'
861           einfo 'Using console based isolinux method as requested via $ISOLINUX_METHOD.'
862           if grep -q '^include console.cfg' "${BUILD_OUTPUT}/boot/isolinux/distri.cfg" ; then
863             einfo "include for console.cfg already foud, nothing to do."
864             eend 0
865           else
866             log "including console.cfg in ${BUILD_OUTPUT}/boot/isolinux/isolinux.cfg"
867             einfo "including console.cfg in ${BUILD_OUTPUT}/boot/isolinux/isolinux.cfg"
868             echo "include console.cfg" >> "${BUILD_OUTPUT}/boot/isolinux/isolinux.cfg"
869             eend $?
870           fi
871        else
872           log 'Using graphical boot menu.'
873           if grep -q '^include vesamenu.cfg' "${BUILD_OUTPUT}/boot/isolinux/isolinux.cfg" ; then
874             log "include for vesamenu.cfg already foud, nothing to do."
875           else
876             log "including vesamenu.cfg in ${BUILD_OUTPUT}/boot/isolinux/isolinux.cfg"
877             echo "include vesamenu.cfg" >> "${BUILD_OUTPUT}/boot/isolinux/isolinux.cfg"
878           fi
879        fi
880
881        # jump back to grub from bsd4grml (/boot/grub/stage2):
882        GRUB_LEGACY=stage2
883
884        if [ -e "$BUILD_OUTPUT"/boot/addons/bsd4grml/boot.6 ]; then
885           if [ -e "$BUILD_OUTPUT"/boot/grub/core.img ]; then
886              GRUB_VERSION=2
887           else
888              GRUB_VERSION=1
889           fi
890
891           for file in "$BUILD_OUTPUT"/boot/addons/bsd4grml/boot.6 \
892                       "$BUILD_OUTPUT"/boot/addons/bsd4grml/boot.cfg \
893                       "$BUILD_OUTPUT"/boot/isolinux/*.cfg \
894                       "$BUILD_OUTPUT"/boot/grub/grub.cfg \
895                       "$BUILD_OUTPUT"/boot/grub/menu.lst ; do
896               if [ -e "$file" ] ; then
897                 sed -i -e "s!%GRUB_VERSION%!$GRUB_VERSION!g" \
898                        -e "s!%GRUB_LEGACY%!$GRUB_LEGACY!g" "$file"
899               fi
900           done
901
902           sed -i "s/%RELEASE_INFO%/$GRML_NAME $VERSION - $RELEASENAME/" "$BUILD_OUTPUT"/boot/addons/bsd4grml/boot.6
903        fi
904
905        if [ -e "$BUILD_OUTPUT"/boot/grub/$GRUB_LEGACY ]; then
906           sed -i "s/%GRUB_LEGACY%/$GRUB_LEGACY/g" "$BUILD_OUTPUT"/boot/grub/menu.lst
907           sed -i "s/%GRUB_LEGACY%/$GRUB_LEGACY/g" "$BUILD_OUTPUT"/boot/grub/grub.cfg
908        elif [ -e "$BUILD_OUTPUT"/boot/grub/menu.lst -a -e "$BUILD_OUTPUT"/boot/grub/grub.cfg ] ; then
909           sed -i "/%GRUB_LEGACY%/d" "$BUILD_OUTPUT"/boot/grub/menu.lst
910           sed -i "/%GRUB_LEGACY%/d" "$BUILD_OUTPUT"/boot/grub/grub.cfg
911        fi
912
913        DPKG_LIST="/var/log/fai/$HOSTNAME/last/dpkg.list" # the dpkg --list output of the chroot
914        if ! [ -r "$DPKG_LIST" ] ; then
915           ewarn "$DPKG_LIST could not be read, ignoring to store package information on ISO therefore."
916        else
917           einfo "Storing package list information as /GRML/${GRML_NAME}-packages.txt on ISO."
918           cp "$DPKG_LIST" "${BUILD_OUTPUT}/GRML/${GRML_NAME}-packages.txt"
919           eend $?
920        fi
921
922        # autostart for Windows:
923        if [ -d "${TEMPLATE_DIRECTORY}/windows/autostart/" ] ; then
924           cp ${TEMPLATE_DIRECTORY}/windows/autostart/* "$BUILD_OUTPUT"/
925        fi
926
927     FORCE_ISO_REBUILD=true
928     einfo "Finished execution of stage 'boot'" ; eend 0
929     fi
930   fi # BOOTSTRAP_ONLY
931 else
932    log    'Error: Unsupported ARCH, sorry. Want to support it? Contribute!'
933    eerror 'Error: Unsupported ARCH, sorry. Want to support it? Contribute!' ; eend 1
934    bailout
935 fi
936
937 # support installation of local files into the chroot/ISO
938 if [ -n "$CHROOT_INSTALL" ] ; then
939   if ! [ -d "$CHROOT_INSTALL" ] ; then
940      log "Configuration variable \$CHROOT_INSTALL is set but not a directory; ignoring"
941      ewarn "Configuration variable \$CHROOT_INSTALL is set but not a directory; ignoring"
942   else
943      log "Copying local files to chroot as requested via \$CHROOT_INSTALL"
944      einfo "Copying local files to chroot as requested via \$CHROOT_INSTALL"
945      rsync -avz --inplace "$CHROOT_INSTALL"/ "$CHROOT_OUTPUT/"
946      eend $?
947      einfo "Make sure to run squashfs stage, otherwise your local files won't be part of the ISO."
948      FORCE_ISO_REBUILD=true
949   fi
950 fi
951
952 if [ -f "$BUILD_OUTPUT"/live/${GRML_NAME}.squashfs -a -z "$UPDATE" -a -z "$BUILD_ONLY" -a -z "$BUILD_DIRTY" ] ; then
953    log   "Skipping stage 'squashfs' as $BUILD_OUTPUT/live exists already."
954    ewarn "Skipping stage 'squashfs' as $BUILD_OUTPUT/live exists already." ; eend 0
955 elif [ -n "$SKIP_MKSQUASHFS" ] ; then
956    log   "Skipping stage 'squashfs' as requested via option -q or -N"
957    ewarn "Skipping stage 'squashfs' as requested via option -q or -N" ; eend 0
958 else
959    [ -d "$BUILD_OUTPUT"/live ] || mkdir "$BUILD_OUTPUT"/live
960    # make sure we don't leave (even an empty) base.tgz:
961    [ -f "$CHROOT_OUTPUT/base.tgz" ] && rm -f "$CHROOT_OUTPUT/base.tgz"
962
963    # if unconfigured default to squashfs-tools' mksquashfs binary
964    if [ -z "$SQUASHFS_BINARY" ] ; then
965       SQUASHFS_BINARY='mksquashfs'
966    fi
967
968    if which "$SQUASHFS_BINARY" >/dev/null 2>&1 ; then
969       log    "Using mksquashfs binary ${SQUASHFS_BINARY}"
970       einfo  "Using mksquashfs binary ${SQUASHFS_BINARY}" ; eend 0
971    else
972       log    "Error: mksquashfs binary ($SQUASHFS_BINARY) not found. Exiting."
973       eerror "Error: mksquashfs binary ($SQUASHFS_BINARY) not found. Exiting." ; eend 1
974       bailout
975    fi
976
977    # use sane defaults if $SQUASHFS_OPTIONS isn't set
978    if [ -z "$SQUASHFS_OPTIONS" ] ; then
979      # use blocksize 256k as this gives best result with regards to time + compression
980      SQUASHFS_OPTIONS="-b 256k"
981
982      # set lzma/xz compression by default, unless -z option has been specified on command line
983      if [ -z "$SQUASHFS_ZLIB" ] ; then
984         SQUASHFS_OPTIONS="$SQUASHFS_OPTIONS -comp xz"
985      else
986         SQUASHFS_OPTIONS="$SQUASHFS_OPTIONS -comp gzip"
987      fi
988    fi
989
990    # support exclusion of files via exclude-file:
991    if [ -n "$SQUASHFS_EXCLUDES_FILE" -a "$SQUASHFS_EXCLUDES_FILE" ] ; then
992       SQUASHFS_OPTIONS="$SQUASHFS_OPTIONS -ef $SQUASHFS_EXCLUDES_FILE"
993    fi
994
995    # get rid of unnecessary files when building grml-small for final release:
996    if echo "$CLASSES" | grep -q GRML_SMALL ; then
997       SQUASHFS_OPTIONS="$SQUASHFS_OPTIONS -e initrd.img* vmlinuz*"
998    fi
999
1000    # log stuff
1001    SQUASHFS_STDERR="$(mktemp -t grml-live.XXXXXX)"
1002
1003    # informational stuff
1004    [ -n "$SQUASHFS_OPTIONS" ]  && SQUASHFS_INFO_MSG="$SQUASHFS_OPTIONS"
1005    [ -n "$SQUASHFS_INFO_MSG" ] && SQUASHFS_INFO_MSG="using options: $SQUASHFS_INFO_MSG"
1006    einfo "Squashfs build information: running binary $SQUASHFS_BINARY $SQUASHFS_INFO_MSG"
1007
1008    log "$SQUASHFS_BINARY $CHROOT_OUTPUT/* $BUILD_OUTPUT/live/${GRML_NAME}.squashfs -noappend $SQUASHFS_OPTIONS"
1009
1010    if $SQUASHFS_BINARY $CHROOT_OUTPUT/* $BUILD_OUTPUT/live/"${GRML_NAME}".squashfs \
1011       -noappend $SQUASHFS_OPTIONS 2>"${SQUASHFS_STDERR}" ; then
1012       echo "${GRML_NAME}.squashfs" > $BUILD_OUTPUT/live/filesystem.module
1013       log "Finished execution of stage 'squashfs' [$(date)]"
1014       einfo "Finished execution of stage 'squashfs'" ; eend 0
1015    else
1016       log    "Error: there was a critical error executing stage 'squashfs' [$(date)]:"
1017       log    "$(cat $SQUASHFS_STDERR)"
1018       eerror "Error: there was a critical error executing stage 'squashfs':"
1019       cat    "${SQUASHFS_STDERR}"
1020       eend 1
1021       bailout
1022    fi
1023
1024    FORCE_ISO_REBUILD=true
1025 fi
1026
1027 # create md5sum file:
1028 if [ -z "$BOOTSTRAP_ONLY" ] ; then
1029   ( cd $BUILD_OUTPUT/GRML &&
1030   find .. -type f -not -name md5sums -not -name isolinux.bin -exec md5sum {} \; > md5sums )
1031 fi
1032 # }}}
1033
1034 # ISO_OUTPUT - mkisofs {{{
1035 [ -n "$ISO_OUTPUT" ] || ISO_OUTPUT="$OUTPUT/grml_isos"
1036 [ -n "$ISO_NAME" ] || ISO_NAME="${GRML_NAME}_${VERSION}.iso"
1037
1038 if [ "$BOOT_METHOD" = "isolinux" ] ; then
1039    BOOT_ARGS="-no-emul-boot -boot-load-size 4 -boot-info-table -b boot/isolinux/isolinux.bin -c boot/isolinux/boot.cat"
1040 elif [ "$BOOT_METHOD" = "grub" ] ; then
1041    BOOT_ARGS="-no-emul-boot -boot-load-size 4 -boot-info-table -b boot/grub/stage2"
1042 elif [ "$BOOT_METHOD" = "grub2" ] ; then
1043    BOOT_ARGS="-no-emul-boot -boot-load-size 4 -b boot/grub/toriboot.bin"
1044 fi
1045
1046 # Just until http://bts.grml.org/grml/issue945 has been resolved.
1047 # HYBRID_METHOD defaults to manifold, so make sure the default works OOTB.
1048 if [[ $BOOT_METHOD != isolinux && ($HYBRID_METHOD = isohybrid || $HYBRID_METHOD = manifold) ]]; then
1049   log   "Setting HYBRID_METHOD to grub2 as hybrid mode does not work with isohybrid yet."
1050   ewarn "Setting HYBRID_METHOD to grub2 as hybrid mode does not work with isohybrid yet."
1051   HYBRID_METHOD='grub2'
1052   eend 0
1053 fi
1054
1055 if [ -f "${ISO_OUTPUT}/${ISO_NAME}" -a -z "$UPDATE" -a -z "$BUILD_ONLY" -a -z "$BUILD_DIRTY" -a "$FORCE_ISO_REBUILD" = "false" ]  ; then
1056    log   "Skipping stage 'iso build' as $ISO_OUTPUT/${ISO_NAME} exists already."
1057    ewarn "Skipping stage 'iso build' as $ISO_OUTPUT/${ISO_NAME} exists already." ; eend 0
1058 elif [ -n "$SKIP_MKISOFS" ] ; then
1059    log   "Skipping stage 'iso build' as requested via option -n or -N"
1060    ewarn "Skipping stage 'iso build' as requested via option -n or -N" ; eend 0
1061 else
1062    mkdir -p "$ISO_OUTPUT" || bailout 6 "Problem with creating $ISO_OUTPUT for stage 'iso build'"
1063
1064    if $FORCE_ISO_REBUILD && ! [ -f "${ISO_OUTPUT}/${ISO_NAME}" ] ; then
1065       log   "Forcing rebuild of ISO because files on ISO have been modified."
1066       einfo "Forcing rebuild of ISO because files on ISO have been modified."
1067    fi
1068
1069    # support mkisofs as well as genisoimage
1070    if which mkisofs >/dev/null 2>&1; then
1071       MKISOFS='mkisofs'
1072    elif which genisoimage >/dev/null 2>&1; then
1073       MKISOFS='genisoimage'
1074    else
1075       log    "Error: neither mkisofs nor genisoimage available - can not create ISO."
1076       eerror "Error: neither mkisofs nor genisoimage available - can not create ISO." ; eend 1
1077       bailout
1078    fi
1079
1080    CURRENT_DIR=$(pwd)
1081    if cd "$BUILD_OUTPUT" ; then
1082       if [ "$BOOT_METHOD" = "grub2" ]; then
1083          # make a 2048-byte bootsector for El Torito
1084          dd if=/dev/zero of=boot/grub/toriboot.bin bs=512 count=4 2>/dev/null
1085          # those are in 2048-byte sectors, so 1 16 matches 4 63 below
1086          echo 1 16 | mksh /usr/share/grml-live/scripts/bootgrub.mksh -B 11 | \
1087             dd of=boot/grub/toriboot.bin conv=notrunc 2>/dev/null
1088       fi
1089       log "$MKISOFS -V '${GRML_NAME} ${VERSION}' -publisher 'grml-live | grml.org' -l -r -J $BOOT_ARGS -o ${ISO_OUTPUT}/${ISO_NAME} ."
1090       "$MKISOFS" -V "${GRML_NAME} ${VERSION}" -publisher 'grml-live | grml.org' \
1091               -l -r -J $BOOT_ARGS -no-pad \
1092               -o "${ISO_OUTPUT}/${ISO_NAME}" . ; RC=$?
1093       # both of these need core.img there, so it’s easier to write it here
1094       if [ "$BOOT_METHOD" = "grub2" ] || [ "$HYBRID_METHOD" = "grub2" ]; then
1095          # must be <= 30720 bytes
1096          dd if=boot/grub/core.img of="${ISO_OUTPUT}/${ISO_NAME}" \
1097            conv=notrunc bs=512 seek=4 2>/dev/null
1098       fi
1099
1100       # pad the output ISO to multiples of 256 KiB for partition table support
1101       siz=$($getfilesize "${ISO_OUTPUT}/${ISO_NAME}")
1102       cyls=$((siz / 512 / 32 / 16 + 1))   # C=$cyls H=16 S=32
1103       siz=$((cyls * 16 * 32 * 512))   # size after padding
1104       dd if=/dev/zero bs=1 count=1 seek=$((siz - 1)) \
1105          of="${ISO_OUTPUT}/${ISO_NAME}" 2>/dev/null
1106
1107       # support disabling hybrid ISO image
1108       if [ "$HYBRID_METHOD" = "disable" ] ; then\
1109          log   "Skipping creation of hybrid ISO file as requested via HYBRID_METHOD=disable"
1110          einfo "Skipping creation of hybrid ISO file as requested via HYBRID_METHOD=disable"
1111          eend 0
1112       # use isohybrid only on request
1113       elif [ "$HYBRID_METHOD" = "isohybrid" ] ; then
1114          if ! which isohybrid >/dev/null 2>&1 ; then
1115            bailout 12 "isohybrid binary not found - please install syslinux/syslinux-common"
1116          else
1117            log "Creating hybrid ISO file with isohybrid method"
1118            einfo "Creating hybrid ISO file with isohybrid method"
1119            # Notes for consideration:
1120            # "-entry 4 -type 1c"
1121            # * using 4 as the partition number is supposed to help with BIOSes
1122            #   that only support USB-Zip boot
1123            # * using 1c (i.e. hidden FAT32 LBA), instead of the default 0x17
1124            #   (hidden NTFS, IIRC), as the partition type is sometimes needed
1125            #   to get the BIOS even look at the partition created by isohybrid
1126            isohybrid "${ISO_OUTPUT}/${ISO_NAME}"
1127            eend $?
1128          fi
1129       # by default use our manifold boot method:
1130       else
1131          # isoinfo is part of both mkisofs and genisoimage so we're good
1132          bootoff=$(isoinfo -l -i "${ISO_OUTPUT}/${ISO_NAME}" | \
1133            sed -n '/^.*\[ *\([0-9]*\)[] ].* ISOLINUX.BIN;1 *$/s//\1/p')
1134          if ! [ -r boot/grub/core.img ] ; then
1135            ewarn "boot/grub/core.img not found, not creating manifold boot ISO file"
1136          elif [ "${bootoff:-0}" -lt 1 ] ; then
1137            ewarn "isolinux.bin not found on the ISO file, disabling manifold boot"
1138          else
1139            log "Creating hybrid ISO file with manifold method"
1140            einfo "Creating hybrid ISO file with manifold method"
1141            if [ "$HYBRID_METHOD" = "grub2" ] ; then
1142                # 512 bytes: MBR, partition table, load GRUB 2
1143                echo 4 63 | mksh /usr/share/grml-live/scripts/bootgrub.mksh -A -M 4:0x96 -g $cyls:16:32
1144            else
1145               # read only one but 2048-byte sized (scale: << 2) sector
1146               echo $bootoff $bootoff | \
1147                  mksh /usr/share/grml-live/scripts/bootilnx.mksh -A -M 4:0x96 -g $cyls:16:32 -S 2
1148            fi | dd of="${ISO_OUTPUT}/${ISO_NAME}" conv=notrunc 2>/dev/null
1149            eend $?
1150          fi
1151       fi
1152
1153       # generate md5sum and sha1sum of ISO if we are using class 'RELEASE':
1154       case $CLASSES in *RELEASE*)
1155          [ "$RC" = 0 ] && \
1156          (
1157            if cd $ISO_OUTPUT ; then
1158              md5sum ${ISO_NAME} > ${ISO_NAME}.md5 && \
1159              touch -r ${ISO_NAME} ${ISO_NAME}.md5
1160              sha1sum ${ISO_NAME} > ${ISO_NAME}.sha1 && \
1161              touch -r ${ISO_NAME} ${ISO_NAME}.sha1
1162            fi
1163          )
1164          ;;
1165       esac
1166
1167       cd "$CURRENT_DIR"
1168    fi
1169
1170    if [ "$RC" = 0 ] ; then
1171       log   "Finished execution of stage 'iso build' [$(date)]"
1172       einfo "Finished execution of stage 'iso build'" ; eend 0
1173    else
1174       log    "Error: there was a critical error ($RC) executing stage 'iso build' [$(date)]"
1175       eerror "Error: there was a critical error executing stage 'iso build'" ; eend 1
1176       bailout $RC
1177    fi
1178 fi
1179 # }}}
1180
1181 # log build information to database if grml-live-db is installed and enabled {{{
1182 dpkg_to_db() {
1183 if [ -d /usr/share/grml-live-db ] ; then
1184
1185   # safe defaults
1186   DPKG_LIST="/var/log/fai/$HOSTNAME/last/dpkg.list" # the dpkg --list output of the chroot:
1187   [ -n "$DPKG_DATABASE" ]  || DPKG_DATABASE=/var/log/grml-live.db
1188   [ -n "$DPKG_DBSCRIPT" ]  || DPKG_DBSCRIPT=/usr/share/grml-live-db/scripts/dpkg-to-db
1189   [ -n "$DPKG_DBOPTIONS" ] || DPKG_DBOPTIONS="--database $DPKG_DATABASE --logfile $LOGFILE --flavour $GRML_NAME --dpkg $DPKG_LIST"
1190
1191   if ! [ -x "$DPKG_DBSCRIPT" ] ; then
1192     log "Error: $DPKG_DBSCRIPT is not executable, can not log dpkg information."
1193     eerror "Error: $DPKG_DBSCRIPT is not executable, can not log dpkg information." ; eend 1
1194     bailout 14
1195   fi
1196
1197   # disable by default for now, not sure whether really everyone is using a local db file
1198   #if ! touch "$DPKG_DATABASE" ; then
1199   #  eerror "Error: can not write to ${DPKG_DATABASE}, can not log dpkg information." ; eend 1
1200   #  bailout 14
1201   #fi
1202
1203   if ! [ -r "$DPKG_LIST" ] ; then
1204      log   "Warning: can not read $DPKG_LIST - can not provide information to $DPKG_DBSCRIPT (dirty build?)"
1205      ewarn "Warning: can not read $DPKG_LIST - can not provide information to $DPKG_DBSCRIPT (dirty build?)" ; eend 0
1206   else
1207      einfo "Logging $DPKG_LIST to database $DPKG_DATABASE"
1208      log "Logging $DPKG_LIST to database $DPKG_DATABASE"
1209      log "Executing $DPKG_DBSCRIPT $DPKG_DBOPTIONS"
1210      eindent
1211
1212      if DB_INFO=$("$DPKG_DBSCRIPT" $DPKG_DBOPTIONS 2>&1) ; then
1213        einfo "$DB_INFO"
1214        eend 0
1215      else
1216        eerror "$DB_INFO"
1217        eend 1
1218      fi
1219
1220      eoutdent
1221   fi
1222
1223 fi
1224 }
1225 # }}}
1226
1227 # finalize {{{
1228 [ -n "$start_seconds" ] && SECONDS="$[$(cut -d . -f 1 /proc/uptime)-$start_seconds]" || SECONDS="unknown"
1229 log "Successfully finished execution of $PN [$(date) - running ${SECONDS} seconds]"
1230
1231 dpkg_to_db # make sure we catch the last log line as well, therefore execute between log + einfo
1232
1233 einfo "Successfully finished execution of $PN [$(date) - running ${SECONDS} seconds]" ; eend 0
1234 bailout 0
1235 # }}}
1236
1237 ## END OF FILE #################################################################
1238 # vim:foldmethod=marker ts=2 ft=sh ai expandtab tw=80 sw=2