Implement -D option to set configuration directory; fai.conf: don't set variables...
[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 ADDONS_LIST_FILE='/boot/isolinux/addons_list.cfg'
31 # }}}
32
33 # usage information {{{
34 usage()
35 {
36   echo "
37 $PN - build process script for generating a (grml based) Linux Live-ISO
38
39 Usage: $PN [options, see as follows]
40
41    -a <architecture>       architecture; available values: i386 and amd64
42    -b                      build the ISO without updating the chroot via FAI
43    -B                      build the ISO without touching the chroot (skips cleanup)
44    -c <classe[s]>          classes to be used for building the ISO via FAI
45    -C <configfile>         configuration file for grml-live
46    -d <date>               use specified date instead of build time as date of release
47    -D <configdir>          use specified configuration directory instead of /etc/grml/fai
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: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     D) GRML_FAI_CONFIG="$OPTARG" ;;
240     g) GRML_NAME="$OPTARG" ;;
241     i) ISO_NAME="$OPTARG" ;;
242     I) CHROOT_INSTALL="$OPTARG" ;;
243     n) SKIP_MKISOFS=1 ;;
244     N) BOOTSTRAP_ONLY=1; SKIP_MKISOFS=1; SKIP_MKSQUASHFS=1 ;;
245     o) OUTPUT="$OPTARG" ;;
246     q) SKIP_MKSQUASHFS=1 ;;
247     r) RELEASENAME="$OPTARG" ;;
248     s) SUITE="$OPTARG" ;;
249     t) TEMPLATE_DIRECTORY="$OPTARG";;
250     v) VERSION="$OPTARG" ;;
251     F) FORCE=1 ;;
252     u) UPDATE=1 ;;
253     V) VERBOSE="-v" ;;
254     z) SQUASHFS_ZLIB=1 ;;
255     ?) echo "invalid option -$OPTARG" >&2; bailout 1 ;;
256   esac
257 done
258 shift $(($OPTIND - 1))  # set ARGV to the first not parsed commandline parameter
259 # }}}
260
261 # assume sane defaults (if not set already) {{{
262 [ -n "$ARCH" ]                    || ARCH="$(dpkg --print-architecture)"
263 [ -n "$BOOT_METHOD" ]             || BOOT_METHOD='isolinux'
264 [ -n "$CLASSES" ]                 || CLASSES="GRMLBASE,GRML_MEDIUM,I386"
265 [ -n "$DATE" ]                    || DATE="$(date +%Y-%m-%d)"
266 [ -n "$DISTRI_INFO" ]             || DISTRI_INFO='Grml - Live Linux for system administrators   '
267 [ -n "$DISTRI_NAME" ]             || DISTRI_NAME="grml"
268 [ -n "$DISTRI_SPLASH" ]           || DISTRI_SPLASH='grml.png'
269 [ -n "$FORCE_ISO_REBUILD" ]       || FORCE_ISO_REBUILD="false"
270 [ -n "$GRML_FAI_CONFIG" ]         || GRML_FAI_CONFIG='/etc/grml/fai'
271 [ -n "$GRML_NAME" ]               || GRML_NAME='grml'
272 [ -n "$HOSTNAME" ]                || HOSTNAME='grml'
273 [ -n "$HYBRID_METHOD" ]           || HYBRID_METHOD='manifold'
274 [ -n "$NFSROOT_CONF" ]            || NFSROOT_CONF="${GRML_FAI_CONFIG}/make-fai-nfsroot.conf"
275 [ -n "$RELEASENAME" ]             || RELEASENAME='grml-live rocks'
276 [ -n "$SOURCES_LIST_INPUT" ]      || SOURCES_LIST_INPUT="${GRML_FAI_CONFIG}/apt/sources.list"
277 [ -n "$SOURCES_LIST_OUTPUT" ]     || SOURCES_LIST_OUTPUT="${GRML_FAI_CONFIG}/config/files/etc/apt/sources.list/GRMLBASE"
278 [ -n "$SQUASHFS_EXCLUDES_FILE " ] || SQUASHFS_EXCLUDES_FILE="${GRML_FAI_CONFIG}/config/grml/squashfs-excludes"
279 [ -n "$SUITE" ]                   || SUITE='squeeze'
280 [ -n "$TEMPLATE_DIRECTORY" ]      || TEMPLATE_DIRECTORY='/usr/share/grml-live/templates'
281 [ -n "$USERNAME" ]                || USERNAME='grml'
282 [ -n "$VERSION" ]                 || VERSION='0.0.1'
283
284 # output specific stuff, depends on $OUTPUT (iff not set):
285 [ -n "$OUTPUT" ]           || OUTPUT='/grml/grml-live'
286 [ -n "$BUILD_OUTPUT" ]     || BUILD_OUTPUT="$OUTPUT/grml_cd"
287 [ -n "$CHROOT_OUTPUT" ]    || CHROOT_OUTPUT="$OUTPUT/grml_chroot"
288 [ -n "$ISO_OUTPUT" ]       || ISO_OUTPUT="$OUTPUT/grml_isos"
289 # }}}
290
291 # some misc checks before executing FAI {{{
292 [ -n "$CLASSES" ] || bailout 1 "Error: \$CLASSES unset, please set it in $LIVE_CONF or
293 specify it on the command line using the -c option."
294 [ -n "$OUTPUT" ] || bailout 1 "Error: \$OUTPUT unset, please set it in $LIVE_CONF or
295 specify it on the command line using the -o option."
296
297 # trim characters that are known to cause problems inside $GRML_NAME;
298 # for example isolinux does not like '-' inside the directory name
299 [ -n "$GRML_NAME" ] && export SHORT_NAME="$(echo $GRML_NAME | tr -d ',./;\- ')"
300
301 # export variables to have them available in fai scripts:
302 [ -n "$GRML_NAME" ]   && export GRML_NAME="$GRML_NAME"
303 [ -n "$RELEASENAME" ] && export RELEASENAME="$RELEASENAME"
304 # }}}
305
306 # ZERO_LOGFILE - check for backwards compatibility reasons {{{
307 # this was default behaviour until grml-live 0.9.34:
308 if [ -n "$ZERO_LOGFILE" ] ; then
309    PRESERVE_LOGFILE='' # make sure it's cleaned then
310    ewarn "Please consider disabling the \$ZERO_LOGFILE option as grml-live clears..."
311    ewarn "... the logfile $LOGFILE by default (unless \$PRESERVE_LOGFILE is set) nowadays."
312    eend 0
313 fi
314 # }}}
315
316 # ask user whether the setup is ok {{{
317 if [ -z "$FORCE" ] ; then
318    echo
319    echo "${PN} [${GRML_LIVE_VERSION}]: check your configuration (or use -F to force execution):"
320    echo
321    echo "  FAI classes:       $CLASSES"
322    [ -r "$LOCAL_CONFIG" ]        && echo "  Local config:      /etc/grml/grml-live.local"
323    [ -n "$CONFIG" ]              && echo "  Configuration:     $CONFIG"
324    [ -n "$GRML_FAI_CONFIG" ]     && echo "  Config directory:  $GRML_FAI_CONFIG"
325    echo "  main directory:    $OUTPUT"
326    [ -n "$CHROOT_OUTPUT" ]       && echo "  Chroot target:     $CHROOT_OUTPUT"
327    [ -n "$BUILD_OUTPUT" ]        && echo "  Build target:      $BUILD_OUTPUT"
328    [ -n "$ISO_OUTPUT" ]          && echo "  ISO target:        $ISO_OUTPUT"
329    [ -n "$GRML_NAME" ]           && echo "  Grml name:         $GRML_NAME"
330    [ -n "$RELEASENAME" ]         && echo "  Release name:      $RELEASENAME"
331    [ -n "$DATE" ]                && echo "  Build date:        $DATE"
332    [ -n "$VERSION" ]             && echo "  Grml version:      $VERSION"
333    [ -n "$SUITE" ]               && echo "  Debian suite:      $SUITE"
334    [ -n "$ARCH" ]                && echo "  Architecture:      $ARCH"
335    [ -n "$BOOT_METHOD" ]         && echo "  Boot method:       $BOOT_METHOD"
336    [ -n "$HYBRID_METHOD" ]       && echo "  Hybrid method:     $HYBRID_METHOD"
337    [ -n "$TEMPLATE_DIRECTORY" ]  && echo "  Template files:    $TEMPLATE_DIRECTORY"
338    [ -n "$CHROOT_INSTALL" ]      && echo "  Install files from directory to chroot:  $CHROOT_INSTALL"
339    [ -n "$BOOTID" ]              && echo "  Boot identifier:   $BOOTID"
340    [ -n "$NO_BOOTID" ]           && echo "  Skipping bootid feature."
341    [ -n "$DEFAULT_BOOTOPTIONS" ] && echo "  Adding default bootoptions: \"$DEFAULT_BOOTOPTIONS\""
342    [ -n "$FAI_ARGS" ]            && echo "  Additional arguments for FAI: $FAI_ARGS"
343    [ -n "$LOGFILE" ]             && echo "  Logging to file:   $LOGFILE"
344    [ -n "$SQUASHFS_ZLIB" ]       && echo "  Using ZLIB (instead of LZMA/XZ) compression."
345    [ -n "$SQUASHFS_OPTIONS" ]    && echo "  Using SQUASHFS_OPTIONS ${SQUASHFS_OPTIONS}"
346    [ -n "$VERBOSE" ]             && echo "  Using VERBOSE mode."
347    [ -n "$UPDATE" ]              && echo "  Executing UPDATE instead of fresh installation."
348    if [ -n "$BOOTSTRAP_ONLY" ] ; then
349      echo "  Bootstrapping only and not building (files for) ISO."
350    else
351      [ -n "$SKIP_MKSQUASHFS" ]     && echo "  Skipping creation of SQUASHFS file."
352      [ -n "$SKIP_MKISOFS" ]        && echo "  Skipping creation of ISO file."
353      [ -n "$BUILD_ONLY" ]          && echo "  Executing BUILD_ONLY instead of fresh installation or UPDATE."
354      [ -n "$BUILD_DIRTY" ]         && echo "  Executing BUILD_DIRTY to leave chroot untouched."
355    fi
356    echo
357    echo -n "Is this ok for you? [y/N] "
358    read a
359    if ! [ "$a" = 'y' -o "$a" = 'Y' ] ; then
360       bailout 1 "Exiting as requested."
361    fi
362    echo
363 fi
364 # }}}
365
366 # clean/zero/remove logfiles {{{
367
368 if [ -n "$PRESERVE_LOGFILE" ] ; then
369    echo "Preserving logfile $LOGFILE as requested via \$PRESERVE_LOGFILE"
370 else
371    # make sure it is empty (as it is e.g. appended to grml-live-db)
372    echo -n > $LOGFILE
373 fi
374
375 if [ -n "$ZERO_FAI_LOGFILE" ] ; then
376    if [ -d /var/log/fai/"$HOSTNAME" ] ; then
377       rm -rf /var/log/fai/"$HOSTNAME"/"$(readlink /var/log/fai/"$HOSTNAME"/last)"
378       rm -rf /var/log/fai/"$HOSTNAME"/"$(readlink /var/log/fai/"$HOSTNAME"/last-dirinstall)"
379       rm -rf /var/log/fai/"$HOSTNAME"/"$(readlink /var/log/fai/"$HOSTNAME"/last-softupdate)"
380       rm -f /var/log/fai/"$HOSTNAME"/last \
381             /var/log/fai/"$HOSTNAME"/last-dirinstall \
382             /var/log/fai/"$HOSTNAME"/last-softupdate
383    fi
384 fi
385 # }}}
386
387 # source config and startup {{{
388 if [ -n "$CONFIG" ] ; then
389    if ! [ -f "$CONFIG" ] ; then
390       log    "Error: $CONFIG could not be read. Exiting. [$(date)]"
391       eerror "Error: $CONFIG could not be read. Exiting." ; eend 1
392       bailout 1
393    else
394       log "Sourcing $CONFIG"
395       . $CONFIG
396    fi
397 fi
398
399 start_seconds=$(cut -d . -f 1 /proc/uptime)
400 log "------------------------------------------------------------------------------"
401 log "Starting grml-live [${GRML_LIVE_VERSION}] run on $(date)"
402 log "Executed grml-live command line:"
403 log "$CMDLINE"
404
405 einfo "Logging actions to logfile $LOGFILE"
406 # }}}
407
408 # on-the-fly configuration {{{
409 mkdir -p "$(dirname $SOURCES_LIST_OUTPUT)" # might not be present in -D config space
410
411 cat > "$SOURCES_LIST_OUTPUT" << EOF
412 # NOTE: This file is *NOT* meant for manual customisation! This file is
413 # modified by grml-live and any changes will be overridden.
414 # You might consider using GRML_LIVE_SOURCES in /etc/grml/grml-live.conf*
415 # or FAI's fcopy command with ${GRML_FAI_CONFIG}/config/files instead!
416 EOF
417
418 if [ -n "$MIRROR_DIRECTORY" ] ; then
419   if ! [ -d "$MIRROR_DIRECTORY/debian" ] ; then
420     log    "Error: $MIRROR_DIRECTORY/debian does not seem to exist. Exiting. [$(date)]"
421     eerror "Error: $MIRROR_DIRECTORY/debian does not seem to exist. Exiting." ; eend 1
422     bailout 1
423   fi
424   echo "$MIRROR_SOURCES" >> "$SOURCES_LIST_OUTPUT"
425 fi
426
427 if [ -n "$GRML_LIVE_SOURCES" ] ; then
428   echo "$GRML_LIVE_SOURCES" >> "$SOURCES_LIST_OUTPUT"
429 fi
430
431 if [ -n "$FAI_DEBOOTSTRAP" ] ; then
432   sed "s#^FAI_DEBOOTSTRAP=.*#FAI_DEBOOTSTRAP=\"$FAI_DEBOOTSTRAP\"#" "$NFSROOT_CONF" | sponge "$NFSROOT_CONF"
433 fi
434
435 # does this suck? YES!
436 # /usr/share/debootstrap/scripts/unstable does not exist, instead use 'sid':
437 case $SUITE in
438    unstable) SUITE='sid' ;;
439    # make sure that we *NEVER* write any broken suite name to sources.list,
440    # otherwise we won't be able to adjust it one next (correct) execution
441    stable)   ;;
442    testing)  ;;
443    etch)     ;;
444    lenny)    ;;
445    squeeze)  ;;
446    wheezy)   ;;
447    sid)      ;;
448    *) echo "Sorry, $SUITE is not a valid Debian suite, exiting.">&2; bailout 1 ;;
449 esac
450
451 DIST=" etch\| stable\| lenny\| squeeze\| wheezy\| testing\| sid\| unstable"
452 sed "s/\(^deb .\+\)\([ \t]*\)\($DIST\)\([ \t]*\)\(main \)/\1 \2$SUITE\4\5/" "$SOURCES_LIST_INPUT" | sponge "$SOURCES_LIST_OUTPUT"
453 for file in "$LIVE_CONF" "$CONFIG" "$LOCAL_CONFIG" ; do
454     if [ -n "$file" ] ; then
455        sed "s/^SUITE=.*/SUITE=\"$SUITE\"/" $file | sponge $file
456        sed "s/\(^deb .\+\)\([ \t]*\)\($DIST\)\([ \t]*\)\(main \)/\1 \2$SUITE\4\5/" "$file" | sponge "$file"
457     fi
458 done
459
460 # notice: activate grml-live pool only if we are building against unstable:
461 if grep -qwe unstable -qwe sid "$SOURCES_LIST_INPUT" ; then
462    grep -q 'grml-live.*main' "$SOURCES_LIST_OUTPUT" || \
463    grep grml-stable "$SOURCES_LIST_OUTPUT" | \
464         sed 's/grml-stable/grml-live/' >> "$SOURCES_LIST_OUTPUT"
465 else
466    grep -q 'grml-live.*main' "$SOURCES_LIST_INPUT" && \
467    sed 's/.*grml-live.*/# removed grml-live repository/' "$SOURCES_LIST_INPUT" | sponge "$SOURCES_LIST_OUTPUT"
468 fi
469
470 for file in "$LIVE_CONF" "$CONFIG" "$LOCAL_CONFIG" "$NFSROOT_CONF" ; do
471     if [ -n "$file" ] ; then
472        sed "s|^FAI_DEBOOTSTRAP=\"[a-z]* |FAI_DEBOOTSTRAP=\"$SUITE |" "$file" | sponge "$file"
473     fi
474 done
475
476 # validate whether the specified architecture class matches the
477 # architecture (option), otherwise installation of kernel will fail
478 if echo $CLASSES | grep -qi i386 ; then
479    if ! [[ "$ARCH" == "i386" ]] ; then
480       log    "Error: You specified the I386 class but are trying to build something else (AMD64?)."
481       eerror "Error: You specified the I386 class but are trying to build something else (AMD64?)."
482       eerror "Tip:   Either invoke grml-live with '-a i386' or adjust the architecture class. Exiting."
483       eend 1
484       bailout
485    fi
486 elif echo $CLASSES | grep -qi amd64 ; then
487    if ! [[ "$ARCH" == "amd64" ]] ; then
488       log    "Error: You specified the AMD64 class but are trying to build something else (I386?)."
489       eerror "Error: You specified the AMD64 class but are trying to build something else (I386?)."
490       eerror "Tip:   Either invoke grml-live with '-a amd64' or adjust the architecture class. Exiting."
491       eend 1
492       bailout
493    fi
494 fi
495
496 if grep -q -- 'FAI_DEBOOTSTRAP_OPTS.*--arch' "$NFSROOT_CONF" ; then
497    sed "s/--arch [a-z0-9]* /--arch $ARCH /" "$NFSROOT_CONF" | sponge "$NFSROOT_CONF"
498 else
499    sed "s|^FAI_DEBOOTSTRAP_OPTS=\"\(.*\)|FAI_DEBOOTSTRAP_OPTS=\"--arch $ARCH \1|" "$NFSROOT_CONF" | sponge "$NFSROOT_CONF"
500 fi
501 # }}}
502
503 # CHROOT_OUTPUT - execute FAI {{{
504 if [ -n "$BUILD_DIRTY" ]; then
505    log   "Skipping stage 'fai' as requested via option -B"
506    ewarn "Skipping stage 'fai' as requested via option -B" ; eend 0
507 else
508    [ -n "$CHROOT_OUTPUT" ] || CHROOT_OUTPUT="$OUTPUT/grml_chroot"
509
510    # provide inform fai about the ISO we build
511    [ -d "$CHROOT_OUTPUT/etc/" ] || mkdir -p "$CHROOT_OUTPUT/etc/"
512    echo '# This file has been generated by grml-live.' > "$CHROOT_OUTPUT/etc/grml_live_version"
513    [ -n "$GRML_LIVE_VERSION" ] && echo "GRML_LIVE_VERSION=$GRML_LIVE_VERSION" >> "$CHROOT_OUTPUT/etc/grml_live_version"
514    [ -n "$SUITE" ] && echo "SUITE=$SUITE" >> "$CHROOT_OUTPUT/etc/grml_live_version"
515
516    if [ -n "$UPDATE" -o -n "$BUILD_ONLY" ] ; then
517       FAI_ACTION=softupdate
518    else
519       FAI_ACTION=dirinstall
520    fi
521
522    if [ -n "$UPDATE" -o -n "$BUILD_ONLY" ] ; then
523       if ! [ -r "$CHROOT_OUTPUT/etc/debian_version" ] ; then
524          log    "Error: does not look like you have a working chroot. Updating/building not possible."
525          eerror "Error: does not look like you have a working chroot. Updating/building not possible. (Drop -u/-b option?)"
526          eend 1
527          bailout 20
528       fi
529    fi
530
531    if [ -d "$CHROOT_OUTPUT/bin" -a -z "$UPDATE" -a -z "$BUILD_ONLY" ] ; then
532       log   "Skipping stage 'fai dirinstall' as $CHROOT_OUTPUT exists already."
533       ewarn "Skipping stage 'fai dirinstall' as $CHROOT_OUTPUT exists already." ; eend 0
534    else
535       mkdir -p "$CHROOT_OUTPUT" || bailout 5 "Problem with creating $CHROOT_OUTPUT for FAI"
536
537       if [ -n "${MIRROR_DIRECTORY}" ] ; then
538          mkdir -p "${CHROOT_OUTPUT}/${MIRROR_DIRECTORY}"
539          mount --bind "${MIRROR_DIRECTORY}" "${CHROOT_OUTPUT}/${MIRROR_DIRECTORY}"
540       fi
541
542       # tell dpkg to use "unsafe io" during the build
543       [ -d "$CHROOT_OUTPUT/etc/dpkg/dpkg.cfg.d" ] || mkdir -p "$CHROOT_OUTPUT/etc/dpkg/dpkg.cfg.d"
544       echo force-unsafe-io > "$CHROOT_OUTPUT/etc/dpkg/dpkg.cfg.d/unsafe-io"
545
546       log "Executed FAI command line:"
547       log "BUILD_ONLY=$BUILD_ONLY BOOTSTRAP_ONLY=$BOOTSTRAP_ONLY fai $VERBOSE -C $GRML_FAI_CONFIG -s file:///$GRML_FAI_CONFIG/config -c$CLASSES -u $HOSTNAME $FAI_ACTION $CHROOT_OUTPUT $FAI_ARGS"
548       BUILD_ONLY="$BUILD_ONLY" BOOTSTRAP_ONLY="$BOOTSTRAP_ONLY" fai $VERBOSE \
549                   -C "$GRML_FAI_CONFIG" -s "file:///$GRML_FAI_CONFIG/config" -c"$CLASSES" \
550                   -u "$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     FORCE_ISO_REBUILD=true
923     einfo "Finished execution of stage 'boot'" ; eend 0
924     fi
925   fi # BOOTSTRAP_ONLY
926 else
927    log    'Error: Unsupported ARCH, sorry. Want to support it? Contribute!'
928    eerror 'Error: Unsupported ARCH, sorry. Want to support it? Contribute!' ; eend 1
929    bailout
930 fi
931
932 # support installation of local files into the chroot/ISO
933 if [ -n "$CHROOT_INSTALL" ] ; then
934   if ! [ -d "$CHROOT_INSTALL" ] ; then
935      log "Configuration variable \$CHROOT_INSTALL is set but not a directory; ignoring"
936      ewarn "Configuration variable \$CHROOT_INSTALL is set but not a directory; ignoring"
937   else
938      log "Copying local files to chroot as requested via \$CHROOT_INSTALL"
939      einfo "Copying local files to chroot as requested via \$CHROOT_INSTALL"
940      rsync -avz --inplace "$CHROOT_INSTALL"/ "$CHROOT_OUTPUT/"
941      eend $?
942      einfo "Make sure to run squashfs stage, otherwise your local files won't be part of the ISO."
943      FORCE_ISO_REBUILD=true
944   fi
945 fi
946
947 if [ -f "$BUILD_OUTPUT"/live/${GRML_NAME}.squashfs -a -z "$UPDATE" -a -z "$BUILD_ONLY" -a -z "$BUILD_DIRTY" ] ; then
948    log   "Skipping stage 'squashfs' as $BUILD_OUTPUT/live exists already."
949    ewarn "Skipping stage 'squashfs' as $BUILD_OUTPUT/live exists already." ; eend 0
950 elif [ -n "$SKIP_MKSQUASHFS" ] ; then
951    log   "Skipping stage 'squashfs' as requested via option -q or -N"
952    ewarn "Skipping stage 'squashfs' as requested via option -q or -N" ; eend 0
953 else
954    [ -d "$BUILD_OUTPUT"/live ] || mkdir "$BUILD_OUTPUT"/live
955    # make sure we don't leave (even an empty) base.tgz:
956    [ -f "$CHROOT_OUTPUT/base.tgz" ] && rm -f "$CHROOT_OUTPUT/base.tgz"
957
958    # if unconfigured default to squashfs-tools' mksquashfs binary
959    if [ -z "$SQUASHFS_BINARY" ] ; then
960       SQUASHFS_BINARY='mksquashfs'
961    fi
962
963    if which "$SQUASHFS_BINARY" >/dev/null 2>&1 ; then
964       log    "Using mksquashfs binary ${SQUASHFS_BINARY}"
965       einfo  "Using mksquashfs binary ${SQUASHFS_BINARY}" ; eend 0
966    else
967       log    "Error: mksquashfs binary ($SQUASHFS_BINARY) not found. Exiting."
968       eerror "Error: mksquashfs binary ($SQUASHFS_BINARY) not found. Exiting." ; eend 1
969       bailout
970    fi
971
972    # use sane defaults if $SQUASHFS_OPTIONS isn't set
973    if [ -z "$SQUASHFS_OPTIONS" ] ; then
974      # use blocksize 256k as this gives best result with regards to time + compression
975      SQUASHFS_OPTIONS="-b 256k"
976
977      # set lzma/xz compression by default, unless -z option has been specified on command line
978      if [ -z "$SQUASHFS_ZLIB" ] ; then
979         SQUASHFS_OPTIONS="$SQUASHFS_OPTIONS -comp xz"
980      else
981         SQUASHFS_OPTIONS="$SQUASHFS_OPTIONS -comp gzip"
982      fi
983    fi
984
985    # support exclusion of files via exclude-file:
986    if [ -n "$SQUASHFS_EXCLUDES_FILE" -a "$SQUASHFS_EXCLUDES_FILE" ] ; then
987       SQUASHFS_OPTIONS="$SQUASHFS_OPTIONS -ef $SQUASHFS_EXCLUDES_FILE"
988    fi
989
990    # get rid of unnecessary files when building grml-small for final release:
991    if echo "$CLASSES" | grep -q GRML_SMALL ; then
992       SQUASHFS_OPTIONS="$SQUASHFS_OPTIONS -e initrd.img* vmlinuz*"
993    fi
994
995    # log stuff
996    SQUASHFS_STDERR="$(mktemp -t grml-live.XXXXXX)"
997
998    # informational stuff
999    [ -n "$SQUASHFS_OPTIONS" ]  && SQUASHFS_INFO_MSG="$SQUASHFS_OPTIONS"
1000    [ -n "$SQUASHFS_INFO_MSG" ] && SQUASHFS_INFO_MSG="using options: $SQUASHFS_INFO_MSG"
1001    einfo "Squashfs build information: running binary $SQUASHFS_BINARY $SQUASHFS_INFO_MSG"
1002
1003    log "$SQUASHFS_BINARY $CHROOT_OUTPUT/* $BUILD_OUTPUT/live/${GRML_NAME}.squashfs -noappend $SQUASHFS_OPTIONS"
1004
1005    if $SQUASHFS_BINARY $CHROOT_OUTPUT/* $BUILD_OUTPUT/live/"${GRML_NAME}".squashfs \
1006       -noappend $SQUASHFS_OPTIONS 2>"${SQUASHFS_STDERR}" ; then
1007       echo "${GRML_NAME}.squashfs" > $BUILD_OUTPUT/live/filesystem.module
1008       log "Finished execution of stage 'squashfs' [$(date)]"
1009       einfo "Finished execution of stage 'squashfs'" ; eend 0
1010    else
1011       log    "Error: there was a critical error executing stage 'squashfs' [$(date)]:"
1012       log    "$(cat $SQUASHFS_STDERR)"
1013       eerror "Error: there was a critical error executing stage 'squashfs':"
1014       cat    "${SQUASHFS_STDERR}"
1015       eend 1
1016       bailout
1017    fi
1018
1019    FORCE_ISO_REBUILD=true
1020 fi
1021
1022 # create md5sum file:
1023 if [ -z "$BOOTSTRAP_ONLY" ] ; then
1024   ( cd $BUILD_OUTPUT/GRML &&
1025   find .. -type f -not -name md5sums -not -name isolinux.bin -exec md5sum {} \; > md5sums )
1026 fi
1027 # }}}
1028
1029 # ISO_OUTPUT - mkisofs {{{
1030 [ -n "$ISO_OUTPUT" ] || ISO_OUTPUT="$OUTPUT/grml_isos"
1031 [ -n "$ISO_NAME" ] || ISO_NAME="${GRML_NAME}_${VERSION}.iso"
1032
1033 if [ "$BOOT_METHOD" = "isolinux" ] ; then
1034    BOOT_ARGS="-no-emul-boot -boot-load-size 4 -boot-info-table -b boot/isolinux/isolinux.bin -c boot/isolinux/boot.cat"
1035 elif [ "$BOOT_METHOD" = "grub" ] ; then
1036    BOOT_ARGS="-no-emul-boot -boot-load-size 4 -boot-info-table -b boot/grub/stage2"
1037 elif [ "$BOOT_METHOD" = "grub2" ] ; then
1038    BOOT_ARGS="-no-emul-boot -boot-load-size 4 -b boot/grub/toriboot.bin"
1039 fi
1040
1041 # Just until http://bts.grml.org/grml/issue945 has been resolved.
1042 # HYBRID_METHOD defaults to manifold, so make sure the default works OOTB.
1043 if [[ $BOOT_METHOD != isolinux && ($HYBRID_METHOD = isohybrid || $HYBRID_METHOD = manifold) ]]; then
1044   log   "Setting HYBRID_METHOD to grub2 as hybrid mode does not work with isohybrid yet."
1045   ewarn "Setting HYBRID_METHOD to grub2 as hybrid mode does not work with isohybrid yet."
1046   HYBRID_METHOD='grub2'
1047   eend 0
1048 fi
1049
1050 if [ -f "${ISO_OUTPUT}/${ISO_NAME}" -a -z "$UPDATE" -a -z "$BUILD_ONLY" -a -z "$BUILD_DIRTY" -a "$FORCE_ISO_REBUILD" = "false" ]  ; then
1051    log   "Skipping stage 'iso build' as $ISO_OUTPUT/${ISO_NAME} exists already."
1052    ewarn "Skipping stage 'iso build' as $ISO_OUTPUT/${ISO_NAME} exists already." ; eend 0
1053 elif [ -n "$SKIP_MKISOFS" ] ; then
1054    log   "Skipping stage 'iso build' as requested via option -n or -N"
1055    ewarn "Skipping stage 'iso build' as requested via option -n or -N" ; eend 0
1056 else
1057    mkdir -p "$ISO_OUTPUT" || bailout 6 "Problem with creating $ISO_OUTPUT for stage 'iso build'"
1058
1059    if $FORCE_ISO_REBUILD && ! [ -f "${ISO_OUTPUT}/${ISO_NAME}" ] ; then
1060       log   "Forcing rebuild of ISO because files on ISO have been modified."
1061       einfo "Forcing rebuild of ISO because files on ISO have been modified."
1062    fi
1063
1064    # support mkisofs as well as genisoimage
1065    if which mkisofs >/dev/null 2>&1; then
1066       MKISOFS='mkisofs'
1067    elif which genisoimage >/dev/null 2>&1; then
1068       MKISOFS='genisoimage'
1069    else
1070       log    "Error: neither mkisofs nor genisoimage available - can not create ISO."
1071       eerror "Error: neither mkisofs nor genisoimage available - can not create ISO." ; eend 1
1072       bailout
1073    fi
1074
1075    CURRENT_DIR=$(pwd)
1076    if cd "$BUILD_OUTPUT" ; then
1077       if [ "$BOOT_METHOD" = "grub2" ]; then
1078          # make a 2048-byte bootsector for El Torito
1079          dd if=/dev/zero of=boot/grub/toriboot.bin bs=512 count=4 2>/dev/null
1080          # those are in 2048-byte sectors, so 1 16 matches 4 63 below
1081          echo 1 16 | mksh /usr/share/grml-live/scripts/bootgrub.mksh -B 11 | \
1082             dd of=boot/grub/toriboot.bin conv=notrunc 2>/dev/null
1083       fi
1084       log "$MKISOFS -V '${GRML_NAME} ${VERSION}' -publisher 'grml-live | grml.org' -l -r -J $BOOT_ARGS -o ${ISO_OUTPUT}/${ISO_NAME} ."
1085       "$MKISOFS" -V "${GRML_NAME} ${VERSION}" -publisher 'grml-live | grml.org' \
1086               -l -r -J $BOOT_ARGS -no-pad \
1087               -o "${ISO_OUTPUT}/${ISO_NAME}" . ; RC=$?
1088       # both of these need core.img there, so it’s easier to write it here
1089       if [ "$BOOT_METHOD" = "grub2" ] || [ "$HYBRID_METHOD" = "grub2" ]; then
1090          # must be <= 30720 bytes
1091          dd if=boot/grub/core.img of="${ISO_OUTPUT}/${ISO_NAME}" \
1092            conv=notrunc bs=512 seek=4 2>/dev/null
1093       fi
1094
1095       # pad the output ISO to multiples of 256 KiB for partition table support
1096       siz=$($getfilesize "${ISO_OUTPUT}/${ISO_NAME}")
1097       cyls=$((siz / 512 / 32 / 16 + 1))   # C=$cyls H=16 S=32
1098       siz=$((cyls * 16 * 32 * 512))   # size after padding
1099       dd if=/dev/zero bs=1 count=1 seek=$((siz - 1)) \
1100          of="${ISO_OUTPUT}/${ISO_NAME}" 2>/dev/null
1101
1102       # support disabling hybrid ISO image
1103       if [ "$HYBRID_METHOD" = "disable" ] ; then\
1104          log   "Skipping creation of hybrid ISO file as requested via HYBRID_METHOD=disable"
1105          einfo "Skipping creation of hybrid ISO file as requested via HYBRID_METHOD=disable"
1106          eend 0
1107       # use isohybrid only on request
1108       elif [ "$HYBRID_METHOD" = "isohybrid" ] ; then
1109          if ! which isohybrid >/dev/null 2>&1 ; then
1110            bailout 12 "isohybrid binary not found - please install syslinux/syslinux-common"
1111          else
1112            log "Creating hybrid ISO file with isohybrid method"
1113            einfo "Creating hybrid ISO file with isohybrid method"
1114            # Notes for consideration:
1115            # "-entry 4 -type 1c"
1116            # * using 4 as the partition number is supposed to help with BIOSes
1117            #   that only support USB-Zip boot
1118            # * using 1c (i.e. hidden FAT32 LBA), instead of the default 0x17
1119            #   (hidden NTFS, IIRC), as the partition type is sometimes needed
1120            #   to get the BIOS even look at the partition created by isohybrid
1121            isohybrid "${ISO_OUTPUT}/${ISO_NAME}"
1122            eend $?
1123          fi
1124       # by default use our manifold boot method:
1125       else
1126          # isoinfo is part of both mkisofs and genisoimage so we're good
1127          bootoff=$(isoinfo -l -i "${ISO_OUTPUT}/${ISO_NAME}" | \
1128            sed -n '/^.*\[ *\([0-9]*\)[] ].* ISOLINUX.BIN;1 *$/s//\1/p')
1129          if ! [ -r boot/grub/core.img ] ; then
1130            ewarn "boot/grub/core.img not found, not creating manifold boot ISO file"
1131          elif [ "${bootoff:-0}" -lt 1 ] ; then
1132            ewarn "isolinux.bin not found on the ISO file, disabling manifold boot"
1133          else
1134            log "Creating hybrid ISO file with manifold method"
1135            einfo "Creating hybrid ISO file with manifold method"
1136            if [ "$HYBRID_METHOD" = "grub2" ] ; then
1137                # 512 bytes: MBR, partition table, load GRUB 2
1138                echo 4 63 | mksh /usr/share/grml-live/scripts/bootgrub.mksh -A -M 4:0x96 -g $cyls:16:32
1139            else
1140               # read only one but 2048-byte sized (scale: << 2) sector
1141               echo $bootoff $bootoff | \
1142                  mksh /usr/share/grml-live/scripts/bootilnx.mksh -A -M 4:0x96 -g $cyls:16:32 -S 2
1143            fi | dd of="${ISO_OUTPUT}/${ISO_NAME}" conv=notrunc 2>/dev/null
1144            eend $?
1145          fi
1146       fi
1147
1148       # generate md5sum and sha1sum of ISO if we are using class 'RELEASE':
1149       case $CLASSES in *RELEASE*)
1150          [ "$RC" = 0 ] && \
1151          (
1152            if cd $ISO_OUTPUT ; then
1153              md5sum ${ISO_NAME} > ${ISO_NAME}.md5 && \
1154              touch -r ${ISO_NAME} ${ISO_NAME}.md5
1155              sha1sum ${ISO_NAME} > ${ISO_NAME}.sha1 && \
1156              touch -r ${ISO_NAME} ${ISO_NAME}.sha1
1157            fi
1158          )
1159          ;;
1160       esac
1161
1162       cd "$CURRENT_DIR"
1163    fi
1164
1165    if [ "$RC" = 0 ] ; then
1166       log   "Finished execution of stage 'iso build' [$(date)]"
1167       einfo "Finished execution of stage 'iso build'" ; eend 0
1168    else
1169       log    "Error: there was a critical error ($RC) executing stage 'iso build' [$(date)]"
1170       eerror "Error: there was a critical error executing stage 'iso build'" ; eend 1
1171       bailout $RC
1172    fi
1173 fi
1174 # }}}
1175
1176 # log build information to database if grml-live-db is installed and enabled {{{
1177 dpkg_to_db() {
1178 if [ -d /usr/share/grml-live-db ] ; then
1179
1180   # safe defaults
1181   DPKG_LIST="/var/log/fai/$HOSTNAME/last/dpkg.list" # the dpkg --list output of the chroot:
1182   [ -n "$DPKG_DATABASE" ]  || DPKG_DATABASE=/var/log/grml-live.db
1183   [ -n "$DPKG_DBSCRIPT" ]  || DPKG_DBSCRIPT=/usr/share/grml-live-db/scripts/dpkg-to-db
1184   [ -n "$DPKG_DBOPTIONS" ] || DPKG_DBOPTIONS="--database $DPKG_DATABASE --logfile $LOGFILE --flavour $GRML_NAME --dpkg $DPKG_LIST"
1185
1186   if ! [ -x "$DPKG_DBSCRIPT" ] ; then
1187     log "Error: $DPKG_DBSCRIPT is not executable, can not log dpkg information."
1188     eerror "Error: $DPKG_DBSCRIPT is not executable, can not log dpkg information." ; eend 1
1189     bailout 14
1190   fi
1191
1192   # disable by default for now, not sure whether really everyone is using a local db file
1193   #if ! touch "$DPKG_DATABASE" ; then
1194   #  eerror "Error: can not write to ${DPKG_DATABASE}, can not log dpkg information." ; eend 1
1195   #  bailout 14
1196   #fi
1197
1198   if ! [ -r "$DPKG_LIST" ] ; then
1199      log   "Warning: can not read $DPKG_LIST - can not provide information to $DPKG_DBSCRIPT (dirty build?)"
1200      ewarn "Warning: can not read $DPKG_LIST - can not provide information to $DPKG_DBSCRIPT (dirty build?)" ; eend 0
1201   else
1202      einfo "Logging $DPKG_LIST to database $DPKG_DATABASE"
1203      log "Logging $DPKG_LIST to database $DPKG_DATABASE"
1204      log "Executing $DPKG_DBSCRIPT $DPKG_DBOPTIONS"
1205      eindent
1206
1207      if DB_INFO=$("$DPKG_DBSCRIPT" $DPKG_DBOPTIONS 2>&1) ; then
1208        einfo "$DB_INFO"
1209        eend 0
1210      else
1211        eerror "$DB_INFO"
1212        eend 1
1213      fi
1214
1215      eoutdent
1216   fi
1217
1218 fi
1219 }
1220 # }}}
1221
1222 # finalize {{{
1223 [ -n "$start_seconds" ] && SECONDS="$[$(cut -d . -f 1 /proc/uptime)-$start_seconds]" || SECONDS="unknown"
1224 log "Successfully finished execution of $PN [$(date) - running ${SECONDS} seconds]"
1225
1226 dpkg_to_db # make sure we catch the last log line as well, therefore execute between log + einfo
1227
1228 einfo "Successfully finished execution of $PN [$(date) - running ${SECONDS} seconds]" ; eend 0
1229 bailout 0
1230 # }}}
1231
1232 ## END OF FILE #################################################################
1233 # vim:foldmethod=marker ts=2 ft=sh ai expandtab tw=80 sw=2