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