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