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