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