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