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