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