4b06507a7977b0328f51ff0fd3b8dc6a962e0567
[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       # tell dpkg to use "unsafe io" during the build
544       [ -d "$CHROOT_OUTPUT/etc/dpkg/dpkg.cfg.d" ] || mkdir -p "$CHROOT_OUTPUT/etc/dpkg/dpkg.cfg.d"
545       echo force-unsafe-io > "$CHROOT_OUTPUT/etc/dpkg/dpkg.cfg.d/unsafe-io"
546
547       log "Executed FAI command line:"
548       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"
549       BUILD_ONLY="$BUILD_ONLY" BOOTSTRAP_ONLY="$BOOTSTRAP_ONLY" fai $VERBOSE -C "$GRML_FAI_CONFIG" -c"$CLASSES" -u \
550       "$HOSTNAME" $FAI_ACTION "$CHROOT_OUTPUT" $FAI_ARGS | tee -a $LOGFILE
551       RC="$PIPESTATUS" # notice: bash-only
552
553       rm -f "$CHROOT_OUTPUT/etc/dpkg/dpkg.cfg.d/unsafe-io"
554
555       FORCE_ISO_REBUILD=true
556
557       if [ "$RC" != 0 ] ; then
558          log    "Error: critical error while executing fai [exit code ${RC}]. Exiting."
559          eerror "Error: critical error while executing fai [exit code ${RC}]. Exiting." ; eend 1
560          bailout 1
561       else
562          log "Setting /etc/grml_version to $GRML_NAME $VERSION Release Codename $RELEASENAME [$DATE]"
563          echo "$GRML_NAME $VERSION Release Codename $RELEASENAME [$DATE]" > $CHROOT_OUTPUT/etc/grml_version
564          chmod 644 $CHROOT_OUTPUT/etc/grml_version
565          einfo "Rebuilding initramfs"
566          # make sure new /etc/grml_version reaches initramfs, iterate over all
567          # present kernel versions (note: we can't really handle more than one
568          # kernel version anyway right now)
569          # chroot $CHROOT_OUTPUT update-initramfs -u -t => might break when using kernel-package :(
570          for initrd in "$(basename $CHROOT_OUTPUT/boot/vmlinuz-*)" ; do
571            chroot $CHROOT_OUTPUT update-initramfs -k "${initrd##vmlinuz-}" -c
572          done
573          eend $?
574       fi
575
576       # Remove all FAI logs from chroot if class RELEASE is used:
577       if [ -f "$CHROOT_OUTPUT"/etc/grml_fai_release ] ; then
578          rm -rf "$CHROOT_OUTPUT"/var/log/fai/*
579          rm -f "$CHROOT_OUTPUT"/var/log/install_packages.list
580       fi
581
582       umount_all
583
584       # notice: 'fai dirinstall' does not seem to exit appropriate, so:
585       ERROR=''
586       CHECKLOG=/var/log/fai/$HOSTNAME/last
587       if [ -r "$CHECKLOG/software.log" ] ; then
588          # 1 errors during executing of commands
589          grep 'dpkg: error processing' $CHECKLOG/software.log >> $LOGFILE && ERROR=1
590          grep 'E: Method http has died unexpectedly!' $CHECKLOG/software.log >> $LOGFILE && ERROR=2
591          grep 'ERROR: chroot' $CHECKLOG/software.log >> $LOGFILE && ERROR=3
592          grep 'E: Failed to fetch' $CHECKLOG/software.log >> $LOGFILE && ERROR=4
593          grep 'Unable to write mmap - msync (28 No space left on device)' $CHECKLOG/software.log >> $LOGFILE && ERROR=5
594       fi
595
596       if [ -r "$CHECKLOG/shell.log" ] ; then
597          grep 'FAILED with exit code' $CHECKLOG/shell.log >> $LOGFILE && ERROR=6
598       fi
599
600       if [ -n "$ERROR" ] ; then
601          log    "Error: there was a critical error [${ERROR}] during execution of stage 'fai dirinstall' [$(date)]"
602          eerror "Error: there was a critical error during execution of stage 'fai dirinstall'"
603          eerror "Note:  check out ${CHECKLOG}/ for details. [exit ${ERROR}]"
604          eend 1
605          bailout 1
606       else
607          log "Finished execution of stage 'fai dirinstall' [$(date)]"
608          einfo "Finished execution of stage 'fai dirinstall'"
609       fi
610
611       einfo "Find FAI build logs at $(readlink -f /var/log/fai/$HOSTNAME/last)"
612       log   "Find FAI build logs at $(readlink -f /var/log/fai/$HOSTNAME/last)"
613       eend 0
614    fi
615 fi # BUILD_DIRTY?
616 # }}}
617
618 # package validator {{{
619 CHECKLOG=/var/log/fai/$HOSTNAME/last
620 # package validator
621 if [ -r "$CHECKLOG/package_errors.log" ] && grep -q '[a-z]' "$CHECKLOG/package_errors.log" ; then
622
623    if [ -n "$EXIT_ON_MISSING_PACKAGES" -a -z "$BUILD_DIRTY" ] ; then
624       eerror "The following packages were requested for installation but could not be processed:"
625       cat $CHECKLOG/package_errors.log
626       eerror "... exiting as requested via \$EXIT_ON_MISSING_PACKAGES."
627       eend 1
628       bailout 13
629    else
630       ewarn "The following packages were requested for installation but could not be processed:"
631       cat $CHECKLOG/package_errors.log
632       eend 0
633    fi
634 fi
635 # }}}
636
637 # BUILD_OUTPUT - execute arch specific stuff and squashfs {{{
638 [ -n "$BUILD_OUTPUT" ] || BUILD_OUTPUT="$OUTPUT/grml_cd"
639 mkdir -p "$BUILD_OUTPUT" || bailout 6 "Problem with creating $BUILD_OUTPUT for stage ARCH"
640
641 # prepare ISO
642 if [ "$ARCH" = i386 ] || [ "$ARCH" = amd64 ] ; then
643   if [ -n "$BOOTSTRAP_ONLY" ] ; then
644      log   "Skipping stage 'boot' as building with bootstrap only."
645      ewarn "Skipping stage 'boot' as building with bootstrap only." ; eend 0
646   else
647     if [ -d "$BUILD_OUTPUT"/boot/isolinux -a -z "$UPDATE" -a -z "$BUILD_ONLY" ] ; then
648        log   "Skipping stage 'boot' as $BUILD_OUTPUT/boot/isolinux exists already."
649        ewarn "Skipping stage 'boot' as $BUILD_OUTPUT/boot/isolinux exists already." ; eend 0
650     else
651        # booting stuff:
652        [ -d "$BUILD_OUTPUT"/boot/isolinux ] || mkdir -p "$BUILD_OUTPUT"/boot/isolinux
653        [ -d "$BUILD_OUTPUT"/boot/"${SHORT_NAME}" ] || mkdir -p "$BUILD_OUTPUT"/boot/"${SHORT_NAME}"
654
655        if [ -z "$NO_ADDONS" ] ; then
656           [ -d "$BUILD_OUTPUT"/boot/addons   ] || mkdir -p "$BUILD_OUTPUT"/boot/addons
657           if [ -r "$TEMPLATE_DIRECTORY"/boot/addons/memtest ] ; then
658              log "Installing $TEMPLATE_DIRECTORY/boot/addons/memtest"
659              cp "$TEMPLATE_DIRECTORY"/boot/addons/memtest "$BUILD_OUTPUT"/boot/addons/memtest
660           elif [ -r /boot/memtest86+.bin ] ; then
661              log "Installing /boot/memtest86+.bin"
662              cp /boot/memtest86+.bin "$BUILD_OUTPUT"/boot/addons/memtest
663           else
664              ewarn "No memtest binary found (either install package grml-live-addons or memtest86+), skipping."
665              log "No memtest binary found (either install package grml-live-addons or memtest86+), skipping."
666              eend 0
667           fi
668        fi
669
670        # if we don't have an initrd we a) can't boot and b) there was an error
671        # during build, so check for the file:
672        INITRD="$(ls $CHROOT_OUTPUT/boot/initrd* 2>/dev/null| grep -v '.bak$' | sort -r | head -1)"
673        if [ -n "$INITRD" ] ; then
674           cp $INITRD "$BUILD_OUTPUT"/boot/"${SHORT_NAME}"/initrd.gz
675           find $CHROOT_OUTPUT/boot/ -name initrd\*.bak -exec rm {} \;
676        else
677           log    "Error: No initrd found inside $CHROOT_OUTPUT/boot/ - Exiting"
678           eerror "Error: No initrd found inside $CHROOT_OUTPUT/boot/ - Exiting" ; eend 1
679           bailout 10
680        fi
681
682        KERNEL_IMAGE="$(ls $CHROOT_OUTPUT/boot/vmlinuz* 2>/dev/null | sort -r | head -1)"
683        if [ -n "$KERNEL_IMAGE" ] ; then
684           cp "$KERNEL_IMAGE" "$BUILD_OUTPUT"/boot/"${SHORT_NAME}"/linux26
685        else
686           log    "Error: No kernel found inside $CHROOT_OUTPUT/boot/ - Exiting"
687           eerror "Error: No kernel found inside $CHROOT_OUTPUT/boot/ - Exiting" ; eend 1
688           bailout 11
689        fi
690
691        [ -n "$TEMPLATE_DIRECTORY" ] || TEMPLATE_DIRECTORY='/usr/share/grml-live/templates'
692        if ! [ -d "${TEMPLATE_DIRECTORY}"/boot ] ; then
693           log    "Error: ${TEMPLATE_DIRECTORY}/boot does not exist. Exiting."
694           eerror "Error: ${TEMPLATE_DIRECTORY}/boot does not exist. Exiting." ; eend 1
695           bailout 8
696        fi
697
698        # *always* copy files to output directory so the variables
699        # get adjusted according to the build
700        cp ${TEMPLATE_DIRECTORY}/boot/isolinux/*  "$BUILD_OUTPUT"/boot/isolinux/
701
702        if [ -n "$NO_ADDONS" ] ; then
703           log   "Skipping installation of boot addons as requested via \$NO_ADDONS."
704           einfo "Skipping installation of boot addons as requested via \$NO_ADDONS."; eend 0
705        else
706           if ! [ -d "$TEMPLATE_DIRECTORY"/boot/addons ] ; then
707             log   "Boot addons not found, skipping therefore. (Consider installing package grml-live-addons)"
708             ewarn "Boot addons not found, skipping therefore. (Consider installing package grml-live-addons)" ; eend 0
709           else
710             # copy only files so we can handle bsd4grml on its own
711             for file in ${TEMPLATE_DIRECTORY}/boot/addons/* ; do
712               test -f $file && cp $file "$BUILD_OUTPUT"/boot/addons/
713             done
714
715             if [ -n "$NO_ADDONS_BSD4GRML" ] ; then
716                log   "Skipping installation of bsd4grml as requested via \$NO_ADDONS_BSD4GRML."
717                einfo "Skipping installation of bsd4grml as requested via \$NO_ADDONS_BSD4GRML."; eend 0
718             else
719                if [ -d "$TEMPLATE_DIRECTORY"/boot/addons/bsd4grml ] ; then
720                  cp -a ${TEMPLATE_DIRECTORY}/boot/addons/bsd4grml "$BUILD_OUTPUT"/boot/addons/
721                else
722                  log   "bsd4grml addon not found, skipping therefore."
723                  ewarn "bsd4grml addon not found, skipping therefore." ; eend 0
724                fi
725             fi
726
727           fi # no "$TEMPLATE_DIRECTORY"/boot/addons
728        fi # NO_ADDONS
729
730        if ! [ -d ${TEMPLATE_DIRECTORY}/boot/grub ] ; then
731           log   "grub templates do not exist, skipping therefore."
732           ewarn "grub templates do not exist, skipping therefore." ; eend 0
733        else
734           if ! [ -d "${BUILD_OUTPUT}/boot/grub" ] ; then
735              cp -a ${TEMPLATE_DIRECTORY}/boot/grub  "$BUILD_OUTPUT"/boot/
736           fi
737
738           # make sure we have recent template files available, otherwise updating
739           # the strings like $GRML_NAME and $VERSION might be out of date
740           cp ${TEMPLATE_DIRECTORY}/boot/grub/* "$BUILD_OUTPUT"/boot/grub/
741        fi
742
743        if ! [ -d "${TEMPLATE_DIRECTORY}"/GRML ] ; then
744           log    "Error: ${TEMPLATE_DIRECTORY}/GRML does not exist. Exiting."
745           eerror "Error: ${TEMPLATE_DIRECTORY}/GRML does not exist. Exiting." ; eend 1
746           bailout 9
747        fi
748
749        [ -d "$BUILD_OUTPUT"/GRML ] || mkdir "$BUILD_OUTPUT"/GRML
750        cp -a ${TEMPLATE_DIRECTORY}/GRML/* "$BUILD_OUTPUT"/GRML/
751
752        # adjust boot splash information:
753        RELEASE_INFO="$GRML_NAME $VERSION - Release Codename $RELEASENAME"
754        RELEASE_INFO="$(cut_string 68 "$RELEASE_INFO")"
755        RELEASE_INFO="$(extend_string_end 68 "$RELEASE_INFO")"
756
757        if [ -r "$BUILD_OUTPUT"/GRML/grml-version ] ; then
758           sed -i "s/%RELEASE_INFO%/$GRML_NAME $VERSION - $RELEASENAME/" "$BUILD_OUTPUT"/GRML/grml-version
759           sed -i "s/%DATE%/$DATE/"                                      "$BUILD_OUTPUT"/GRML/grml-version
760        fi
761
762        # make sure the squashfs filename is set accordingly:
763        SQUASHFS_NAME="$GRML_NAME.squashfs"
764
765        if [ -n "$NO_BOOTID" ] ; then
766           log   'Skipping bootid feature as requested via $NO_BOOTID.'
767           einfo 'Skipping bootid feature as requested via $NO_BOOTID.'
768        else
769           [ -n "$BOOTID" ] || BOOTID="$(echo ${GRML_NAME}${VERSION} | tr -d ',./;\- ')"
770           [ -d "$BUILD_OUTPUT"/conf ] || mkdir "$BUILD_OUTPUT"/conf
771           einfo "Generating /conf/bootid.txt with entry ${BOOTID}."
772           log   "Generating /conf/bootid.txt with entry ${BOOTID}."
773           echo "$BOOTID" > "$BUILD_OUTPUT"/conf/bootid.txt
774           eend $?
775        fi
776
777        # adjust all variables in the templates with the according distribution information
778        for file in "${BUILD_OUTPUT}"/boot/isolinux/*.cfg "${BUILD_OUTPUT}"/boot/isolinux/*.msg \
779                    "${BUILD_OUTPUT}"/boot/grub/* ; do
780          if [ -r "${file}" ] ; then
781            sed -i "s/%ARCH%/$ARCH/g"                    "${file}"
782            sed -i "s/%DATE%/$DATE/g"                    "${file}"
783            sed -i "s/%DISTRI_INFO%/$DISTRI_INFO/g"      "${file}"
784            sed -i "s/%DISTRI_NAME%/$DISTRI_NAME/g"      "${file}"
785            sed -i "s/%DISTRI_SPLASH%/$DISTRI_SPLASH/g"  "${file}"
786            sed -i "s/%GRML_NAME%/$GRML_NAME/g"          "${file}"
787            sed -i "s/%SQUASHFS_NAME%/$SQUASHFS_NAME/g"  "${file}"
788            sed -i "s/%RELEASE_INFO%/$RELEASE_INFO/g"    "${file}"
789            sed -i "s/%SHORT_NAME%/$SHORT_NAME/g"        "${file}"
790            sed -i "s/%VERSION%/$VERSION/g"              "${file}"
791
792            [ -n "$DEFAULT_BOOTOPTIONS" ] && sed -i "s/ boot=live/ boot=live $DEFAULT_BOOTOPTIONS/"  "${file}"
793
794            if [ -n "$NO_BOOTID" ] ; then
795               sed -i "s/ bootid=%BOOTID%//g" "${file}" # drop bootid bootoption
796            else
797               sed -i "s/%BOOTID%/$BOOTID/g" "${file}" # adjust bootid=... argument
798            fi
799          fi
800        done
801
802        # adjust bootsplash accordingly but make sure the string has the according lenght
803        SQUASHFS_NAME="$(cut_string 20 "$SQUASHFS_NAME")"
804        SQUASHFS_NAME="$(extend_string_end 20 "$SQUASHFS_NAME")"
805        for file in f4 f5 ; do
806           if [ -r "${BUILD_OUTPUT}/boot/isolinux/${file}" ] ; then
807              sed -i "s/%SQUASHFS_NAME%/$SQUASHFS_NAME/" "${BUILD_OUTPUT}/boot/isolinux/${file}"
808              sed -i "s/%SQUASHFS_NAME%/$SQUASHFS_NAME/" "${BUILD_OUTPUT}/boot/isolinux/${file}"
809           fi
810        done
811
812        # generate addon list
813        rm "${BUILD_OUTPUT}/${ADDONS_LIST_FILE}"
814        for name in "${BUILD_OUTPUT}"/boot/isolinux/addon_*.cfg ; do
815          include_name=$(basename "$name")
816          echo "include $include_name"  >> "${BUILD_OUTPUT}/${ADDONS_LIST_FILE}"
817        done
818
819        if ! [ -r "${BUILD_OUTPUT}/boot/isolinux/${DISTRI_NAME}.cfg" ] || [ "$DISTRI_NAME" = "grml" ] ; then
820           log "including grmlmain.cfg in ${BUILD_OUTPUT}/boot/isolinux/distri.cfg"
821           echo "include grmlmain.cfg"    >  "${BUILD_OUTPUT}/boot/isolinux/distri.cfg"
822           echo "include default.cfg"     >  "${BUILD_OUTPUT}/boot/isolinux/grmlmain.cfg"
823           echo "include menuoptions.cfg" >> "${BUILD_OUTPUT}/boot/isolinux/grmlmain.cfg"
824           echo "include grml.cfg"        >> "${BUILD_OUTPUT}/boot/isolinux/grmlmain.cfg"
825
826           for f in "${BUILD_OUTPUT}"/boot/isolinux/submenu*.cfg ; do
827             echo "include $(basename $f)"     >> "${BUILD_OUTPUT}/boot/isolinux/grmlmain.cfg"
828           done
829
830           echo "include options.cfg"     >> "${BUILD_OUTPUT}/boot/isolinux/grmlmain.cfg"
831           if [ ! -n "$NO_ADDONS" ] ; then
832             echo "include addons.cfg"    >> "${BUILD_OUTPUT}/boot/isolinux/grmlmain.cfg"
833           fi
834           echo "include isoprompt.cfg"   >> "${BUILD_OUTPUT}/boot/isolinux/grmlmain.cfg"
835           echo "include hd.cfg"          >> "${BUILD_OUTPUT}/boot/isolinux/grmlmain.cfg"
836           echo "include hidden.cfg"      >> "${BUILD_OUTPUT}/boot/isolinux/grmlmain.cfg"
837        else # assume we are building a custom distribution:
838           log "File ${BUILD_OUTPUT}/boot/isolinux/${DISTRI_NAME}.cfg found, using it."
839           einfo "File ${BUILD_OUTPUT}/boot/isolinux/${DISTRI_NAME}.cfg found, using it."
840           if grep -q "^include ${DISTRI_NAME}.cfg" "${BUILD_OUTPUT}/boot/isolinux/distri.cfg" ; then
841             log "include for ${DISTRI_NAME}.cfg already present, nothing to do."
842             eindent
843             einfo "include for ${DISTRI_NAME}.cfg already present, nothing to do."
844             eoutdent
845             eend $?
846          else
847             log "including ${DISTRI_NAME}.cfg in ${BUILD_OUTPUT}/boot/isolinux/distri.cfg"
848             echo "include ${DISTRI_NAME}.cfg" > "${BUILD_OUTPUT}/boot/isolinux/distri.cfg"
849             [ -n "$NO_ADDONS" ] || echo "include addons.cfg" >> "${BUILD_OUTPUT}/boot/isolinux/distri.cfg"
850           fi
851        fi
852
853        # use old style console based isolinux method only if requested:
854        if [[ "${ISOLINUX_METHOD}" == "console" ]] ; then
855           log 'Using console based isolinux method as requested via $ISOLINUX_METHOD.'
856           einfo 'Using console based isolinux method as requested via $ISOLINUX_METHOD.'
857           if grep -q '^include console.cfg' "${BUILD_OUTPUT}/boot/isolinux/distri.cfg" ; then
858             einfo "include for console.cfg already foud, nothing to do."
859             eend 0
860           else
861             log "including console.cfg in ${BUILD_OUTPUT}/boot/isolinux/isolinux.cfg"
862             einfo "including console.cfg in ${BUILD_OUTPUT}/boot/isolinux/isolinux.cfg"
863             echo "include console.cfg" >> "${BUILD_OUTPUT}/boot/isolinux/isolinux.cfg"
864             eend $?
865           fi
866        else
867           log 'Using graphical boot menu.'
868           if grep -q '^include vesamenu.cfg' "${BUILD_OUTPUT}/boot/isolinux/isolinux.cfg" ; then
869             log "include for vesamenu.cfg already foud, nothing to do."
870           else
871             log "including vesamenu.cfg in ${BUILD_OUTPUT}/boot/isolinux/isolinux.cfg"
872             echo "include vesamenu.cfg" >> "${BUILD_OUTPUT}/boot/isolinux/isolinux.cfg"
873           fi
874        fi
875
876        # jump back to grub from bsd4grml (/boot/grub/stage2):
877        GRUB_LEGACY=stage2
878
879        if [ -e "$BUILD_OUTPUT"/boot/addons/bsd4grml/boot.6 ]; then
880           if [ -e "$BUILD_OUTPUT"/boot/grub/core.img ]; then
881              GRUB_VERSION=2
882           else
883              GRUB_VERSION=1
884           fi
885
886           for file in "$BUILD_OUTPUT"/boot/addons/bsd4grml/boot.6 \
887                       "$BUILD_OUTPUT"/boot/addons/bsd4grml/boot.cfg \
888                       "$BUILD_OUTPUT"/boot/isolinux/*.cfg \
889                       "$BUILD_OUTPUT"/boot/grub/grub.cfg \
890                       "$BUILD_OUTPUT"/boot/grub/menu.lst ; do
891               if [ -e "$file" ] ; then
892                 sed -i -e "s!%GRUB_VERSION%!$GRUB_VERSION!g" \
893                        -e "s!%GRUB_LEGACY%!$GRUB_LEGACY!g" "$file"
894               fi
895           done
896
897           sed -i "s/%RELEASE_INFO%/$GRML_NAME $VERSION - $RELEASENAME/" "$BUILD_OUTPUT"/boot/addons/bsd4grml/boot.6
898        fi
899
900        if [ -e "$BUILD_OUTPUT"/boot/grub/$GRUB_LEGACY ]; then
901           sed -i "s/%GRUB_LEGACY%/$GRUB_LEGACY/g" "$BUILD_OUTPUT"/boot/grub/menu.lst
902           sed -i "s/%GRUB_LEGACY%/$GRUB_LEGACY/g" "$BUILD_OUTPUT"/boot/grub/grub.cfg
903        elif [ -e "$BUILD_OUTPUT"/boot/grub/menu.lst -a -e "$BUILD_OUTPUT"/boot/grub/grub.cfg ] ; then
904           sed -i "/%GRUB_LEGACY%/d" "$BUILD_OUTPUT"/boot/grub/menu.lst
905           sed -i "/%GRUB_LEGACY%/d" "$BUILD_OUTPUT"/boot/grub/grub.cfg
906        fi
907
908        DPKG_LIST="/var/log/fai/$HOSTNAME/last/dpkg.list" # the dpkg --list output of the chroot
909        if ! [ -r "$DPKG_LIST" ] ; then
910           ewarn "$DPKG_LIST could not be read, ignoring to store package information on ISO therefore."
911        else
912           einfo "Storing package list information as /GRML/${GRML_NAME}-packages.txt on ISO."
913           cp "$DPKG_LIST" "${BUILD_OUTPUT}/GRML/${GRML_NAME}-packages.txt"
914           eend $?
915        fi
916
917        # autostart for Windows:
918        if [ -d "${TEMPLATE_DIRECTORY}/windows/autostart/" ] ; then
919           cp ${TEMPLATE_DIRECTORY}/windows/autostart/* "$BUILD_OUTPUT"/
920        fi
921
922        # windows-binaries:
923        if [ -n "$NO_WINDOWS_BINARIES" ] ; then
924           log   "Skipping download of windows binaries as requested via \$NO_WINDOWS_BINARIES."
925           einfo "Skipping download of windows binaries as requested via \$NO_WINDOWS_BINARIES."
926           eend 0
927        else
928           if [ -f "$BUILD_OUTPUT"/windows/putty.exe ] ; then
929              log   "Skipping stage 'WINDOWS_BINARIES' as $BUILD_OUTPUT/windows exists already."
930              ewarn "Skipping stage 'WINDOWS_BINARIES' as $BUILD_OUTPUT/windows exists already." ; eend 0
931           else
932              if ! [ -d "$BUILD_OUTPUT"/windows ] ; then
933                 mkdir "$BUILD_OUTPUT"/windows
934                 ( cd "$BUILD_OUTPUT"/windows
935                   for file in pageant plink pscp psftp putty puttygen ; do
936                      wget -O ${file}.exe ${WINDOWS_BINARIES}/${file}.exe
937                      md5sum ${file}.exe > ${file}.exe.md5
938                   done )
939              fi
940
941              log "Finished execution of stage 'WINDOWS_BINARIES' [$(date)]"
942              einfo "Finished execution of stage 'WINDOWS_BINARIES'" ; eend 0
943           fi
944        fi
945
946     FORCE_ISO_REBUILD=true
947     einfo "Finished execution of stage 'boot'" ; eend 0
948     fi
949   fi # BOOTSTRAP_ONLY
950 else
951    log    'Error: Unsupported ARCH, sorry. Want to support it? Contribute!'
952    eerror 'Error: Unsupported ARCH, sorry. Want to support it? Contribute!' ; eend 1
953    bailout
954 fi
955
956 # support installation of local files into the chroot/ISO
957 if [ -n "$CHROOT_INSTALL" ] ; then
958   if ! [ -d "$CHROOT_INSTALL" ] ; then
959      log "Configuration variable \$CHROOT_INSTALL is set but not a directory; ignoring"
960      ewarn "Configuration variable \$CHROOT_INSTALL is set but not a directory; ignoring"
961   else
962      log "Copying local files to chroot as requested via \$CHROOT_INSTALL"
963      einfo "Copying local files to chroot as requested via \$CHROOT_INSTALL"
964      rsync -avz --inplace "$CHROOT_INSTALL"/ "$CHROOT_OUTPUT/"
965      eend $?
966      einfo "Make sure to run squashfs stage, otherwise your local files won't be part of the ISO."
967      FORCE_ISO_REBUILD=true
968   fi
969 fi
970
971 if [ -f "$BUILD_OUTPUT"/live/${GRML_NAME}.squashfs -a -z "$UPDATE" -a -z "$BUILD_ONLY" -a -z "$BUILD_DIRTY" ] ; then
972    log   "Skipping stage 'squashfs' as $BUILD_OUTPUT/live exists already."
973    ewarn "Skipping stage 'squashfs' as $BUILD_OUTPUT/live exists already." ; eend 0
974 elif [ -n "$SKIP_MKSQUASHFS" ] ; then
975    log   "Skipping stage 'squashfs' as requested via option -q or -N"
976    ewarn "Skipping stage 'squashfs' as requested via option -q or -N" ; eend 0
977 else
978    [ -d "$BUILD_OUTPUT"/live ] || mkdir "$BUILD_OUTPUT"/live
979    # make sure we don't leave (even an empty) base.tgz:
980    [ -f "$CHROOT_OUTPUT/base.tgz" ] && rm -f "$CHROOT_OUTPUT/base.tgz"
981
982    # if unconfigured default to squashfs-tools' mksquashfs binary
983    if [ -z "$SQUASHFS_BINARY" ] ; then
984       SQUASHFS_BINARY='mksquashfs'
985    fi
986
987    if which "$SQUASHFS_BINARY" >/dev/null 2>&1 ; then
988       log    "Using mksquashfs binary ${SQUASHFS_BINARY}"
989       einfo  "Using mksquashfs binary ${SQUASHFS_BINARY}" ; eend 0
990    else
991       log    "Error: mksquashfs binary ($SQUASHFS_BINARY) not found. Exiting."
992       eerror "Error: mksquashfs binary ($SQUASHFS_BINARY) not found. Exiting." ; eend 1
993       bailout
994    fi
995
996    # use sane defaults if $SQUASHFS_OPTIONS isn't set
997    if [ -z "$SQUASHFS_OPTIONS" ] ; then
998      # use blocksize 256k as this gives best result with regards to time + compression
999      SQUASHFS_OPTIONS="-b 256k"
1000
1001      # set lzma/xz compression by default, unless -z option has been specified on command line
1002      if [ -z "$SQUASHFS_ZLIB" ] ; then
1003         SQUASHFS_OPTIONS="$SQUASHFS_OPTIONS -comp xz"
1004      else
1005         SQUASHFS_OPTIONS="$SQUASHFS_OPTIONS -comp gzip"
1006      fi
1007    fi
1008
1009    # support exclusion of files via exclude-file:
1010    if [ -n "$SQUASHFS_EXCLUDES_FILE" -a "$SQUASHFS_EXCLUDES_FILE" ] ; then
1011       SQUASHFS_OPTIONS="$SQUASHFS_OPTIONS -ef $SQUASHFS_EXCLUDES_FILE"
1012    fi
1013
1014    # get rid of unnecessary files when building grml-small for final release:
1015    if echo "$CLASSES" | grep -q GRML_SMALL ; then
1016       SQUASHFS_OPTIONS="$SQUASHFS_OPTIONS -e initrd.img* vmlinuz*"
1017    fi
1018
1019    # log stuff
1020    SQUASHFS_STDERR="$(mktemp -t grml-live.XXXXXX)"
1021
1022    # informational stuff
1023    [ -n "$SQUASHFS_OPTIONS" ]  && SQUASHFS_INFO_MSG="$SQUASHFS_OPTIONS"
1024    [ -n "$SQUASHFS_INFO_MSG" ] && SQUASHFS_INFO_MSG="using options: $SQUASHFS_INFO_MSG"
1025    einfo "Squashfs build information: running binary $SQUASHFS_BINARY $SQUASHFS_INFO_MSG"
1026
1027    log "$SQUASHFS_BINARY $CHROOT_OUTPUT/* $BUILD_OUTPUT/live/${GRML_NAME}.squashfs -noappend $SQUASHFS_OPTIONS"
1028
1029    if $SQUASHFS_BINARY $CHROOT_OUTPUT/* $BUILD_OUTPUT/live/"${GRML_NAME}".squashfs \
1030       -noappend $SQUASHFS_OPTIONS 2>"${SQUASHFS_STDERR}" ; then
1031       echo "${GRML_NAME}.squashfs" > $BUILD_OUTPUT/live/filesystem.module
1032       log "Finished execution of stage 'squashfs' [$(date)]"
1033       einfo "Finished execution of stage 'squashfs'" ; eend 0
1034    else
1035       log    "Error: there was a critical error executing stage 'squashfs' [$(date)]:"
1036       log    "$(cat $SQUASHFS_STDERR)"
1037       eerror "Error: there was a critical error executing stage 'squashfs':"
1038       cat    "${SQUASHFS_STDERR}"
1039       eend 1
1040       bailout
1041    fi
1042
1043    FORCE_ISO_REBUILD=true
1044 fi
1045
1046 # create md5sum file:
1047 if [ -z "$BOOTSTRAP_ONLY" ] ; then
1048   ( cd $BUILD_OUTPUT/GRML &&
1049   find .. -type f -not -name md5sums -not -name isolinux.bin -exec md5sum {} \; > md5sums )
1050 fi
1051 # }}}
1052
1053 # ISO_OUTPUT - mkisofs {{{
1054 [ -n "$ISO_OUTPUT" ] || ISO_OUTPUT="$OUTPUT/grml_isos"
1055 [ -n "$ISO_NAME" ] || ISO_NAME="${GRML_NAME}_${VERSION}.iso"
1056
1057 if [ "$BOOT_METHOD" = "isolinux" ] ; then
1058    BOOT_ARGS="-no-emul-boot -boot-load-size 4 -boot-info-table -b boot/isolinux/isolinux.bin -c boot/isolinux/boot.cat"
1059 elif [ "$BOOT_METHOD" = "grub" ] ; then
1060    BOOT_ARGS="-no-emul-boot -boot-load-size 4 -boot-info-table -b boot/grub/stage2"
1061 elif [ "$BOOT_METHOD" = "grub2" ] ; then
1062    BOOT_ARGS="-no-emul-boot -boot-load-size 4 -b boot/grub/toriboot.bin"
1063 fi
1064
1065 # Just until http://bts.grml.org/grml/issue945 has been resolved.
1066 # HYBRID_METHOD defaults to manifold, so make sure the default works OOTB.
1067 if [[ $BOOT_METHOD != isolinux && ($HYBRID_METHOD = isohybrid || $HYBRID_METHOD = manifold) ]]; then
1068   log   "Setting HYBRID_METHOD to grub2 as hybrid mode does not work with isohybrid yet."
1069   ewarn "Setting HYBRID_METHOD to grub2 as hybrid mode does not work with isohybrid yet."
1070   HYBRID_METHOD='grub2'
1071   eend 0
1072 fi
1073
1074 if [ -f "${ISO_OUTPUT}/${ISO_NAME}" -a -z "$UPDATE" -a -z "$BUILD_ONLY" -a -z "$BUILD_DIRTY" -a "$FORCE_ISO_REBUILD" = "false" ]  ; then
1075    log   "Skipping stage 'iso build' as $ISO_OUTPUT/${ISO_NAME} exists already."
1076    ewarn "Skipping stage 'iso build' as $ISO_OUTPUT/${ISO_NAME} exists already." ; eend 0
1077 elif [ -n "$SKIP_MKISOFS" ] ; then
1078    log   "Skipping stage 'iso build' as requested via option -n or -N"
1079    ewarn "Skipping stage 'iso build' as requested via option -n or -N" ; eend 0
1080 else
1081    mkdir -p "$ISO_OUTPUT" || bailout 6 "Problem with creating $ISO_OUTPUT for stage 'iso build'"
1082
1083    if $FORCE_ISO_REBUILD && ! [ -f "${ISO_OUTPUT}/${ISO_NAME}" ] ; then
1084       log   "Forcing rebuild of ISO because files on ISO have been modified."
1085       einfo "Forcing rebuild of ISO because files on ISO have been modified."
1086    fi
1087
1088    # support mkisofs as well as genisoimage
1089    if which mkisofs >/dev/null 2>&1; then
1090       MKISOFS='mkisofs'
1091    elif which genisoimage >/dev/null 2>&1; then
1092       MKISOFS='genisoimage'
1093    else
1094       log    "Error: neither mkisofs nor genisoimage available - can not create ISO."
1095       eerror "Error: neither mkisofs nor genisoimage available - can not create ISO." ; eend 1
1096       bailout
1097    fi
1098
1099    CURRENT_DIR=$(pwd)
1100    if cd "$BUILD_OUTPUT" ; then
1101       if [ "$BOOT_METHOD" = "grub2" ]; then
1102          # make a 2048-byte bootsector for El Torito
1103          dd if=/dev/zero of=boot/grub/toriboot.bin bs=512 count=4 2>/dev/null
1104          # those are in 2048-byte sectors, so 1 16 matches 4 63 below
1105          echo 1 16 | mksh /usr/share/grml-live/scripts/bootgrub.mksh -B 11 | \
1106             dd of=boot/grub/toriboot.bin conv=notrunc 2>/dev/null
1107       fi
1108       log "$MKISOFS -V '${GRML_NAME} ${VERSION}' -publisher 'grml-live | grml.org' -l -r -J $BOOT_ARGS -o ${ISO_OUTPUT}/${ISO_NAME} ."
1109       "$MKISOFS" -V "${GRML_NAME} ${VERSION}" -publisher 'grml-live | grml.org' \
1110               -l -r -J $BOOT_ARGS -no-pad \
1111               -o "${ISO_OUTPUT}/${ISO_NAME}" . ; RC=$?
1112       # both of these need core.img there, so it’s easier to write it here
1113       if [ "$BOOT_METHOD" = "grub2" ] || [ "$HYBRID_METHOD" = "grub2" ]; then
1114          # must be <= 30720 bytes
1115          dd if=boot/grub/core.img of="${ISO_OUTPUT}/${ISO_NAME}" \
1116            conv=notrunc bs=512 seek=4 2>/dev/null
1117       fi
1118
1119       # pad the output ISO to multiples of 256 KiB for partition table support
1120       siz=$($getfilesize "${ISO_OUTPUT}/${ISO_NAME}")
1121       cyls=$((siz / 512 / 32 / 16 + 1))   # C=$cyls H=16 S=32
1122       siz=$((cyls * 16 * 32 * 512))   # size after padding
1123       dd if=/dev/zero bs=1 count=1 seek=$((siz - 1)) \
1124          of="${ISO_OUTPUT}/${ISO_NAME}" 2>/dev/null
1125
1126       # support disabling hybrid ISO image
1127       if [ "$HYBRID_METHOD" = "disable" ] ; then\
1128          log   "Skipping creation of hybrid ISO file as requested via HYBRID_METHOD=disable"
1129          einfo "Skipping creation of hybrid ISO file as requested via HYBRID_METHOD=disable"
1130          eend 0
1131       # use isohybrid only on request
1132       elif [ "$HYBRID_METHOD" = "isohybrid" ] ; then
1133          if ! which isohybrid >/dev/null 2>&1 ; then
1134            bailout 12 "isohybrid binary not found - please install syslinux/syslinux-common"
1135          else
1136            log "Creating hybrid ISO file with isohybrid method"
1137            einfo "Creating hybrid ISO file with isohybrid method"
1138            # Notes for consideration:
1139            # "-entry 4 -type 1c"
1140            # * using 4 as the partition number is supposed to help with BIOSes
1141            #   that only support USB-Zip boot
1142            # * using 1c (i.e. hidden FAT32 LBA), instead of the default 0x17
1143            #   (hidden NTFS, IIRC), as the partition type is sometimes needed
1144            #   to get the BIOS even look at the partition created by isohybrid
1145            isohybrid "${ISO_OUTPUT}/${ISO_NAME}"
1146            eend $?
1147          fi
1148       # by default use our manifold boot method:
1149       else
1150          # isoinfo is part of both mkisofs and genisoimage so we're good
1151          bootoff=$(isoinfo -l -i "${ISO_OUTPUT}/${ISO_NAME}" | \
1152            sed -n '/^.*\[ *\([0-9]*\)[] ].* ISOLINUX.BIN;1 *$/s//\1/p')
1153          if ! [ -r boot/grub/core.img ] ; then
1154            ewarn "boot/grub/core.img not found, not creating manifold boot ISO file"
1155          elif [ "${bootoff:-0}" -lt 1 ] ; then
1156            ewarn "isolinux.bin not found on the ISO file, disabling manifold boot"
1157          else
1158            log "Creating hybrid ISO file with manifold method"
1159            einfo "Creating hybrid ISO file with manifold method"
1160            if [ "$HYBRID_METHOD" = "grub2" ] ; then
1161                # 512 bytes: MBR, partition table, load GRUB 2
1162                echo 4 63 | mksh /usr/share/grml-live/scripts/bootgrub.mksh -A -M 4:0x96 -g $cyls:16:32
1163            else
1164               # read only one but 2048-byte sized (scale: << 2) sector
1165               echo $bootoff $bootoff | \
1166                  mksh /usr/share/grml-live/scripts/bootilnx.mksh -A -M 4:0x96 -g $cyls:16:32 -S 2
1167            fi | dd of="${ISO_OUTPUT}/${ISO_NAME}" conv=notrunc 2>/dev/null
1168            eend $?
1169          fi
1170       fi
1171
1172       # generate md5sum and sha1sum of ISO if we are using class 'RELEASE':
1173       case $CLASSES in *RELEASE*)
1174          [ "$RC" = 0 ] && \
1175          (
1176            if cd $ISO_OUTPUT ; then
1177              md5sum ${ISO_NAME} > ${ISO_NAME}.md5 && \
1178              touch -r ${ISO_NAME} ${ISO_NAME}.md5
1179              sha1sum ${ISO_NAME} > ${ISO_NAME}.sha1 && \
1180              touch -r ${ISO_NAME} ${ISO_NAME}.sha1
1181            fi
1182          )
1183          ;;
1184       esac
1185
1186       cd "$CURRENT_DIR"
1187    fi
1188
1189    if [ "$RC" = 0 ] ; then
1190       log   "Finished execution of stage 'iso build' [$(date)]"
1191       einfo "Finished execution of stage 'iso build'" ; eend 0
1192    else
1193       log    "Error: there was a critical error ($RC) executing stage 'iso build' [$(date)]"
1194       eerror "Error: there was a critical error executing stage 'iso build'" ; eend 1
1195       bailout $RC
1196    fi
1197 fi
1198 # }}}
1199
1200 # log build information to database if grml-live-db is installed and enabled {{{
1201 dpkg_to_db() {
1202 if [ -d /usr/share/grml-live-db ] ; then
1203
1204   # safe defaults
1205   DPKG_LIST="/var/log/fai/$HOSTNAME/last/dpkg.list" # the dpkg --list output of the chroot:
1206   [ -n "$DPKG_DATABASE" ]  || DPKG_DATABASE=/var/log/grml-live.db
1207   [ -n "$DPKG_DBSCRIPT" ]  || DPKG_DBSCRIPT=/usr/share/grml-live-db/scripts/dpkg-to-db
1208   [ -n "$DPKG_DBOPTIONS" ] || DPKG_DBOPTIONS="--database $DPKG_DATABASE --logfile $LOGFILE --flavour $GRML_NAME --dpkg $DPKG_LIST"
1209
1210   if ! [ -x "$DPKG_DBSCRIPT" ] ; then
1211     log "Error: $DPKG_DBSCRIPT is not executable, can not log dpkg information."
1212     eerror "Error: $DPKG_DBSCRIPT is not executable, can not log dpkg information." ; eend 1
1213     bailout 14
1214   fi
1215
1216   # disable by default for now, not sure whether really everyone is using a local db file
1217   #if ! touch "$DPKG_DATABASE" ; then
1218   #  eerror "Error: can not write to ${DPKG_DATABASE}, can not log dpkg information." ; eend 1
1219   #  bailout 14
1220   #fi
1221
1222   if ! [ -r "$DPKG_LIST" ] ; then
1223      log   "Warning: can not read $DPKG_LIST - can not provide information to $DPKG_DBSCRIPT (dirty build?)"
1224      ewarn "Warning: can not read $DPKG_LIST - can not provide information to $DPKG_DBSCRIPT (dirty build?)" ; eend 0
1225   else
1226      einfo "Logging $DPKG_LIST to database $DPKG_DATABASE"
1227      log "Logging $DPKG_LIST to database $DPKG_DATABASE"
1228      log "Executing $DPKG_DBSCRIPT $DPKG_DBOPTIONS"
1229      eindent
1230
1231      if DB_INFO=$("$DPKG_DBSCRIPT" $DPKG_DBOPTIONS 2>&1) ; then
1232        einfo "$DB_INFO"
1233        eend 0
1234      else
1235        eerror "$DB_INFO"
1236        eend 1
1237      fi
1238
1239      eoutdent
1240   fi
1241
1242 fi
1243 }
1244 # }}}
1245
1246 # finalize {{{
1247 [ -n "$start_seconds" ] && SECONDS="$[$(cut -d . -f 1 /proc/uptime)-$start_seconds]" || SECONDS="unknown"
1248 log "Successfully finished execution of $PN [$(date) - running ${SECONDS} seconds]"
1249
1250 dpkg_to_db # make sure we catch the last log line as well, therefore execute between log + einfo
1251
1252 einfo "Successfully finished execution of $PN [$(date) - running ${SECONDS} seconds]" ; eend 0
1253 bailout 0
1254 # }}}
1255
1256 ## END OF FILE #################################################################
1257 # vim:foldmethod=marker ts=2 ft=sh ai expandtab tw=80 sw=3