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