Release new version 0.25.0
[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 # avoid leaking into chroots
16 unset TMPDIR
17
18 # define function getfilesize before "set -e"
19 if stat --help >/dev/null 2>&1; then
20   getfilesize='stat -c %s'  # GNU stat
21 else
22   getfilesize='stat -f %z'  # BSD stat
23 fi
24
25 # exit on any error:
26 # disable for now since it seems to cause some problems
27 # set -e
28
29 # The line following this line is patched by debian/rules.
30 GRML_LIVE_VERSION='***UNRELEASED***'
31
32 # global variables
33 PN="$(basename $0)"
34 CMDLINE="$0 $@"
35 ADDONS_LIST_FILE='/boot/isolinux/addons_list.cfg'
36 # }}}
37
38 # usage information {{{
39 usage()
40 {
41   echo "
42 $PN - build process script for generating a (grml based) Linux Live-ISO
43
44 Usage: $PN [options, see as follows]
45
46    -a <architecture>       architecture; available values: i386 and amd64
47    -A                      clean build directories before and after running
48    -b                      build the ISO without updating the chroot via FAI
49    -B                      build the ISO without touching the chroot (skips cleanup)
50    -c <classe[s]>          classes to be used for building the ISO via FAI
51    -C <configfile>         configuration file for grml-live
52    -d <date>               use specified date instead of build time as date of release
53    -D <configdir>          use specified configuration directory instead of /etc/grml/fai
54    -e <iso_name>           extract ISO and squashfs contents from iso_name
55    -F                      force execution without prompting
56    -g <grml_name>          set the grml flavour name
57    -h                      display short usage information and exit
58    -i <iso_name>           name of ISO
59    -I <src_directory>      directory which provides files that should become
60                            part of the chroot/ISO
61    -n                      skip generation of ISO
62    -N                      bootstrap (build chroot) only, do not create files for ISO
63    -o <output_directory>   main output directory of the build process
64    -q                      skip mksquashfs
65    -Q                      skip netboot package build
66    -r <release_name>       release name
67    -s <suite>              Debian suite/release, like: stable, testing, unstable
68    -S <script_directory>   place of scripts (defaults to /usr/share/grml-live/scripts)
69    -t <template_directory> place of the templates
70    -u                      update existing chroot instead of rebuilding it from scratch
71    -U <username>           arrange output to be owned by specified username
72    -v <version_number>     specify version number of the release
73    -V                      increase verbosity in the build process
74    -z                      use ZLIB instead of LZMA/XZ compression
75
76 Usage examples:
77
78     $PN
79     $PN -c GRMLBASE,GRML_FULL,AMD64 -o /dev/shm/grml
80     $PN -c GRMLBASE,GRML_FULL,AMD64 -i grml_0.0-1.iso -v 0.0-1
81     $PN -c GRMLBASE,GRML_FULL,AMD64 -s stable -V -r 'grml-ftw'
82
83 More details: man grml-live + /usr/share/doc/grml-live/grml-live.html
84               http://grml.org/grml-live/
85
86 Please send your bug reports and feedback to the grml-team: http://grml.org/bugs/
87 "
88    [ "$(id -u 2>/dev/null)" != 0 ] && echo "Please notice that this script requires root permissions."
89 }
90
91 # make sure it's possible to get usage information without being
92 # root or actually executing the script
93 if [ "$1" = '-h' -o "$1" = '--help' ] ; then
94    usage
95    exit 0
96 fi
97 # }}}
98
99 # some runtime checks {{{
100 # we need root permissions for the build-process:
101 if [ "$(id -u 2>/dev/null)" != 0 ] ; then
102    echo "Error: please run this script with uid 0 (root)." >&2
103    exit 1
104 fi
105
106 if [ -r /var/run/fai/FAI_INSTALLATION_IN_PROGRESS ] ; then
107    echo "/usr/sbin/fai already running or was aborted before.">&2
108    echo "You may remove /var/run/fai/FAI_INSTALLATION_IN_PROGRESS and try again.">&2
109    exit 1
110 fi
111
112 # see #449236
113 if [ -r /var/run/fai/fai_softupdate_is_running ] ; then
114    echo "/usr/sbin/fai softupdate already running or was aborted before.">&2
115    echo "You may remove /var/run/fai/fai_softupdate_is_running and try again.">&2
116    exit 1
117 fi
118 # }}}
119
120 # lsb-functions and configuration stuff {{{
121 # make sure they are not set by default
122 VERBOSE=''
123 FORCE=''
124 UPDATE=''
125 BUILD_ONLY=''
126 BUILD_DIRTY=''
127 BOOTSTRAP_ONLY=''
128 HOSTNAME=''
129 USERNAME=''
130 CONFIGDUMP=''
131
132 # don't use colors/escape sequences
133 if [ -r /lib/lsb/init-functions ] ; then
134   . /lib/lsb/init-functions
135   ! log_use_fancy_output && NOCOLORS=true
136 fi
137
138 if [ -r /etc/grml/lsb-functions ] ; then
139    . /etc/grml/lsb-functions
140 else
141    einfo()  { echo "  [*] $*" ;}
142    eerror() { echo "  [!] $*">&2 ;}
143    ewarn()  { echo "  [x] $*" ;}
144    eend()   { return 0 ;}
145    eindent()  { return 0 ;}
146    eoutdent() { return 0 ;}
147 fi
148
149 # source main configuration file:
150 [ -z "$LIVE_CONF" ] && LIVE_CONF='/etc/grml/grml-live.conf'
151 if ! [ -r "$LIVE_CONF" ] ; then
152   ewarn "Configuration file $LIVE_CONF can not be read, ignoring"
153 else
154   einfo "Sourcing configuration file $LIVE_CONF"
155   . $LIVE_CONF
156   eend $?
157 fi
158 # }}}
159
160 # umount all directories {{{
161 umount_all() {
162    # make sure we don't leave any mounts - FAI doesn't remove them always
163    umount $CHROOT_OUTPUT/proc/sys/fs/binfmt_misc 2>/dev/null || /bin/true
164    umount $CHROOT_OUTPUT/proc 2>/dev/null || /bin/true
165    umount $CHROOT_OUTPUT/run  2>/dev/null || /bin/true
166    umount $CHROOT_OUTPUT/sys  2>/dev/null || /bin/true
167    umount $CHROOT_OUTPUT/dev/pts 2>/dev/null || /bin/true
168    umount $CHROOT_OUTPUT/dev 2>/dev/null || /bin/true
169
170    if [ -n "$EXTRACT_ISO_NAME" ] ; then
171      umount "$EXTRACT_ISO_NAME" 2>/dev/null || /bin/true
172    fi
173
174    # certain FAI versions sadly leave a ramdisk behind, so better safe than sorry
175    if [ -x /usr/lib/fai/mkramdisk ] ; then
176      /usr/lib/fai/mkramdisk -u "$(readlink -f ${CHROOT_OUTPUT}/var/lib/dpkg)" >/dev/null 2>&1 || /bin/true
177    fi
178
179    umount "${CHROOT_OUTPUT}/grml-live/sources/" 2>/dev/null || /bin/true
180    [ -n "$MIRROR_DIRECTORY" ] && umount "${CHROOT_OUTPUT}/${MIRROR_DIRECTORY}"
181 }
182 # }}}
183
184 # clean exit {{{
185 bailout() {
186   rm -f /var/run/fai/fai_softupdate_is_running \
187         /var/run/fai/FAI_INSTALLATION_IN_PROGRESS
188   [ -n "$CONFIGDUMP"      ]  && rm -f  "$CONFIGDUMP"
189   [ -n "$SQUASHFS_STDERR" ]  && rm -rf "$SQUASHFS_STDERR"
190   umount_all
191   [ -n "$1" ] && EXIT="$1" || EXIT="1"
192   [ -n "$2" ] && eerror "$2">&2
193   if [ -n "$CLEAN_ARTIFACTS" ]; then
194     log "Cleaning up"
195     einfo "Cleaning up"
196     [ -n "${BUILD_OUTPUT}"  -a -d "${BUILD_OUTPUT}"  ] && rm -r "${BUILD_OUTPUT}"
197     [ -n "${CHROOT_OUTPUT}" -a -d "${CHROOT_OUTPUT}" ] && rm -r "${CHROOT_OUTPUT}"
198     eend 0
199   fi
200
201   # get rid of automatically generated conffiles
202   rm -f ${GRML_FAI_CONFIG}/nfsroot.conf
203   rm -f ${GRML_FAI_CONFIG}/make-fai-nfsroot.conf
204
205   if [ -n "$CHOWN_USER" ]; then
206     log "Setting ownership"
207     einfo "Setting ownership"
208     [ -n "${OUTPUT}"         -a -d "${OUTPUT}"         ] && chown -R "${CHOWN_USER}:" "${OUTPUT}"
209     [ -n "${BUILD_OUTPUT}"   -a -d "${BUILD_OUTPUT}"   ] && chown -R "${CHOWN_USER}:" "${BUILD_OUTPUT}"
210     [ -n "${CHROOT_OUTPUT}"  -a -d "${CHROOT_OUTPUT}"  ] && chown -R "${CHOWN_USER}:" "${CHROOT_OUTPUT}"
211     [ -n "${ISO_OUTPUT}"     -a -d "${ISO_OUTPUT}"     ] && chown -R "${CHOWN_USER}:" "${ISO_OUTPUT}"
212     [ -n "${LOG_OUTPUT}"     -a -d "${LOG_OUTPUT}"     ] && chown -R "${CHOWN_USER}:" "${LOG_OUTPUT}"
213     [ -n "${NETBOOT}"        -a -d "${NETBOOT}"        ] && chown -R "${CHOWN_USER}:" "${NETBOOT}"
214     eend 0
215   fi
216   log "------------------------------------------------------------------------------"
217   exit "$EXIT"
218 }
219 trap bailout 1 2 3 3 6 9 14 15
220 trap umount_all EXIT
221 # }}}
222
223 # some important functions {{{
224
225 # log output:
226 # usage: log "string to log"
227 log() { [ -n "$LOGFILE" ] && echo "$*" >> $LOGFILE ; }
228
229 # cut string at character number int = $1
230 # usage: cut_string 5 "1234567890" will output "12345"
231 cut_string() {
232   [ -n "$2" ] || return 1
233   echo "$2" | head -c "$1"; echo -ne "\n"
234 }
235
236 # prepend int = $1 spaces before string = $2
237 # usage: extend_string_begin 5 "123" will output "  123"
238 extend_string_begin() {
239   [ -n "$2" ] || return 1
240   local COUNT="$(echo $2 | wc -c)"
241   local FILL="$(expr $COUNT - $1)"
242   while [ "$FILL" -gt 1 ] ; do
243     echo -n " "
244     local FILL=$(expr $FILL - 1)
245   done
246   while [ "$FILL" -lt 1 ] ; do
247     echo -n " "
248     local FILL=$(expr $FILL + 1)
249   done
250   echo "$2" | head -c "$1"; echo -ne "\n"
251 }
252
253 # append int = $1 spaces to string = $2
254 # usage: extend_string_begin 5 "123" will output "123  "
255 extend_string_end() {
256   [ -n "$2" ] || return 1
257   echo -n "$2" | head -c "$1"
258   local COUNT="$(echo $2 | wc -c)"
259   local FILL="$(expr $COUNT - $1)"
260   while [ "$FILL" -gt 1 ] ; do
261     echo -n " "
262     local FILL=$(expr $FILL - 1)
263   done
264   while [ "$FILL" -lt 1 ] ; do
265     echo -n " "
266     local FILL=$(expr $FILL + 1)
267   done
268   echo -ne "\n"
269 }
270
271 # Copy addonfile $1 from either
272 #   * the chroot (via $2, the system path),
273 #   * or from TEMPLATE_DIRECTORY/compat (if exists),
274 #   * or from the host system (again, using $2),
275 # or warn about the missing file.
276 #
277 # This is because:
278 #   * We assume that the chroot always has a "good" version of
279 #     the file. Also it makes sources handling easier.
280 #   * On unstable, we Recommend the Debian packages containing
281 #     these files. The user can override them by putting his
282 #     "better" version into the chroot.
283 #   * On stable, the Debian packages are probably not available,
284 #     or outdated, so we look in TEMPLATE_DIRECTORY/compat first, where
285 #     our grml-live-compat package installs current file versions.
286 copy_addon_file() {
287   DEST="${BUILD_OUTPUT}/boot/$3"
288   if [ ! -d "${DEST}/" ]; then
289     mkdir -p "${DEST}"
290   fi
291   if [ -e "$CHROOT_OUTPUT/$2/$1" ]; then
292     log   "Copying $1 from chroot"
293     cp "$CHROOT_OUTPUT/$2/$1" "${DEST}/"
294     return $?
295   fi
296   if [ -e "${TEMPLATE_DIRECTORY}/compat/$3/$1" ]; then
297     log   "Copying $1 from grml-live-compat"
298     cp "${TEMPLATE_DIRECTORY}/compat/$3/$1" "${DEST}/"
299     return $?
300   fi
301   if [ -e "$2/$1" ]; then
302     log   "Copying $1 from system"
303     cp "$2/$1" "${DEST}/"
304     return $?
305   fi
306
307   msg="Missing addon file: \"$1\""
308   ewarn "$msg" ; eend 1
309   log "copy_addon_file: $msg"
310 }
311 # }}}
312
313 # command line parsing {{{
314 while getopts "a:C:c:d:D:e:g:i:I:o:r:s:S:t:U:v:AbBFhnNqQuVz" opt; do
315   case "$opt" in
316     a) ARCH="$OPTARG" ;;
317     A) CLEAN_ARTIFACTS=1 ;;
318     b) BUILD_ONLY=1 ;;
319     B) BUILD_DIRTY=1 ;;
320     c) CLASSES="$OPTARG" ;;
321     C) LOCAL_CONFIG="$(readlink -f $OPTARG)" ;;
322     d) DATE="$OPTARG" ;;
323     D) GRML_FAI_CONFIG="$(readlink -f $OPTARG)" ;;
324     e) EXTRACT_ISO_NAME="$(readlink -f $OPTARG)" ;;
325     g) GRML_NAME="$OPTARG" ;;
326     h) usage ; bailout 0 ;;
327     i) ISO_NAME="$OPTARG" ;;
328     I) CHROOT_INSTALL="$OPTARG" ;;
329     n) SKIP_MKISOFS=1 ;;
330     N) BOOTSTRAP_ONLY=1; SKIP_MKISOFS=1; SKIP_MKSQUASHFS=1 ;;
331     o) OUTPUT="$(readlink -f $OPTARG)" ;;
332     q) SKIP_MKSQUASHFS=1 ;;
333     Q) SKIP_NETBOOT=1 ;;
334     r) RELEASENAME="$OPTARG" ;;
335     s) SUITE="$OPTARG" ;;
336     S) SCRIPTS_DIRECTORY="$OPTARG";;
337     t) TEMPLATE_DIRECTORY="$OPTARG";;
338     v) VERSION="$OPTARG" ;;
339     F) FORCE=1 ;;
340     u) UPDATE=1 ;;
341     U) CHOWN_USER="$OPTARG" ;;
342     V) VERBOSE="-v" ;;
343     z) SQUASHFS_ZLIB=1 ;;
344     ?) echo "invalid option -$OPTARG" >&2; usage; bailout 1 ;;
345   esac
346 done
347 shift $(($OPTIND - 1))  # set ARGV to the first not parsed commandline parameter
348
349 if [ -n "$1" ] ; then
350   echo "Error: unknown argument '$1' in options. Exiting to avoid possible data loss." >&2
351   bailout 1
352 fi
353 # }}}
354
355 # read local (non-packaged) configuration {{{
356 if [ -z "$LOCAL_CONFIG" ]; then
357   if [ -r "/etc/grml/grml-live.local" ]; then
358     LOCAL_CONFIG="/etc/grml/grml-live.local"
359   fi
360 fi
361 if [ -n "$LOCAL_CONFIG" ]; then
362   if [ -r "$LOCAL_CONFIG" ]; then
363     . $LOCAL_CONFIG
364   else
365     eerror "Could not read specified local configuration file \"$LOCAL_CONFIG\"."
366     bailout 1
367   fi
368   LOCAL_CONFIG=$(readlink -f "$LOCAL_CONFIG")
369 else
370   LOCAL_CONFIG=''
371 fi
372
373 if [ -n "${GRML_LIVE_SOURCES:-}" ] ; then
374   eerror "Config variable \$GRML_LIVE_SOURCES is set. This variable has been deprecated."
375   ewarn  "Please set up \${GRML_FAI_CONFIG}/config/files/etc/apt/sources.list.d/* instead."
376   bailout 1
377 fi
378 # }}}
379
380 # assume sane defaults (if not set already) {{{
381 [ -n "$ARCH" ]                    || ARCH="$(dpkg --print-architecture)"
382 [ -n "$BOOT_METHOD" ]             || BOOT_METHOD='isolinux'
383 [ -n "$CLASSES" ]                 || CLASSES="GRMLBASE,GRML_FULL,$(echo ${ARCH} | tr 'a-z' 'A-Z')"
384 [ -n "$DATE" ]                    || DATE="$(date +%Y-%m-%d)"
385 [ -n "$DISTRI_INFO" ]             || DISTRI_INFO='Grml - Live Linux for system administrators'
386 [ -n "$DISTRI_NAME" ]             || DISTRI_NAME="grml"
387 [ -n "$DISTRI_SPLASH" ]           || DISTRI_SPLASH='grml.png'
388 [ -n "$FORCE_ISO_REBUILD" ]       || FORCE_ISO_REBUILD="false"
389 [ -n "$GRML_FAI_CONFIG" ]         || GRML_FAI_CONFIG='/etc/grml/fai'
390 [ -n "$GRML_NAME" ]               || GRML_NAME='grml'
391 [ -n "$HOSTNAME" ]                || HOSTNAME='grml'
392 [ -n "$HYBRID_METHOD" ]           || HYBRID_METHOD='isohybrid'
393 [ -n "$RELEASENAME" ]             || RELEASENAME='grml-live rocks'
394 [ -n "$SQUASHFS_EXCLUDES_FILE" ]  || SQUASHFS_EXCLUDES_FILE="${GRML_FAI_CONFIG}/config/grml/squashfs-excludes"
395 [ -n "$SUITE" ]                   || SUITE='testing'
396 [ -n "$TEMPLATE_DIRECTORY" ]      || TEMPLATE_DIRECTORY='/usr/share/grml-live/templates'
397 [ -n "$SCRIPTS_DIRECTORY" ]       || SCRIPTS_DIRECTORY='/usr/share/grml-live/scripts'
398 [ -n "$USERNAME" ]                || USERNAME='grml'
399 [ -n "$VERSION" ]                 || VERSION='0.0.1'
400
401 # output specific stuff, depends on $OUTPUT (iff not set):
402 [ -n "$OUTPUT" ]           || OUTPUT='/grml/grml-live'
403 [ -n "$BUILD_OUTPUT" ]     || BUILD_OUTPUT="$OUTPUT/grml_cd"
404 [ -n "$CHROOT_OUTPUT" ]    || CHROOT_OUTPUT="$OUTPUT/grml_chroot"
405 [ -n "$ISO_OUTPUT" ]       || ISO_OUTPUT="$OUTPUT/grml_isos"
406 [ -n "$LOG_OUTPUT" ]       || LOG_OUTPUT="$OUTPUT/grml_logs"
407 [ -n "$REPORTS" ]          || REPORTS="${LOG_OUTPUT}/reports/"
408 [ -n "$NETBOOT" ]          || NETBOOT="${OUTPUT}/netboot/"
409 # }}}
410
411 # some misc checks before executing FAI {{{
412 [ -n "$CLASSES" ] || bailout 1 "Error: \$CLASSES unset, please set it in $LIVE_CONF or
413 specify it on the command line using the -c option."
414 [ -n "$OUTPUT" ] || bailout 1 "Error: \$OUTPUT unset, please set it in $LIVE_CONF or
415 specify it on the command line using the -o option."
416
417 # trim characters that are known to cause problems inside $GRML_NAME;
418 # for example isolinux does not like '-' inside the directory name
419 [ -n "$GRML_NAME" ] && export SHORT_NAME="$(echo $GRML_NAME | tr -d ',./;\- ')"
420
421 # export variables to have them available in fai scripts:
422 [ -n "$GRML_NAME" ]   && export GRML_NAME="$GRML_NAME"
423 [ -n "$RELEASENAME" ] && export RELEASENAME="$RELEASENAME"
424 # }}}
425
426 # ZERO_LOGFILE - check for backwards compatibility reasons {{{
427 # this was default behaviour until grml-live 0.9.34:
428 if [ -n "$ZERO_LOGFILE" ] ; then
429    PRESERVE_LOGFILE='' # make sure it's cleaned then
430    ewarn "Please consider disabling the \$ZERO_LOGFILE option as grml-live clears..."
431    ewarn "... the logfile $LOGFILE by default (unless \$PRESERVE_LOGFILE is set) nowadays."
432    eend 0
433 fi
434 # }}}
435
436 # ask user whether the setup is ok {{{
437 if [ -z "$FORCE" ] ; then
438    echo
439    echo "${PN} [${GRML_LIVE_VERSION}]: check your configuration (or use -F to force execution):"
440    echo
441    echo "  FAI classes:       $CLASSES"
442    [ -n "$LOCAL_CONFIG" ]        && echo "  Configuration:     $LOCAL_CONFIG"
443    [ -n "$GRML_FAI_CONFIG" ]     && echo "  Config directory:  $GRML_FAI_CONFIG"
444    echo "  main directory:    $OUTPUT"
445    [ -n "$EXTRACT_ISO_NAME" ]    && echo "  Extract ISO:       $EXTRACT_ISO_NAME"
446    [ -n "$CHROOT_OUTPUT" ]       && echo "  Chroot target:     $CHROOT_OUTPUT"
447    [ -n "$BUILD_OUTPUT" ]        && echo "  Build target:      $BUILD_OUTPUT"
448    [ -n "$ISO_OUTPUT" ]          && echo "  ISO target:        $ISO_OUTPUT"
449    [ -n "$GRML_NAME" ]           && echo "  Grml name:         $GRML_NAME"
450    [ -n "$RELEASENAME" ]         && echo "  Release name:      $RELEASENAME"
451    [ -n "$DATE" ]                && echo "  Build date:        $DATE"
452    [ -n "$VERSION" ]             && echo "  Grml version:      $VERSION"
453    [ -n "$SUITE" ]               && echo "  Debian suite:      $SUITE"
454    [ -n "$ARCH" ]                && echo "  Architecture:      $ARCH"
455    [ -n "$BOOT_METHOD" ]         && echo "  Boot method:       $BOOT_METHOD"
456    [ -n "$HYBRID_METHOD" ]       && echo "  Hybrid method:     $HYBRID_METHOD"
457    [ -n "$TEMPLATE_DIRECTORY" ]  && echo "  Template files:    $TEMPLATE_DIRECTORY"
458    [ -n "$CHROOT_INSTALL" ]      && echo "  Install files from directory to chroot:  $CHROOT_INSTALL"
459    [ -n "$BOOTID" ]              && echo "  Boot identifier:   $BOOTID"
460    [ -n "$NO_BOOTID" ]           && echo "  Skipping bootid feature."
461    [ -n "$CHOWN_USER" ]          && echo "  Output owner:      $CHOWN_USER"
462    [ -n "$DEFAULT_BOOTOPTIONS" ] && echo "  Adding default bootoptions: \"$DEFAULT_BOOTOPTIONS\""
463    [ -n "$FAI_ARGS" ]            && echo "  Additional arguments for FAI: $FAI_ARGS"
464    [ -n "$LOGFILE" ]             && echo "  Logging to file:   $LOGFILE"
465    [ -n "$SQUASHFS_ZLIB" ]       && echo "  Using ZLIB (instead of LZMA/XZ) compression."
466    [ -n "$SQUASHFS_OPTIONS" ]    && echo "  Using SQUASHFS_OPTIONS ${SQUASHFS_OPTIONS}"
467    [ -n "$VERBOSE" ]             && echo "  Using VERBOSE mode."
468    [ -n "$CLEAN_ARTIFACTS" ]     && echo "  Will clean output before and after running."
469    [ -n "$UPDATE" ]              && echo "  Executing UPDATE instead of fresh installation."
470    if [ -n "$BOOTSTRAP_ONLY" ] ; then
471      echo "  Bootstrapping only and not building (files for) ISO."
472    else
473      [ -n "$SKIP_MKSQUASHFS" ]     && echo "  Skipping creation of SQUASHFS file."
474      [ -n "$SKIP_NETBOOT" ]        && echo "  Skipping creation of NETBOOT package."
475      [ -n "$SKIP_MKISOFS" ]        && echo "  Skipping creation of ISO file."
476      [ -n "$BUILD_ONLY" ]          && echo "  Executing BUILD_ONLY instead of fresh installation or UPDATE."
477      [ -n "$BUILD_DIRTY" ]         && echo "  Executing BUILD_DIRTY to leave chroot untouched."
478    fi
479    echo
480    echo -n "Is this ok for you? [y/N] "
481    read a
482    if ! [ "$a" = 'y' -o "$a" = 'Y' ] ; then
483       CLEAN_ARTIFACTS=0
484       echo "Exiting as requested."
485       exit 0
486    fi
487    echo
488 fi
489 # }}}
490
491 # clean up before start {{{
492 if [ -n "${CLEAN_ARTIFACTS}" ]; then
493   echo "Wiping old artifacts"
494   [ -n "${CHROOT_OUTPUT}"  -a -d "${CHROOT_OUTPUT}"  ] && rm -r "${CHROOT_OUTPUT}"
495   [ -n "${BUILD_OUTPUT}"   -a -d "${BUILD_OUTPUT}"   ] && rm -r "${BUILD_OUTPUT}"
496   [ -n "${ISO_OUTPUT}"     -a -d "${ISO_OUTPUT}"     ] && rm -r "${ISO_OUTPUT}"
497   [ -n "${LOG_OUTPUT}"     -a -d "${LOG_OUTPUT}"     ] && rm -r "${LOG_OUTPUT}"
498   [ -n "${NETBOOT}"        -a -d "${NETBOOT}"        ] && rm -r "${NETBOOT}"
499 fi
500 # }}}
501
502 # create log file {{{
503 [ -n "$LOGFILE" ] || LOGFILE=${LOG_OUTPUT}/grml-live.log
504 mkdir -p $(dirname "${LOGFILE}")
505 touch $LOGFILE
506 chown root:adm $LOGFILE
507 chmod 664 $LOGFILE
508 # }}}
509
510 # clean/zero/remove logfiles {{{
511
512 if [ -n "$PRESERVE_LOGFILE" ] ; then
513    echo "Preserving logfile $LOGFILE as requested via \$PRESERVE_LOGFILE"
514 else
515    # make sure it is empty (as it is e.g. appended to grml-live-db)
516    echo -n > $LOGFILE
517 fi
518
519 if [ -n "$ZERO_FAI_LOGFILE" ] ; then
520    if [ -d /var/log/fai/"$HOSTNAME" ] ; then
521       rm -rf /var/log/fai/"$HOSTNAME"/"$(readlink /var/log/fai/"$HOSTNAME"/last)"
522       rm -rf /var/log/fai/"$HOSTNAME"/"$(readlink /var/log/fai/"$HOSTNAME"/last-dirinstall)"
523       rm -rf /var/log/fai/"$HOSTNAME"/"$(readlink /var/log/fai/"$HOSTNAME"/last-softupdate)"
524       rm -f /var/log/fai/"$HOSTNAME"/last \
525             /var/log/fai/"$HOSTNAME"/last-dirinstall \
526             /var/log/fai/"$HOSTNAME"/last-softupdate
527    fi
528 fi
529 # }}}
530
531 # source config and startup {{{
532 if [ -n "$CONFIG" ] ; then
533    if ! [ -f "$CONFIG" ] ; then
534       log    "Error: $CONFIG could not be read. Exiting. [$(date)]"
535       eerror "Error: $CONFIG could not be read. Exiting." ; eend 1
536       bailout 1
537    else
538       log "Sourcing $CONFIG"
539       . $CONFIG
540    fi
541 fi
542
543 start_seconds=$(cut -d . -f 1 /proc/uptime)
544 log "------------------------------------------------------------------------------"
545 log "Starting grml-live [${GRML_LIVE_VERSION}] run on $(date)"
546 log "Using local config file: $LOCAL_CONFIG"
547 log "Executed grml-live command line:"
548 log "$CMDLINE"
549
550 einfo "Logging actions to logfile $LOGFILE"
551 # }}}
552
553 # dump config variables into file, for script access {{{
554 CONFIGDUMP=$(mktemp)
555 set | egrep \
556   '^(GRML_NAME|RELEASENAME|DATE|VERSION|SUITE|ARCH|DISTRI_NAME|USERNAME|HOSTNAME|APT_PROXY)=' \
557   > ${CONFIGDUMP}
558 # }}}
559
560 # unpack iso/squashfs {{{
561 extract_iso() {
562 if [ -n "$EXTRACT_ISO_NAME" ]; then
563   log "Unpacking ISO from ${EXTRACT_ISO_NAME}"
564   einfo "Unpacking ISO from ${EXTRACT_ISO_NAME}"
565   local mountpoint=$(mktemp -d)
566   local rc=0
567   mount -o loop "${EXTRACT_ISO_NAME}" "$mountpoint" ; rc=$?
568   if [ "$rc" != 0 ]; then
569     rmdir "$mountpoint"
570     log "mount failed"
571     eerror "mount failed"
572     eend 1
573     bailout 1
574   fi
575
576   if ls "${mountpoint}"/live/*/*.squashfs 2>/dev/null | grep -q . ; then # ISOs >=2011.12
577     log "Using ${mountpoint}/live/*/*.squashfs for unsquashfs"
578     unsquashfs -d "${CHROOT_OUTPUT}" "${mountpoint}"/live/*/*.squashfs ; rc=$?
579   elif ls "${mountpoint}"/live/*.squashfs 2>/dev/null | grep -q . ; then # ISOs before 2011.12
580     log "Using ${mountpoint}/live/*.squashfs for unsquashfs"
581     unsquashfs -d "${CHROOT_OUTPUT}" "${mountpoint}"/live/*.squashfs ; rc=$?
582   else
583     log "Error: Could not find any *.squashfs files on the ISO"
584     eerror "Error: Could not find any *.squashfs files on the ISO"
585     eend 1
586     bailout 1
587   fi
588
589   umount "$mountpoint"
590   rmdir "$mountpoint"
591   if [ "$rc" != 0 ]; then
592     log "unsquashfs failed"
593     eerror "unsquashfs failed"
594     eend 1
595     bailout 1
596   fi
597 fi
598 }
599 extract_iso
600 # }}}
601
602 # on-the-fly configuration {{{
603
604 # does this suck? YES!
605 # /usr/share/debootstrap/scripts/unstable does not exist, instead use 'sid':
606 case $SUITE in
607    unstable) SUITE='sid' ; CLASSES="DEBIAN_UNSTABLE,$CLASSES" ;;
608    *) CLASSES="DEBIAN_$(echo $SUITE | tr 'a-z' 'A-Z'),$CLASSES";;
609 esac
610 export SUITE # make sure it's available in FAI scripts
611
612 # validate whether the specified architecture class matches the
613 # architecture (option), otherwise installation of kernel will fail
614 if echo $CLASSES | grep -qw I386 ; then
615    if ! [[ "$ARCH" == "i386" ]] ; then
616       log    "Error: You specified the I386 class but are trying to build something else (AMD64?)."
617       eerror "Error: You specified the I386 class but are trying to build something else (AMD64?)."
618       eerror "Tip:   Either invoke grml-live with '-a i386' or adjust the architecture class. Exiting."
619       eend 1
620       bailout
621    fi
622 elif echo $CLASSES | grep -qi amd64 ; then
623    if ! [[ "$ARCH" == "amd64" ]] ; then
624       log    "Error: You specified the AMD64 class but are trying to build something else (I386?)."
625       eerror "Error: You specified the AMD64 class but are trying to build something else (I386?)."
626       eerror "Tip:   Either invoke grml-live with '-a amd64' or adjust the architecture class. Exiting."
627       eend 1
628       bailout
629    fi
630 fi
631
632 # generate nfsroot configuration for FAI on the fly
633 if [ -z "$FAI_DEBOOTSTRAP" ] ; then
634   FAI_DEBOOTSTRAP="$SUITE http://http.debian.net/debian"
635 fi
636
637 if [ -z "$FAI_DEBOOTSTRAP_OPTS" ] ; then
638   FAI_DEBOOTSTRAP_OPTS="--exclude=info,tasksel,tasksel-data --arch $ARCH"
639 fi
640
641 # create backup of old (not yet automatically generated) config file
642 if [ -f "${GRML_FAI_CONFIG}/make-fai-nfsroot.conf" ] ; then
643   if ! grep -q 'This is an automatically generated file by grml-live' "${GRML_FAI_CONFIG}/make-fai-nfsroot.conf" ; then
644     ewarn "Found old ${GRML_FAI_CONFIG}/make-fai-nfsroot.conf - moving to ${GRML_FAI_CONFIG}/make-fai-nfsroot.conf.outdated"
645     mv "${GRML_FAI_CONFIG}/make-fai-nfsroot.conf" "${GRML_FAI_CONFIG}/make-fai-nfsroot.conf.outdated"
646     eend $?
647   fi
648 fi
649
650 echo "# This is an automatically generated file by grml-live.
651 # Do NOT edit this file, your changes will be lost.
652 FAI_DEBOOTSTRAP=\"$FAI_DEBOOTSTRAP\"
653 FAI_DEBOOTSTRAP_OPTS=\"$FAI_DEBOOTSTRAP_OPTS\"
654 # EOF " > "${GRML_FAI_CONFIG}/nfsroot.conf"
655 # support FAI <=3.4.8, versions >=4.0 use nfsroot.conf
656 ( cd ${GRML_FAI_CONFIG} && ln -sf nfsroot.conf make-fai-nfsroot.conf )
657 # }}}
658
659 # CHROOT_OUTPUT - execute FAI {{{
660 if [ -n "$BUILD_DIRTY" ]; then
661    log   "Skipping stage 'fai' as requested via option -B"
662    ewarn "Skipping stage 'fai' as requested via option -B" ; eend 0
663 else
664    [ -n "$CHROOT_OUTPUT" ] || CHROOT_OUTPUT="$OUTPUT/grml_chroot"
665
666    if [ -n "$UPDATE" -o -n "$BUILD_ONLY" ] ; then
667       FAI_ACTION=softupdate
668    else
669       FAI_ACTION=dirinstall
670    fi
671
672    if [ -n "$UPDATE" -o -n "$BUILD_ONLY" ] ; then
673       if ! [ -r "$CHROOT_OUTPUT/etc/debian_version" ] ; then
674          log    "Error: does not look like you have a working chroot. Updating/building not possible."
675          eerror "Error: does not look like you have a working chroot. Updating/building not possible. (Drop -u/-b option?)"
676          eend 1
677          bailout 20
678       fi
679    fi
680
681    if [ -d "$CHROOT_OUTPUT/bin" -a -z "$UPDATE" -a -z "$BUILD_ONLY" ] ; then
682       log   "Skipping stage 'fai dirinstall' as $CHROOT_OUTPUT exists already."
683       ewarn "Skipping stage 'fai dirinstall' as $CHROOT_OUTPUT exists already." ; eend 0
684    else
685       mkdir -p "$CHROOT_OUTPUT" || bailout 5 "Problem with creating $CHROOT_OUTPUT for FAI"
686
687       if [ -n "${MIRROR_DIRECTORY}" ] ; then
688          mkdir -p "${CHROOT_OUTPUT}/${MIRROR_DIRECTORY}"
689          mount --bind "${MIRROR_DIRECTORY}" "${CHROOT_OUTPUT}/${MIRROR_DIRECTORY}"
690       fi
691
692       mkdir -p "${OUTPUT}/grml_sources/" "${CHROOT_OUTPUT}/grml-live/sources/"
693       mount --bind "${OUTPUT}/grml_sources/" "${CHROOT_OUTPUT}/grml-live/sources/"
694
695       log "Executed FAI command line:"
696       log "BUILD_ONLY=$BUILD_ONLY BOOTSTRAP_ONLY=$BOOTSTRAP_ONLY GRML_LIVE_CONFIG=$CONFIGDUMP fai $VERBOSE -C $GRML_FAI_CONFIG -s file:///$GRML_FAI_CONFIG/config -c$CLASSES -u $HOSTNAME $FAI_ACTION $CHROOT_OUTPUT $FAI_ARGS"
697       BUILD_ONLY="$BUILD_ONLY" BOOTSTRAP_ONLY="$BOOTSTRAP_ONLY" GRML_LIVE_CONFIG="$CONFIGDUMP" fai $VERBOSE \
698                   -C "$GRML_FAI_CONFIG" -s "file:///$GRML_FAI_CONFIG/config" -c"$CLASSES" \
699                   -u "$HOSTNAME" "$FAI_ACTION" "$CHROOT_OUTPUT" $FAI_ARGS | tee -a $LOGFILE
700       RC="$PIPESTATUS" # notice: bash-only
701
702       if [ "$RC" != 0 ] ; then
703          log    "Error: critical error while executing fai [exit code ${RC}]. Exiting."
704          eerror "Error: critical error while executing fai [exit code ${RC}]. Exiting." ; eend 1
705          bailout 1
706       fi
707
708       # provide inform fai about the ISO we build, needs to be provided
709       # *after* FAI stage, otherwise FAI skips the debootstrap stage if
710       # there is not BASEFILE (as it checks for presence of /etc) :(
711       echo '# This file has been generated by grml-live.' > "$CHROOT_OUTPUT/etc/grml_live_version"
712       [ -n "$GRML_LIVE_VERSION" ] && echo "GRML_LIVE_VERSION=$GRML_LIVE_VERSION" >> "$CHROOT_OUTPUT/etc/grml_live_version"
713       [ -n "$SUITE" ] && echo "SUITE=$SUITE" >> "$CHROOT_OUTPUT/etc/grml_live_version"
714
715       FORCE_ISO_REBUILD=true
716
717       # move fai logs into grml_logs directory
718       mkdir -p "$LOG_OUTPUT"/fai/
719       cp -r "$CHROOT_OUTPUT"/var/log/fai/"$HOSTNAME"/last/* "$LOG_OUTPUT"/fai/
720       rm -rf "$CHROOT_OUTPUT"/var/log/fai
721
722       # store copy of autogenerated configuration file
723       cp ${GRML_FAI_CONFIG}/nfsroot.conf "$LOG_OUTPUT"/fai/
724
725       # copy fai package list
726       cp "$CHROOT_OUTPUT"/var/log/install_packages.list "$LOG_OUTPUT"/fai/
727       # fixup owners
728       chown root:adm "$LOG_OUTPUT"/fai/*
729       chmod 664 "$LOG_OUTPUT"/fai/*
730
731       umount_all
732
733       # notice: 'fai dirinstall' does not seem to exit appropriate, so:
734       ERROR=''
735       CHECKLOG="$LOG_OUTPUT"/fai/
736       if [ -r "$CHECKLOG/software.log" ] ; then
737          # 1 errors during executing of commands
738          grep 'dpkg: error processing' $CHECKLOG/software.log >> $LOGFILE && ERROR=1
739          grep 'E: Method http has died unexpectedly!' $CHECKLOG/software.log >> $LOGFILE && ERROR=2
740          grep 'ERROR: chroot' $CHECKLOG/software.log >> $LOGFILE && ERROR=3
741          grep 'E: Failed to fetch' $CHECKLOG/software.log >> $LOGFILE && ERROR=4
742          grep 'Unable to write mmap - msync (28 No space left on device)' $CHECKLOG/software.log >> $LOGFILE && ERROR=5
743       fi
744
745       if [ -r "$CHECKLOG/shell.log" ] ; then
746          grep 'FAILED with exit code' $CHECKLOG/shell.log >> $LOGFILE && ERROR=6
747       fi
748
749       if [ -n "$ERROR" ] ; then
750          log    "Error: there was a critical error [${ERROR}] during execution of stage 'fai dirinstall' [$(date)]"
751          eerror "Error: there was a critical error during execution of stage 'fai dirinstall'"
752          eerror "Note:  check out ${CHECKLOG}/ for details. [exit ${ERROR}]"
753          eend 1
754          bailout 1
755       else
756          log "Finished execution of stage 'fai dirinstall' [$(date)]"
757          einfo "Finished execution of stage 'fai dirinstall'"
758       fi
759    fi
760 fi # BUILD_DIRTY?
761 # }}}
762
763 # package validator {{{
764 CHECKLOG=/var/log/fai/$HOSTNAME/last
765 if [ -r "$CHECKLOG/dpkg.selections" ] ; then
766   package_count=$(wc -l "$CHECKLOG/dpkg.selections" | awk '{print $1}')
767 else
768   package_count="unknown"
769 fi
770
771 mkdir -p "$REPORTS"
772 REPORT_MISSING_PACKAGES="${REPORTS}/TEST-MissingPackages.xml"
773
774 # check for missing packages
775 if ! [ -s "$CHECKLOG/package_errors.log" ] ; then
776   einfo "No missing packages found, generating empty junit report."
777
778   cat > "${REPORT_MISSING_PACKAGES}" << EOF
779 <?xml version="1.0" encoding="UTF-8"?>
780 <testsuite name="grml-live-missing-packages" tests="${package_count}" time="1" failures="0" errors="0" skipped="0" assertions="0">
781   <testcase name="test_missing_packages" time="0" assertions="0">
782   </testcase>
783   <system-out>
784   </system-out>
785   <system-err>
786   </system-err>
787 </testsuite>
788 EOF
789   eend 0
790 else
791   einfo "Missing packages found, generating junit report."
792
793   if [ -r "$CHECKLOG/package_errors.log" ] ; then
794     package_errors=$(wc -l "$CHECKLOG/package_errors.log" | awk '{print $1}')
795   else
796     package_errors="unknown"
797   fi
798
799   mkdir -p "$REPORTS"
800   REPORT_MISSING_PACKAGES="${REPORTS}/TEST-MissingPackages.xml"
801
802   cat > "${REPORT_MISSING_PACKAGES}" << EOF
803 <?xml version="1.0" encoding="UTF-8"?>
804 <testsuite name="grml-live-missing-packages" tests="${package_count}" time="1" failures="${package_errors}" errors="${package_errors}" skipped="0" assertions="0">
805 EOF
806
807   for package in $(awk '{print $1}' "${CHECKLOG}/package_errors.log") ; do
808     failure_reason="$(awk "/$package/ {print \$2}" "${CHECKLOG}/package_errors.log")"
809     cat >> "${REPORT_MISSING_PACKAGES}" << EOF
810   <testcase name="test_missing_packages_${package}" time="0" assertions="0">
811     <failure type="${failure_reason}" message="Package ${package} is missing">
812 Package $package is missing in chroot (${failure_reason})
813   </failure>
814   </testcase>
815 EOF
816   done
817
818   cat >> "${REPORT_MISSING_PACKAGES}" << EOF
819   <system-out>
820   </system-out>
821   <system-err>
822   </system-err>
823 </testsuite>
824 EOF
825   eend 0
826
827   if [ -n "$EXIT_ON_MISSING_PACKAGES" -a -z "$BUILD_DIRTY" ] ; then
828     eerror "The following packages were requested for installation but could not be processed:"
829     cat "$CHECKLOG/package_errors.log"
830     eerror "... exiting as requested via \$EXIT_ON_MISSING_PACKAGES."
831     eend 1
832     bailout 13
833   else
834     ewarn "The following packages were requested for installation but could not be processed:"
835     cat "$CHECKLOG/package_errors.log"
836     eend 0
837   fi
838 fi
839 # }}}
840
841 # BUILD_OUTPUT - execute arch specific stuff and squashfs {{{
842 [ -n "$BUILD_OUTPUT" ] || BUILD_OUTPUT="$OUTPUT/grml_cd"
843 mkdir -p "$BUILD_OUTPUT" || bailout 6 "Problem with creating $BUILD_OUTPUT for stage ARCH"
844
845 # prepare ISO
846 if [ "$ARCH" = i386 ] || [ "$ARCH" = amd64 ] ; then
847   if [ -n "$BOOTSTRAP_ONLY" ] ; then
848      log   "Skipping stage 'boot' as building with bootstrap only."
849      ewarn "Skipping stage 'boot' as building with bootstrap only." ; eend 0
850   else
851     if [ -d "$BUILD_OUTPUT"/boot/isolinux -a -z "$UPDATE" -a -z "$BUILD_ONLY" ] ; then
852        log   "Skipping stage 'boot' as $BUILD_OUTPUT/boot/isolinux exists already."
853        ewarn "Skipping stage 'boot' as $BUILD_OUTPUT/boot/isolinux exists already." ; eend 0
854     else
855        # booting stuff:
856        [ -d "$BUILD_OUTPUT"/boot/isolinux ] || mkdir -p "$BUILD_OUTPUT"/boot/isolinux
857        [ -d "$BUILD_OUTPUT"/boot/"${SHORT_NAME}" ] || mkdir -p "$BUILD_OUTPUT"/boot/"${SHORT_NAME}"
858
859        # if we don't have an initrd we a) can't boot and b) there was an error
860        # during build, so check for the file:
861        INITRD="$(ls $CHROOT_OUTPUT/boot/initrd* 2>/dev/null| grep -v '.bak$' | sort -r | head -1)"
862        if [ -n "$INITRD" ] ; then
863           cp $INITRD "$BUILD_OUTPUT"/boot/"${SHORT_NAME}"/initrd.img
864           find $CHROOT_OUTPUT/boot/ -name initrd\*.bak -exec rm {} \;
865        else
866           log    "Error: No initrd found inside $CHROOT_OUTPUT/boot/ - Exiting"
867           eerror "Error: No initrd found inside $CHROOT_OUTPUT/boot/ - Exiting" ; eend 1
868           bailout 10
869        fi
870
871        KERNEL_IMAGE="$(ls $CHROOT_OUTPUT/boot/vmlinuz* 2>/dev/null | sort -r | head -1)"
872        if [ -n "$KERNEL_IMAGE" ] ; then
873           cp "$KERNEL_IMAGE" "$BUILD_OUTPUT"/boot/"${SHORT_NAME}"/vmlinuz
874        else
875           log    "Error: No kernel found inside $CHROOT_OUTPUT/boot/ - Exiting"
876           eerror "Error: No kernel found inside $CHROOT_OUTPUT/boot/ - Exiting" ; eend 1
877           bailout 11
878        fi
879
880        # EFI boot files
881        if [ -r "${CHROOT_OUTPUT}/boot/efi.img" -a -r "${CHROOT_OUTPUT}/boot/bootx64.efi" ] ; then
882           einfo "Moving EFI boot files into ISO path."
883           log "Moving EFI boot files into ISO path."
884           RC=$0
885           mv "${CHROOT_OUTPUT}/boot/efi.img" "${BUILD_OUTPUT}/boot/" || RC=$?
886           mkdir -p "${BUILD_OUTPUT}/efi/boot/" || RC=$?
887           mv "${CHROOT_OUTPUT}/boot/bootx64.efi" "${BUILD_OUTPUT}/efi/boot/bootx64.efi" || RC=$?
888           eend $?
889        fi
890
891        [ -n "$TEMPLATE_DIRECTORY" ] || TEMPLATE_DIRECTORY='/usr/share/grml-live/templates'
892        if ! [ -d "${TEMPLATE_DIRECTORY}"/boot ] ; then
893           log    "Error: ${TEMPLATE_DIRECTORY}/boot does not exist. Exiting."
894           eerror "Error: ${TEMPLATE_DIRECTORY}/boot does not exist. Exiting." ; eend 1
895           bailout 8
896        fi
897
898        # copy _required_ isolinux files
899        if [ -d "${CHROOT_OUTPUT}/usr/lib/ISOLINUX" ] ; then
900          copy_addon_file isolinux.bin /usr/lib/ISOLINUX isolinux
901          copy_addon_file ifcpu64.c32  /usr/lib/syslinux/modules/bios/ isolinux
902          copy_addon_file ldlinux.c32  /usr/lib/syslinux/modules/bios/ isolinux
903          copy_addon_file libcom32.c32 /usr/lib/syslinux/modules/bios/ isolinux
904          copy_addon_file libutil.c32  /usr/lib/syslinux/modules/bios/ isolinux
905          copy_addon_file vesamenu.c32 /usr/lib/syslinux/modules/bios/ isolinux
906        else # syslinux versions <= 3:4.05+dfsg-6+deb8u1
907          copy_addon_file isolinux.bin /usr/lib/syslinux isolinux
908          copy_addon_file ifcpu64.c32  /usr/lib/syslinux isolinux
909          copy_addon_file vesamenu.c32 /usr/lib/syslinux isolinux
910        fi
911
912        # *always* copy files to output directory so the variables
913        # get adjusted according to the build.
914        cp ${TEMPLATE_DIRECTORY}/boot/isolinux/*  "$BUILD_OUTPUT"/boot/isolinux/
915
916        mkdir -p "${BUILD_OUTPUT}/boot/grub"
917        cp -a ${TEMPLATE_DIRECTORY}/boot/grub/* "$BUILD_OUTPUT"/boot/grub/
918
919        if [ -n "$NO_ADDONS" ] ; then
920           rm -f "$BUILD_OUTPUT"/boot/grub/addons.cfg
921           log   "Skipping installation of boot addons as requested via \$NO_ADDONS."
922           einfo "Skipping installation of boot addons as requested via \$NO_ADDONS."; eend 0
923        else
924           if ! [ -d "$TEMPLATE_DIRECTORY"/boot/addons ] ; then
925             log   "Boot addons not found, skipping therefore. (Consider installing package grml-live-addons)"
926             ewarn "Boot addons not found, skipping therefore. (Consider installing package grml-live-addons)" ; eend 0
927           else
928             # copy addons from system packages or grml-live-compat
929             copy_addon_file ipxe.lkrn /usr/lib/ipxe addons
930             copy_addon_file pci.ids /usr/share/misc addons
931             copy_addon_file memtest86+.bin /boot addons
932
933             # since syslinux(-common) v3:6.03~pre1+dfsg-4 the files are in a
934             # different directory :(
935             if [ -d "${CHROOT_OUTPUT}/usr/lib/syslinux/modules/bios/" ] ; then
936               syslinux_modules_dir=/usr/lib/syslinux/modules/bios/
937             else
938               syslinux_modules_dir=/usr/lib/syslinux
939             fi
940             for file in memdisk chain.c32 hdt.c32 mboot.c32 menu.c32; do
941               copy_addon_file "${file}" "${syslinux_modules_dir}" addons
942             done
943
944             # make memtest filename FAT16/8.3 compatible
945             mv "${BUILD_OUTPUT}/boot/addons/memtest86+.bin" \
946               "${BUILD_OUTPUT}/boot/addons/memtest"
947
948             # copy only files so we can handle bsd4grml on its own
949             for file in ${TEMPLATE_DIRECTORY}/boot/addons/* ; do
950               test -f $file && cp $file "$BUILD_OUTPUT"/boot/addons/
951             done
952
953             if [ -n "$NO_ADDONS_BSD4GRML" ] ; then
954                log   "Skipping installation of bsd4grml as requested via \$NO_ADDONS_BSD4GRML."
955                einfo "Skipping installation of bsd4grml as requested via \$NO_ADDONS_BSD4GRML."; eend 0
956             else
957                if [ -d "$TEMPLATE_DIRECTORY"/boot/addons/bsd4grml ] ; then
958                  cp -a ${TEMPLATE_DIRECTORY}/boot/addons/bsd4grml "$BUILD_OUTPUT"/boot/addons/
959                else
960                  log   "Missing addon file: bsd4grml"
961                  ewarn "Missing addon file: bsd4grml" ; eend 0
962                fi
963             fi
964
965           fi # no "$TEMPLATE_DIRECTORY"/boot/addons
966        fi # NO_ADDONS
967
968        # generate loopback.cfg config file without depending on grub's regexp module
969        # which isn't available in Debian/squeeze
970        echo "## grub2 loopback configuration" > "${BUILD_OUTPUT}"/boot/grub/loopback.cfg
971        echo "source /boot/grub/header.cfg" >> "${BUILD_OUTPUT}"/boot/grub/loopback.cfg
972        for config in "${BUILD_OUTPUT}"/boot/grub/*_default.cfg "${BUILD_OUTPUT}"/boot/grub/*_options.cfg ; do
973          [ -r "$config" ] || continue
974          echo "source ${config##$BUILD_OUTPUT}" >> "${BUILD_OUTPUT}"/boot/grub/loopback.cfg
975        done
976        if [ -z "$NO_ADDONS" ] ; then
977          echo "source /boot/grub/addons.cfg" >> "${BUILD_OUTPUT}"/boot/grub/loopback.cfg
978        fi
979        echo "source /boot/grub/footer.cfg" >> "${BUILD_OUTPUT}"/boot/grub/loopback.cfg
980
981        # copy grub files from target
982        mkdir -p "${BUILD_OUTPUT}"/boot/grub/i386-pc/
983        cp -a "${CHROOT_OUTPUT}"/usr/lib/grub/*-pc/*.mod "${BUILD_OUTPUT}"/boot/grub/i386-pc/
984        cp -a "${CHROOT_OUTPUT}"/usr/lib/grub/*-pc/*.o "${BUILD_OUTPUT}"/boot/grub/i386-pc/
985        cp -a "${CHROOT_OUTPUT}"/usr/lib/grub/*-pc/*.lst "${BUILD_OUTPUT}"/boot/grub/i386-pc/
986        cp -a "${CHROOT_OUTPUT}"/usr/share/grub/ascii.pf2 "${BUILD_OUTPUT}"/boot/grub/
987        cp -a "${CHROOT_OUTPUT}"/boot/grub/core.img "${BUILD_OUTPUT}"/boot/grub/
988        cp -a "${CHROOT_OUTPUT}"/boot/grub/grub.img "${BUILD_OUTPUT}"/boot/grub/
989
990        # copy modules for UEFI grub
991        mkdir -p "${BUILD_OUTPUT}"/boot/grub/x86_64-efi/
992        cp -a "${CHROOT_OUTPUT}"/usr/lib/grub/x86_64-efi/*.{mod,lst} "${BUILD_OUTPUT}"/boot/grub/x86_64-efi/
993
994        if ! [ -d "${TEMPLATE_DIRECTORY}"/GRML ] ; then
995           log    "Error: ${TEMPLATE_DIRECTORY}/GRML does not exist. Exiting."
996           eerror "Error: ${TEMPLATE_DIRECTORY}/GRML does not exist. Exiting." ; eend 1
997           bailout 9
998        fi
999
1000        mkdir -p "$BUILD_OUTPUT"/GRML/"${GRML_NAME}"/
1001        cp -a ${TEMPLATE_DIRECTORY}/GRML/* "$BUILD_OUTPUT"/GRML/"${GRML_NAME}"/
1002
1003        # adjust boot splash information:
1004        RELEASE_INFO="$GRML_NAME $VERSION - Release Codename $RELEASENAME"
1005        RELEASE_INFO="$(cut_string 68 "$RELEASE_INFO")"
1006        RELEASE_INFO="$(extend_string_end 68 "$RELEASE_INFO")"
1007
1008        if [ -r "$BUILD_OUTPUT"/GRML/"${GRML_NAME}"/grml-version ] ; then
1009           sed -i "s/%RELEASE_INFO%/$GRML_NAME $VERSION - $RELEASENAME/" "$BUILD_OUTPUT"/GRML/"${GRML_NAME}"/grml-version
1010           sed -i "s/%DATE%/$DATE/"                                      "$BUILD_OUTPUT"/GRML/"${GRML_NAME}"/grml-version
1011        fi
1012
1013        # make sure the squashfs filename is set accordingly:
1014        SQUASHFS_NAME="$GRML_NAME.squashfs"
1015
1016        if [ -n "$NO_BOOTID" ] ; then
1017           log   'Skipping bootid feature as requested via $NO_BOOTID.'
1018           einfo 'Skipping bootid feature as requested via $NO_BOOTID.'
1019        else
1020           [ -n "$BOOTID" ] || BOOTID="$(echo ${GRML_NAME}${VERSION} | tr -d ',./;\- ')"
1021           [ -d "$BUILD_OUTPUT"/conf ] || mkdir "$BUILD_OUTPUT"/conf
1022           einfo "Generating /conf/bootid.txt with entry ${BOOTID}."
1023           log   "Generating /conf/bootid.txt with entry ${BOOTID}."
1024           echo "$BOOTID" > "$BUILD_OUTPUT"/conf/bootid.txt
1025           eend $?
1026        fi
1027
1028        # adjust all variables in the templates with the according distribution information
1029        for file in "${BUILD_OUTPUT}"/boot/isolinux/*.cfg "${BUILD_OUTPUT}"/boot/isolinux/*.msg \
1030                    "${BUILD_OUTPUT}"/boot/grub/* ; do
1031          if [ -r "${file}" ] && [ -f "${file}" ] ; then
1032            sed -i "s/%ARCH%/$ARCH/g"                    "${file}"
1033            sed -i "s/%DATE%/$DATE/g"                    "${file}"
1034            sed -i "s/%DISTRI_INFO%/$DISTRI_INFO/g"      "${file}"
1035            sed -i "s/%DISTRI_NAME%/$DISTRI_NAME/g"      "${file}"
1036            sed -i "s/%DISTRI_SPLASH%/$DISTRI_SPLASH/g"  "${file}"
1037            sed -i "s/%GRML_NAME%/$GRML_NAME/g"          "${file}"
1038            sed -i "s/%SQUASHFS_NAME%/$SQUASHFS_NAME/g"  "${file}"
1039            sed -i "s/%RELEASE_INFO%/$RELEASE_INFO/g"    "${file}"
1040            sed -i "s/%SHORT_NAME%/$SHORT_NAME/g"        "${file}"
1041            sed -i "s/%VERSION%/$VERSION/g"              "${file}"
1042
1043            [ -n "$DEFAULT_BOOTOPTIONS" ] && sed -i "s; boot=live; boot=live $DEFAULT_BOOTOPTIONS;"  "${file}"
1044
1045            if [ -n "$NO_BOOTID" ] ; then
1046               sed -i "s/ bootid=%BOOTID%//g" "${file}" # drop bootid bootoption
1047            else
1048               sed -i "s/%BOOTID%/$BOOTID/g" "${file}" # adjust bootid=... argument
1049            fi
1050          fi
1051        done
1052
1053        for param in ARCH DATE DISTRI_INFO DISTRI_NAME DISTRI_SPLASH GRML_NAME SQUASHFS_NAME \
1054            RELEASE_INFO SHORT_NAME VERSION ; do
1055            for file in $(find "${BUILD_OUTPUT}" -name "*%$param%*") ; do
1056                value="$(eval echo '$'"$param")"
1057                mv ${file} ${file/\%${param}\%/$value}
1058            done
1059        done
1060
1061        # adjust bootsplash accordingly but make sure the string has the according lenght
1062        SQUASHFS_NAME="$(cut_string 20 "$SQUASHFS_NAME")"
1063        SQUASHFS_NAME="$(extend_string_end 20 "$SQUASHFS_NAME")"
1064        for file in f4 f5 ; do
1065           if [ -r "${BUILD_OUTPUT}/boot/isolinux/${file}" ] ; then
1066              sed -i "s/%SQUASHFS_NAME%/$SQUASHFS_NAME/" "${BUILD_OUTPUT}/boot/isolinux/${file}"
1067              sed -i "s/%SQUASHFS_NAME%/$SQUASHFS_NAME/" "${BUILD_OUTPUT}/boot/isolinux/${file}"
1068           fi
1069        done
1070
1071        # generate addon list
1072        rm -f "${BUILD_OUTPUT}/${ADDONS_LIST_FILE}"
1073        for name in "${BUILD_OUTPUT}"/boot/isolinux/addon_*.cfg ; do
1074          include_name=$(basename "$name")
1075          echo "include $include_name"  >> "${BUILD_OUTPUT}/${ADDONS_LIST_FILE}"
1076        done
1077
1078        if ! [ -r "${BUILD_OUTPUT}/boot/isolinux/${DISTRI_NAME}.cfg" ] || [ "$DISTRI_NAME" = "grml" ] ; then
1079           log "including grmlmain.cfg in ${BUILD_OUTPUT}/boot/isolinux/distri.cfg"
1080           echo "include grmlmain.cfg"    >  "${BUILD_OUTPUT}/boot/isolinux/distri.cfg"
1081           echo "include default.cfg"     >  "${BUILD_OUTPUT}/boot/isolinux/grmlmain.cfg"
1082           echo "include menuoptions.cfg" >> "${BUILD_OUTPUT}/boot/isolinux/grmlmain.cfg"
1083           echo "include grml.cfg"        >> "${BUILD_OUTPUT}/boot/isolinux/grmlmain.cfg"
1084
1085           for f in "${BUILD_OUTPUT}"/boot/isolinux/submenu*.cfg ; do
1086             echo "include $(basename $f)"     >> "${BUILD_OUTPUT}/boot/isolinux/grmlmain.cfg"
1087           done
1088
1089           echo "include options.cfg"     >> "${BUILD_OUTPUT}/boot/isolinux/grmlmain.cfg"
1090           if [ -z "$NO_ADDONS" ] ; then
1091             echo "include addons.cfg"    >> "${BUILD_OUTPUT}/boot/isolinux/grmlmain.cfg"
1092           fi
1093           echo "include isoprompt.cfg"   >> "${BUILD_OUTPUT}/boot/isolinux/grmlmain.cfg"
1094           echo "include hd.cfg"          >> "${BUILD_OUTPUT}/boot/isolinux/grmlmain.cfg"
1095           echo "include hidden.cfg"      >> "${BUILD_OUTPUT}/boot/isolinux/grmlmain.cfg"
1096        else # assume we are building a custom distribution:
1097           log "File ${BUILD_OUTPUT}/boot/isolinux/${DISTRI_NAME}.cfg found, using it."
1098           einfo "File ${BUILD_OUTPUT}/boot/isolinux/${DISTRI_NAME}.cfg found, using it."
1099           if grep -q "^include ${DISTRI_NAME}.cfg" "${BUILD_OUTPUT}/boot/isolinux/distri.cfg" ; then
1100             log "include for ${DISTRI_NAME}.cfg already present, nothing to do."
1101             eindent
1102             einfo "include for ${DISTRI_NAME}.cfg already present, nothing to do."
1103             eoutdent
1104             eend $?
1105          else
1106             log "including ${DISTRI_NAME}.cfg in ${BUILD_OUTPUT}/boot/isolinux/distri.cfg"
1107             echo "include ${DISTRI_NAME}.cfg" > "${BUILD_OUTPUT}/boot/isolinux/distri.cfg"
1108             if [ -z "$NO_ADDONS" ] ; then
1109               echo "include addons.cfg" >> "${BUILD_OUTPUT}/boot/isolinux/distri.cfg"
1110             fi
1111           fi
1112        fi
1113
1114        # use old style console based isolinux method only if requested:
1115        if [[ "${ISOLINUX_METHOD}" == "console" ]] ; then
1116           log 'Using console based isolinux method as requested via $ISOLINUX_METHOD.'
1117           einfo 'Using console based isolinux method as requested via $ISOLINUX_METHOD.'
1118           if grep -q '^include console.cfg' "${BUILD_OUTPUT}/boot/isolinux/distri.cfg" ; then
1119             einfo "include for console.cfg already found, nothing to do."
1120             eend 0
1121           else
1122             log "including console.cfg in ${BUILD_OUTPUT}/boot/isolinux/isolinux.cfg"
1123             einfo "including console.cfg in ${BUILD_OUTPUT}/boot/isolinux/isolinux.cfg"
1124             echo "include console.cfg" >> "${BUILD_OUTPUT}/boot/isolinux/isolinux.cfg"
1125             eend $?
1126           fi
1127        else
1128           log 'Using graphical boot menu.'
1129           if grep -q '^include vesamenu.cfg' "${BUILD_OUTPUT}/boot/isolinux/isolinux.cfg" ; then
1130             log "include for vesamenu.cfg already found, nothing to do."
1131           else
1132             log "including vesamenu.cfg in ${BUILD_OUTPUT}/boot/isolinux/isolinux.cfg"
1133             echo "include vesamenu.cfg" >> "${BUILD_OUTPUT}/boot/isolinux/isolinux.cfg"
1134           fi
1135        fi
1136
1137        if [ -e "$BUILD_OUTPUT"/boot/addons/bsd4grml/boot.6 ]; then
1138           sed -i "s/%RELEASE_INFO%/$GRML_NAME $VERSION - $RELEASENAME/" "$BUILD_OUTPUT"/boot/addons/bsd4grml/boot.6
1139        fi
1140
1141        DPKG_LIST="/var/log/fai/$HOSTNAME/last/dpkg.list" # the dpkg --list output of the chroot
1142        if ! [ -r "$DPKG_LIST" ] ; then
1143           ewarn "$DPKG_LIST could not be read, ignoring to store package information on ISO therefore."
1144        else
1145           einfo "Storing package list information as /GRML/${GRML_NAME}/packages.txt on ISO."
1146           cp "$DPKG_LIST" "${BUILD_OUTPUT}"/GRML/"${GRML_NAME}"/packages.txt
1147           eend $?
1148        fi
1149
1150        # autostart for Windows:
1151        if [ -d "${TEMPLATE_DIRECTORY}/windows/autostart/" ] ; then
1152           cp ${TEMPLATE_DIRECTORY}/windows/autostart/* "$BUILD_OUTPUT"/
1153        fi
1154
1155     FORCE_ISO_REBUILD=true
1156     einfo "Finished execution of stage 'boot'" ; eend 0
1157     fi
1158   fi # BOOTSTRAP_ONLY
1159 else
1160    log    'Error: Unsupported ARCH, sorry. Want to support it? Contribute!'
1161    eerror 'Error: Unsupported ARCH, sorry. Want to support it? Contribute!' ; eend 1
1162    bailout
1163 fi
1164
1165 # support installation of local files into the chroot/ISO
1166 if [ -n "$CHROOT_INSTALL" ] ; then
1167   if ! [ -d "$CHROOT_INSTALL" ] ; then
1168      log "Configuration variable \$CHROOT_INSTALL is set but not a directory; ignoring"
1169      ewarn "Configuration variable \$CHROOT_INSTALL is set but not a directory; ignoring"
1170   else
1171      log "Copying local files to chroot as requested via \$CHROOT_INSTALL"
1172      einfo "Copying local files to chroot as requested via \$CHROOT_INSTALL"
1173      rsync -avz --inplace "$CHROOT_INSTALL"/ "$CHROOT_OUTPUT/"
1174      eend $?
1175      einfo "Make sure to run squashfs stage, otherwise your local files won't be part of the ISO."
1176      FORCE_ISO_REBUILD=true
1177   fi
1178 fi
1179
1180 if [ -f "$BUILD_OUTPUT"/live/${GRML_NAME}.squashfs -a -z "$UPDATE" -a -z "$BUILD_ONLY" -a -z "$BUILD_DIRTY" ] ; then
1181    log   "Skipping stage 'squashfs' as $BUILD_OUTPUT/live exists already."
1182    ewarn "Skipping stage 'squashfs' as $BUILD_OUTPUT/live exists already." ; eend 0
1183 elif [ -n "$SKIP_MKSQUASHFS" ] ; then
1184    log   "Skipping stage 'squashfs' as requested via option -q or -N"
1185    ewarn "Skipping stage 'squashfs' as requested via option -q or -N" ; eend 0
1186 else
1187    mkdir -p "$BUILD_OUTPUT"/live/"${GRML_NAME}"/
1188    # make sure we don't leave (even an empty) base.tgz:
1189    [ -f "$CHROOT_OUTPUT/base.tgz" ] && rm -f "$CHROOT_OUTPUT/base.tgz"
1190
1191    # if unconfigured default to squashfs-tools' mksquashfs binary
1192    if [ -z "$SQUASHFS_BINARY" ] ; then
1193       SQUASHFS_BINARY='mksquashfs'
1194    fi
1195
1196    if which "$SQUASHFS_BINARY" >/dev/null 2>&1 ; then
1197       log    "Using mksquashfs binary ${SQUASHFS_BINARY}"
1198       einfo  "Using mksquashfs binary ${SQUASHFS_BINARY}" ; eend 0
1199    else
1200       log    "Error: mksquashfs binary ($SQUASHFS_BINARY) not found. Exiting."
1201       eerror "Error: mksquashfs binary ($SQUASHFS_BINARY) not found. Exiting." ; eend 1
1202       bailout
1203    fi
1204
1205    # use sane defaults if $SQUASHFS_OPTIONS isn't set
1206    if [ -z "$SQUASHFS_OPTIONS" ] ; then
1207      # use blocksize 256k as this gives best result with regards to time + compression
1208      SQUASHFS_OPTIONS="-b 256k"
1209
1210      # set lzma/xz compression by default, unless -z option has been specified on command line
1211      if [ -z "$SQUASHFS_ZLIB" ] ; then
1212         SQUASHFS_OPTIONS="$SQUASHFS_OPTIONS -comp xz"
1213      else
1214         SQUASHFS_OPTIONS="$SQUASHFS_OPTIONS -comp gzip"
1215      fi
1216    fi
1217
1218    # support exclusion of files via exclude-file:
1219    if [ -n "$SQUASHFS_EXCLUDES_FILE" -a "$SQUASHFS_EXCLUDES_FILE" ] ; then
1220       SQUASHFS_OPTIONS="$SQUASHFS_OPTIONS -ef $SQUASHFS_EXCLUDES_FILE -wildcards"
1221    fi
1222
1223    # get rid of unnecessary files when building grml-small for final release:
1224    if echo "$CLASSES" | grep -q GRML_SMALL ; then
1225       SQUASHFS_OPTIONS="$SQUASHFS_OPTIONS -e initrd.img* vmlinuz*"
1226    fi
1227
1228    # log stuff
1229    SQUASHFS_STDERR="$(mktemp -t grml-live.XXXXXX)"
1230
1231    # informational stuff
1232    [ -n "$SQUASHFS_OPTIONS" ]  && SQUASHFS_INFO_MSG="$SQUASHFS_OPTIONS"
1233    [ -n "$SQUASHFS_INFO_MSG" ] && SQUASHFS_INFO_MSG="using options: $SQUASHFS_INFO_MSG"
1234    einfo "Squashfs build information: running binary $SQUASHFS_BINARY $SQUASHFS_INFO_MSG"
1235
1236    log "$SQUASHFS_BINARY $CHROOT_OUTPUT/ $BUILD_OUTPUT/live/${GRML_NAME}/${GRML_NAME}.squashfs -noappend $SQUASHFS_OPTIONS"
1237
1238    if $SQUASHFS_BINARY $CHROOT_OUTPUT/ $BUILD_OUTPUT/live/"${GRML_NAME}"/"${GRML_NAME}".squashfs \
1239       -noappend $SQUASHFS_OPTIONS 2>"${SQUASHFS_STDERR}" ; then
1240       echo "${GRML_NAME}.squashfs" > $BUILD_OUTPUT/live/"${GRML_NAME}"/filesystem.module
1241       log "Finished execution of stage 'squashfs' [$(date)]"
1242       einfo "Finished execution of stage 'squashfs'" ; eend 0
1243    else
1244       log    "Error: there was a critical error executing stage 'squashfs' [$(date)]:"
1245       log    "$(cat $SQUASHFS_STDERR)"
1246       eerror "Error: there was a critical error executing stage 'squashfs':"
1247       cat    "${SQUASHFS_STDERR}"
1248       eend 1
1249       bailout
1250    fi
1251
1252    FORCE_ISO_REBUILD=true
1253 fi
1254
1255 # create md5sum file:
1256 if [ -z "$BOOTSTRAP_ONLY" ] ; then
1257   ( cd $BUILD_OUTPUT/GRML/"${GRML_NAME}" &&
1258   find ../.. -type f -not -name md5sums -not -name isolinux.bin -exec md5sum {} \; > md5sums )
1259 fi
1260 # }}}
1261
1262 # ISO_OUTPUT - mkisofs {{{
1263 [ -n "$ISO_OUTPUT" ] || ISO_OUTPUT="$OUTPUT/grml_isos"
1264 [ -n "$ISO_NAME" ] || ISO_NAME="${GRML_NAME}_${VERSION}.iso"
1265
1266 if [ "$BOOT_METHOD" = "isolinux" ] ; then
1267    BOOT_ARGS="-no-emul-boot -boot-load-size 4 -boot-info-table -b boot/isolinux/isolinux.bin -c boot/isolinux/boot.cat"
1268 elif [ "$BOOT_METHOD" = "grub2" ] ; then
1269    BOOT_ARGS="-no-emul-boot -boot-load-size 4 -b boot/grub/toriboot.bin"
1270 fi
1271
1272 # Work around http://bts.grml.org/grml/issue945
1273 if [[ $BOOT_METHOD != isolinux && ($HYBRID_METHOD = isohybrid || $HYBRID_METHOD = manifold) ]]; then
1274   log   "Setting HYBRID_METHOD to grub2 as hybrid mode does not work with isohybrid yet."
1275   ewarn "Setting HYBRID_METHOD to grub2 as hybrid mode does not work with isohybrid yet."
1276   HYBRID_METHOD='grub2'
1277   eend 0
1278 fi
1279
1280 if [ -f "${ISO_OUTPUT}/${ISO_NAME}" -a -z "$UPDATE" -a -z "$BUILD_ONLY" -a -z "$BUILD_DIRTY" -a "$FORCE_ISO_REBUILD" = "false" ]  ; then
1281    log   "Skipping stage 'iso build' as $ISO_OUTPUT/${ISO_NAME} exists already."
1282    ewarn "Skipping stage 'iso build' as $ISO_OUTPUT/${ISO_NAME} exists already." ; eend 0
1283 elif [ -n "$SKIP_MKISOFS" ] ; then
1284    log   "Skipping stage 'iso build' as requested via option -n or -N"
1285    ewarn "Skipping stage 'iso build' as requested via option -n or -N" ; eend 0
1286 else
1287    mkdir -p "$ISO_OUTPUT" || bailout 6 "Problem with creating $ISO_OUTPUT for stage 'iso build'"
1288
1289    if $FORCE_ISO_REBUILD && ! [ -f "${ISO_OUTPUT}/${ISO_NAME}" ] ; then
1290       log   "Forcing rebuild of ISO because files on ISO have been modified."
1291       einfo "Forcing rebuild of ISO because files on ISO have been modified."
1292    fi
1293
1294    # support xorriso as well mkisofs and genisoimage
1295    if which xorriso >/dev/null 2>&1 ; then
1296       MKISOFS='xorriso -as mkisofs'
1297     elif which mkisofs >/dev/null 2>&1; then
1298       MKISOFS='mkisofs'
1299    elif which genisoimage >/dev/null 2>&1; then
1300       MKISOFS='genisoimage'
1301    else
1302       log    "Error: neither xorriso nor mkisofs nor genisoimage available - can not create ISO."
1303       eerror "Error: neither xorriso nor mkisofs nor genisoimage available - can not create ISO." ; eend 1
1304       bailout
1305    fi
1306
1307    einfo "Using ${MKISOFS} to build ISO." ;  eend 0
1308    case "${ARCH}-${MKISOFS}" in
1309      # using -eltorito-alt-boot is limited to xorriso for now
1310      amd64-xorriso*)
1311        eindent
1312
1313        if ! dpkg --compare-versions $(dpkg-query -W -f='${Version}\n' xorriso 2>/dev/null) gt-nl 1.1.6-1 ; then
1314          log   "Disabling (U)EFI boot support because xorriso version is too old."
1315          ewarn "Disabling (U)EFI boot support because xorriso version is too old." ; eend 0
1316        else
1317          if [ -r "${BUILD_OUTPUT}"/boot/efi.img ] ; then
1318            einfo "Enabling (U)EFI boot."
1319            log   "Enabling (U)EFI boot."
1320            BOOT_ARGS="$BOOT_ARGS -boot-info-table -eltorito-alt-boot -e boot/efi.img -no-emul-boot"
1321            eend $?
1322          else
1323            log   "Disabling (U)EFI boot support because /boot/efi.img is missing."
1324            ewarn "Disabling (U)EFI boot support because /boot/efi.img is missing." ; eend 0
1325          fi
1326        fi
1327
1328        eoutdent
1329        ;;
1330    esac
1331
1332    CURRENT_DIR=$(pwd)
1333    if cd "$BUILD_OUTPUT" ; then
1334       if [ "$BOOT_METHOD" = "grub2" ]; then
1335          # make a 2048-byte bootsector for El Torito
1336          dd if=/dev/zero of=boot/grub/toriboot.bin bs=512 count=4 2>/dev/null
1337          # those are in 2048-byte sectors, so 1 16 matches 4 63 below
1338          echo 1 16 | mksh "${SCRIPTS_DIRECTORY}/bootgrub.mksh" -B 11 | \
1339             dd of=boot/grub/toriboot.bin conv=notrunc 2>/dev/null
1340       fi
1341       log "$MKISOFS -V '${GRML_NAME} ${VERSION}' -publisher 'grml-live | grml.org' -l -r -J $BOOT_ARGS -o ${ISO_OUTPUT}/${ISO_NAME} ."
1342       $MKISOFS -V "${GRML_NAME} ${VERSION}" -publisher 'grml-live | grml.org' \
1343               -l -r -J $BOOT_ARGS -no-pad \
1344               -o "${ISO_OUTPUT}/${ISO_NAME}" . ; RC=$?
1345       # both of these need core.img there, so it’s easier to write it here
1346       if [ "$BOOT_METHOD" = "grub2" ] || [ "$HYBRID_METHOD" = "grub2" ]; then
1347          # must be <= 30720 bytes
1348          dd if=boot/grub/core.img of="${ISO_OUTPUT}/${ISO_NAME}" \
1349            conv=notrunc bs=512 seek=4 2>/dev/null
1350       fi
1351
1352       # pad the output ISO to multiples of 256 KiB for partition table support
1353       siz=$($getfilesize "${ISO_OUTPUT}/${ISO_NAME}")
1354       cyls=$((siz / 512 / 32 / 16 + 1))   # C=$cyls H=16 S=32
1355       siz=$((cyls * 16 * 32 * 512))   # size after padding
1356       dd if=/dev/zero bs=1 count=1 seek=$((siz - 1)) \
1357          of="${ISO_OUTPUT}/${ISO_NAME}" 2>/dev/null
1358
1359       # support disabling hybrid ISO image
1360       if [ "$HYBRID_METHOD" = "disable" ] ; then
1361         log   "Skipping creation of hybrid ISO file as requested via HYBRID_METHOD=disable"
1362         einfo "Skipping creation of hybrid ISO file as requested via HYBRID_METHOD=disable"
1363         eend 0
1364       elif [ "$HYBRID_METHOD" = "manifold" ] || [ "$HYBRID_METHOD" = "grub2" ] ; then
1365         # isoinfo is part of both mkisofs and genisoimage so we're good
1366         bootoff=$(isoinfo -l -i "${ISO_OUTPUT}/${ISO_NAME}" | \
1367           sed -n '/^.*\[ *\([0-9]*\)[] ].* ISOLINUX.BIN[;1]* *$/s//\1/p')
1368
1369         if ! [ -r boot/grub/core.img ] ; then
1370           log   "boot/grub/core.img not found, not creating manifold boot ISO file"
1371           ewarn "boot/grub/core.img not found, not creating manifold boot ISO file"
1372         elif [ "${bootoff:-0}" -lt 1 ] ; then
1373           log   "isolinux.bin not found on the ISO file, disabling manifold boot"
1374           ewarn "isolinux.bin not found on the ISO file, disabling manifold boot"
1375         else
1376           if [ "$HYBRID_METHOD" = "grub2" ] ; then
1377             log   "Creating hybrid ISO file with manifold/grub2 method"
1378             einfo "Creating hybrid ISO file with manifold/grub2 method"
1379             # 512 bytes: MBR, partition table, load GRUB 2
1380             echo 4 63 | mksh "${SCRIPTS_DIRECTORY}/bootgrub.mksh" -A -M 4:0x96 -g $cyls:16:32
1381           else
1382             log   "Creating hybrid ISO file with manifold method"
1383             einfo "Creating hybrid ISO file with manifold method"
1384             # read only one but 2048-byte sized (scale: << 2) sector
1385             echo $bootoff $bootoff | \
1386               mksh ${SCRIPTS_DIRECTORY}/bootilnx.mksh -A -M 4:0x96 -g $cyls:16:32 -S 2
1387           fi | dd of="${ISO_OUTPUT}/${ISO_NAME}" conv=notrunc 2>/dev/null
1388           eend $?
1389         fi
1390       elif [ "$HYBRID_METHOD" = "isohybrid" ] ; then
1391         if ! which isohybrid >/dev/null 2>&1 ; then
1392           bailout 12 "isohybrid binary not found - please install syslinux/syslinux-common"
1393         else
1394           log   "Creating hybrid ISO file with isohybrid method"
1395           einfo "Creating hybrid ISO file with isohybrid method"
1396           # Notes for consideration:
1397           # "-entry 4 -type 1c"
1398           # * using 4 as the partition number is supposed to help with BIOSes
1399           #   that only support USB-Zip boot
1400           # * using 1c (i.e. hidden FAT32 LBA), instead of the default 0x17
1401           #   (hidden NTFS, IIRC), as the partition type is sometimes needed
1402           #   to get the BIOS even look at the partition created by isohybrid
1403           if isohybrid --help | grep -q -- --uefi ; then
1404             if echo $CLASSES | grep -qw I386 ; then
1405               log   "Detected uefi support for isohybrid but 32bit systems do not support it, ignoring."
1406               einfo "Detected uefi support for isohybrid but 32bit systems do not support it, ignoring."
1407             else
1408               log   "Detected uefi support for isohybrid, enabling"
1409               einfo "Detected uefi support for isohybrid, enabling"
1410               ISOHYBRID_OPTIONS=--uefi
1411             fi
1412           fi
1413
1414           log "isohybrid $ISOHYBRID_OPTIONS ${ISO_OUTPUT}/${ISO_NAME}"
1415           isohybrid $ISOHYBRID_OPTIONS "${ISO_OUTPUT}/${ISO_NAME}"
1416           eend $?
1417         fi
1418       else
1419         bailout 12 "Unknown HYBRID_METHOD [${HYBRID_METHOD}]. Supported values: disable, isohybrid, grub2, manifold"
1420       fi
1421
1422       # generate md5sum and sha1sum of ISO if we are using class 'RELEASE':
1423       case $CLASSES in *RELEASE*)
1424          [ "$RC" = 0 ] && \
1425          (
1426            if cd $ISO_OUTPUT ; then
1427              md5sum ${ISO_NAME} > ${ISO_NAME}.md5 && \
1428              touch -r ${ISO_NAME} ${ISO_NAME}.md5
1429              sha1sum ${ISO_NAME} > ${ISO_NAME}.sha1 && \
1430              touch -r ${ISO_NAME} ${ISO_NAME}.sha1
1431            fi
1432          )
1433          ;;
1434       esac
1435
1436       cd "$CURRENT_DIR"
1437    fi
1438
1439    if [ "$RC" = 0 ] ; then
1440       log   "Finished execution of stage 'iso build' [$(date)]"
1441       einfo "Finished execution of stage 'iso build'" ; eend 0
1442    else
1443       log    "Error: there was a critical error ($RC) executing stage 'iso build' [$(date)]"
1444       eerror "Error: there was a critical error executing stage 'iso build'" ; eend 1
1445       bailout $RC
1446    fi
1447 fi
1448 # }}}
1449
1450 # netboot package {{{
1451 create_netbootpackage() {
1452   local OUTPUT_FILE="${NETBOOT}/grml_netboot_package_${GRML_NAME}_${VERSION}.tar.bz2"
1453
1454   if [ -f "${OUTPUT_FILE}" -a -z "$UPDATE" -a -z "$BUILD_ONLY" -a -z "$BUILD_DIRTY" ] ; then
1455     log   "Skipping stage 'netboot' as $OUTPUT_FILE exists already."
1456     ewarn "Skipping stage 'netboot' as $OUTPUT_FILE exists already." ; eend 0
1457     return 0
1458   elif [ -n "$SKIP_NETBOOT" ] ; then
1459     log   "Skipping stage 'netboot' as requested via option -Q"
1460     ewarn "Skipping stage 'netboot' as requested via option -Q" ; eend 0
1461     return 0
1462   fi
1463
1464   mkdir -p "$NETBOOT"
1465
1466   # since syslinux v3:6.03~pre1+dfsg-4 the pxelinux.0 has been split into a
1467   # separate pxelinux package
1468   if [ -d "${CHROOT_OUTPUT}/usr/lib/PXELINUX/" ] ; then
1469     local pxelinux_dir=/usr/lib/PXELINUX
1470   else
1471     local pxelinux_dir=/usr/lib/syslinux
1472   fi
1473
1474   if ! [ -r "${CHROOT_OUTPUT}/${pxelinux_dir}/pxelinux.0" ] ; then
1475     ewarn "File ${pxelinux_dir}/pxelinux.0 not found in build chroot." ; eend 0
1476     eindent
1477     einfo "Install syslinux[-common]/pxelinux package in chroot to get a netboot package."
1478     eoutdent
1479     return 0
1480   fi
1481
1482   local OUTPUTDIR="${NETBOOT}/build_tmp"
1483   local WORKING_DIR="${OUTPUTDIR}/grml_netboot_package_${GRML_NAME}_${VERSION}/tftpboot/"
1484
1485   mkdir -p "$WORKING_DIR"
1486
1487   cp "${CHROOT_OUTPUT}"/boot/vmlinuz-*    "$WORKING_DIR"/vmlinuz
1488   cp "${CHROOT_OUTPUT}"/boot/initrd.img-* "$WORKING_DIR"/initrd.img
1489   cp "${CHROOT_OUTPUT}/${pxelinux_dir}/pxelinux.0" "${WORKING_DIR}/pxelinux.0"
1490
1491   mkdir -p "${WORKING_DIR}/pxelinux.cfg"
1492   if [ -r "${BUILD_OUTPUT}/boot/isolinux/netboot.cfg" ] ; then
1493     cp "${BUILD_OUTPUT}/boot/isolinux/netboot.cfg" "${WORKING_DIR}/pxelinux.cfg/default"
1494   else
1495     log   "File ${BUILD_OUTPUT}/boot/isolinux/netboot.cfg not found."
1496     ewarn "File ${BUILD_OUTPUT}/boot/isolinux/netboot.cfg not found."
1497     eindent
1498     log   "Hint: Are you using custom templates which do not provide netboot.cfg?"
1499     ewarn "Hint: Are you using custom templates which do not provide netboot.cfg?" ; eend 0
1500     eoutdent
1501   fi
1502
1503   if tar -C "$OUTPUTDIR" -jcf "${OUTPUT_FILE}" "grml_netboot_package_${GRML_NAME}_${VERSION}" ; then
1504     (
1505       cd $(dirname "${OUTPUT_FILE}")
1506       sha1sum $(basename "${OUTPUT_FILE}") > "${OUTPUT_FILE}.sha1"
1507     )
1508     einfo "Generated netboot package ${OUTPUT_FILE}" ; eend 0
1509     rm -rf "${OUTPUTDIR}"
1510   else
1511     rm -rf "${OUTPUTDIR}"
1512     eerror "Could not generate netboot package ${OUTPUT_FILE}" ; eend 1
1513     bailout 21
1514   fi
1515 }
1516
1517 create_netbootpackage
1518 # }}}
1519
1520 # log build information to database if grml-live-db is installed and enabled {{{
1521 dpkg_to_db() {
1522 if [ -d /usr/share/grml-live-db ] ; then
1523
1524   # safe defaults
1525   DPKG_LIST="/var/log/fai/$HOSTNAME/last/dpkg.list" # the dpkg --list output of the chroot:
1526   [ -n "$DPKG_DATABASE" ]  || DPKG_DATABASE=/var/log/grml-live.db
1527   [ -n "$DPKG_DBSCRIPT" ]  || DPKG_DBSCRIPT=/usr/share/grml-live-db/scripts/dpkg-to-db
1528   [ -n "$DPKG_DBOPTIONS" ] || DPKG_DBOPTIONS="--database $DPKG_DATABASE --logfile $LOGFILE --flavour $GRML_NAME --dpkg $DPKG_LIST"
1529
1530   if ! [ -x "$DPKG_DBSCRIPT" ] ; then
1531     log "Error: $DPKG_DBSCRIPT is not executable, can not log dpkg information."
1532     eerror "Error: $DPKG_DBSCRIPT is not executable, can not log dpkg information." ; eend 1
1533     bailout 14
1534   fi
1535
1536   # disable by default for now, not sure whether really everyone is using a local db file
1537   #if ! touch "$DPKG_DATABASE" ; then
1538   #  eerror "Error: can not write to ${DPKG_DATABASE}, can not log dpkg information." ; eend 1
1539   #  bailout 14
1540   #fi
1541
1542   if ! [ -r "$DPKG_LIST" ] ; then
1543      log   "Warning: can not read $DPKG_LIST - can not provide information to $DPKG_DBSCRIPT (dirty build?)"
1544      ewarn "Warning: can not read $DPKG_LIST - can not provide information to $DPKG_DBSCRIPT (dirty build?)" ; eend 0
1545   else
1546      einfo "Logging $DPKG_LIST to database $DPKG_DATABASE"
1547      log "Logging $DPKG_LIST to database $DPKG_DATABASE"
1548      log "Executing $DPKG_DBSCRIPT $DPKG_DBOPTIONS"
1549      eindent
1550
1551      if DB_INFO=$("$DPKG_DBSCRIPT" $DPKG_DBOPTIONS 2>&1) ; then
1552        einfo "$DB_INFO"
1553        eend 0
1554      else
1555        eerror "$DB_INFO"
1556        eend 1
1557      fi
1558
1559      eoutdent
1560   fi
1561
1562 fi
1563 }
1564 # }}}
1565
1566 # finalize {{{
1567 [ -n "$start_seconds" ] && SECONDS="$[$(cut -d . -f 1 /proc/uptime)-$start_seconds]" || SECONDS="unknown"
1568 log "Successfully finished execution of $PN [$(date) - running ${SECONDS} seconds]"
1569
1570 dpkg_to_db # make sure we catch the last log line as well, therefore execute between log + einfo
1571
1572 einfo "Successfully finished execution of $PN [$(date) - running ${SECONDS} seconds]" ; eend 0
1573 bailout 0
1574 # }}}
1575
1576 ## END OF FILE #################################################################
1577 # vim:foldmethod=marker ts=2 ft=sh ai expandtab tw=80 sw=2