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