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