SW: move multitail from GRML_FULL to GRMLBASE
[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 #   * On stable, the Debian packages are probably not available,
286 #     or outdated, so we look in TEMPLATE_DIRECTORY/compat first, where
287 #     our grml-live-compat package installs 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 grml-live-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='/grml/grml-live'
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   FAI_DEBOOTSTRAP="$SUITE http://ftp.debian.org/debian"
638 fi
639
640 if [ -z "$FAI_DEBOOTSTRAP_OPTS" ] ; then
641   FAI_DEBOOTSTRAP_OPTS="--exclude=info,tasksel,tasksel-data --include=aptitude --arch $ARCH"
642 fi
643
644 # create backup of old (not yet automatically generated) config file
645 if [ -f "${GRML_FAI_CONFIG}/make-fai-nfsroot.conf" ] ; then
646   if ! grep -q 'This is an automatically generated file by grml-live' "${GRML_FAI_CONFIG}/make-fai-nfsroot.conf" ; then
647     ewarn "Found old ${GRML_FAI_CONFIG}/make-fai-nfsroot.conf - moving to ${GRML_FAI_CONFIG}/make-fai-nfsroot.conf.outdated"
648     mv "${GRML_FAI_CONFIG}/make-fai-nfsroot.conf" "${GRML_FAI_CONFIG}/make-fai-nfsroot.conf.outdated"
649     eend $?
650   fi
651 fi
652
653 echo "# This is an automatically generated file by grml-live.
654 # Do NOT edit this file, your changes will be lost.
655 FAI_DEBOOTSTRAP=\"$FAI_DEBOOTSTRAP\"
656 FAI_DEBOOTSTRAP_OPTS=\"$FAI_DEBOOTSTRAP_OPTS\"
657 # EOF " > "${GRML_FAI_CONFIG}/nfsroot.conf"
658 # support FAI <=3.4.8, versions >=4.0 use nfsroot.conf
659 ( cd ${GRML_FAI_CONFIG} && ln -sf nfsroot.conf make-fai-nfsroot.conf )
660 # }}}
661
662 # CHROOT_OUTPUT - execute FAI {{{
663 if [ -n "$BUILD_DIRTY" ]; then
664    log   "Skipping stage 'fai' as requested via option -B"
665    ewarn "Skipping stage 'fai' as requested via option -B" ; eend 0
666 else
667    [ -n "$CHROOT_OUTPUT" ] || CHROOT_OUTPUT="$OUTPUT/grml_chroot"
668
669    if [ -n "$UPDATE" -o -n "$BUILD_ONLY" ] ; then
670       FAI_ACTION=softupdate
671    else
672       FAI_ACTION=dirinstall
673    fi
674
675    if [ -n "$UPDATE" -o -n "$BUILD_ONLY" ] ; then
676       if ! [ -r "$CHROOT_OUTPUT/etc/debian_version" ] ; then
677          log    "Error: does not look like you have a working chroot. Updating/building not possible."
678          eerror "Error: does not look like you have a working chroot. Updating/building not possible. (Drop -u/-b option?)"
679          eend 1
680          bailout 20
681       fi
682    fi
683
684    if [ -d "$CHROOT_OUTPUT/bin" -a -z "$UPDATE" -a -z "$BUILD_ONLY" ] ; then
685       log   "Skipping stage 'fai dirinstall' as $CHROOT_OUTPUT exists already."
686       ewarn "Skipping stage 'fai dirinstall' as $CHROOT_OUTPUT exists already." ; eend 0
687    else
688       mkdir -p "$CHROOT_OUTPUT" || bailout 5 "Problem with creating $CHROOT_OUTPUT for FAI"
689
690       if [ -n "${MIRROR_DIRECTORY}" ] ; then
691          mkdir -p "${CHROOT_OUTPUT}/${MIRROR_DIRECTORY}"
692          mount --bind "${MIRROR_DIRECTORY}" "${CHROOT_OUTPUT}/${MIRROR_DIRECTORY}"
693       fi
694
695       mkdir -p "${OUTPUT}/grml_sources/" "${CHROOT_OUTPUT}/grml-live/sources/"
696       mount --bind "${OUTPUT}/grml_sources/" "${CHROOT_OUTPUT}/grml-live/sources/"
697
698       log "Executed FAI command line:"
699       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"
700       BUILD_ONLY="$BUILD_ONLY" BOOTSTRAP_ONLY="$BOOTSTRAP_ONLY" GRML_LIVE_CONFIG="$CONFIGDUMP" fai $VERBOSE \
701                   -C "$GRML_FAI_CONFIG" -s "file:///$GRML_FAI_CONFIG/config" -c"$CLASSES" \
702                   -u "$HOSTNAME" "$FAI_ACTION" "$CHROOT_OUTPUT" $FAI_ARGS | tee -a $LOGFILE
703       RC="$PIPESTATUS" # notice: bash-only
704
705       if [ "$RC" != 0 ] ; then
706          log    "Error: critical error while executing fai [exit code ${RC}]. Exiting."
707          eerror "Error: critical error while executing fai [exit code ${RC}]. Exiting." ; eend 1
708          bailout 1
709       fi
710
711       # provide inform fai about the ISO we build, needs to be provided
712       # *after* FAI stage, otherwise FAI skips the debootstrap stage if
713       # there is not BASEFILE (as it checks for presence of /etc) :(
714       echo '# This file has been generated by grml-live.' > "$CHROOT_OUTPUT/etc/grml_live_version"
715       [ -n "$GRML_LIVE_VERSION" ] && echo "GRML_LIVE_VERSION=$GRML_LIVE_VERSION" >> "$CHROOT_OUTPUT/etc/grml_live_version"
716       [ -n "$SUITE" ] && echo "SUITE=$SUITE" >> "$CHROOT_OUTPUT/etc/grml_live_version"
717
718       FORCE_ISO_REBUILD=true
719
720       # move fai logs into grml_logs directory
721       mkdir -p "$LOG_OUTPUT"/fai/
722       cp -r "$CHROOT_OUTPUT"/var/log/fai/"$HOSTNAME"/last/* "$LOG_OUTPUT"/fai/
723       rm -rf "$CHROOT_OUTPUT"/var/log/fai
724
725       # store copy of autogenerated configuration file
726       cp ${GRML_FAI_CONFIG}/nfsroot.conf "$LOG_OUTPUT"/fai/
727
728       # copy fai package list
729       cp "$CHROOT_OUTPUT"/var/log/install_packages.list "$LOG_OUTPUT"/fai/
730       # fixup owners
731       chown root:adm "$LOG_OUTPUT"/fai/*
732       chmod 664 "$LOG_OUTPUT"/fai/*
733
734       umount_all
735
736       # notice: 'fai dirinstall' does not seem to exit appropriate, so:
737       ERROR=''
738       CHECKLOG="$LOG_OUTPUT"/fai/
739       if [ -r "$CHECKLOG/software.log" ] ; then
740          # 1 errors during executing of commands
741          grep 'dpkg: error processing' $CHECKLOG/software.log >> $LOGFILE && ERROR=1
742          grep 'E: Method http has died unexpectedly!' $CHECKLOG/software.log >> $LOGFILE && ERROR=2
743          grep 'ERROR: chroot' $CHECKLOG/software.log >> $LOGFILE && ERROR=3
744          grep 'E: Failed to fetch' $CHECKLOG/software.log >> $LOGFILE && ERROR=4
745          grep 'Unable to write mmap - msync (28 No space left on device)' $CHECKLOG/software.log >> $LOGFILE && ERROR=5
746       fi
747
748       if [ -r "$CHECKLOG/shell.log" ] ; then
749          grep 'FAILED with exit code' $CHECKLOG/shell.log >> $LOGFILE && ERROR=6
750       fi
751
752       if [ -r "$CHECKLOG/fai.log" ] ; then
753         grep 'updatebase.*FAILED with exit code' "$CHECKLOG/fai.log" >> "$LOGFILE" && ERROR=7
754         grep 'instsoft.*FAILED with exit code'   "$CHECKLOG/fai.log" >> "$LOGFILE" && ERROR=8
755       fi
756
757       if [ -n "$ERROR" ] ; then
758          log    "Error: there was a critical error [${ERROR}] during execution of stage 'fai dirinstall' [$(date)]"
759          eerror "Error: there was a critical error during execution of stage 'fai dirinstall'"
760          eerror "Note:  check out ${CHECKLOG}/ for details. [exit ${ERROR}]"
761          eend 1
762          bailout 1
763       else
764          log "Finished execution of stage 'fai dirinstall' [$(date)]"
765          einfo "Finished execution of stage 'fai dirinstall'"
766       fi
767    fi
768 fi # BUILD_DIRTY?
769 # }}}
770
771 # package validator {{{
772 CHECKLOG=/var/log/fai/$HOSTNAME/last
773 if [ -r "$CHECKLOG/dpkg.selections" ] ; then
774   package_count=$(wc -l "$CHECKLOG/dpkg.selections" | awk '{print $1}')
775 else
776   package_count="unknown"
777 fi
778
779 mkdir -p "$REPORTS"
780 REPORT_MISSING_PACKAGES="${REPORTS}/TEST-MissingPackages.xml"
781
782 # check for missing packages
783 if ! [ -s "$CHECKLOG/package_errors.log" ] ; then
784   einfo "No missing packages found, generating empty junit report."
785
786   cat > "${REPORT_MISSING_PACKAGES}" << EOF
787 <?xml version="1.0" encoding="UTF-8"?>
788 <testsuite name="grml-live-missing-packages" tests="${package_count}" time="1" failures="0" errors="0" skipped="0" assertions="0">
789   <testcase name="test_missing_packages" time="0" assertions="0">
790   </testcase>
791   <system-out>
792   </system-out>
793   <system-err>
794   </system-err>
795 </testsuite>
796 EOF
797   eend 0
798 else
799   einfo "Missing packages found, generating junit report."
800
801   if [ -r "$CHECKLOG/package_errors.log" ] ; then
802     package_errors=$(wc -l "$CHECKLOG/package_errors.log" | awk '{print $1}')
803   else
804     package_errors="unknown"
805   fi
806
807   mkdir -p "$REPORTS"
808   REPORT_MISSING_PACKAGES="${REPORTS}/TEST-MissingPackages.xml"
809
810   cat > "${REPORT_MISSING_PACKAGES}" << EOF
811 <?xml version="1.0" encoding="UTF-8"?>
812 <testsuite name="grml-live-missing-packages" tests="${package_count}" time="1" failures="${package_errors}" errors="${package_errors}" skipped="0" assertions="0">
813 EOF
814
815   for package in $(awk '{print $1}' "${CHECKLOG}/package_errors.log" | sed 's;/;\\/;') ; do
816     failure_reason="$(awk "/$package/ {print \$2}" "${CHECKLOG}/package_errors.log")"
817     cat >> "${REPORT_MISSING_PACKAGES}" << EOF
818   <testcase name="test_missing_packages_${package}" time="0" assertions="0">
819     <failure type="${failure_reason}" message="Package ${package} is missing">
820 Package $package is missing in chroot (${failure_reason})
821   </failure>
822   </testcase>
823 EOF
824   done
825
826   cat >> "${REPORT_MISSING_PACKAGES}" << EOF
827   <system-out>
828   </system-out>
829   <system-err>
830   </system-err>
831 </testsuite>
832 EOF
833   eend 0
834
835   if [ -n "$EXIT_ON_MISSING_PACKAGES" -a -z "$BUILD_DIRTY" ] ; then
836     eerror "The following packages were requested for installation but could not be processed:"
837     cat "$CHECKLOG/package_errors.log"
838     eerror "... exiting as requested via \$EXIT_ON_MISSING_PACKAGES."
839     eend 1
840     bailout 13
841   else
842     ewarn "The following packages were requested for installation but could not be processed:"
843     cat "$CHECKLOG/package_errors.log"
844     eend 0
845   fi
846 fi
847 # }}}
848
849 # BUILD_OUTPUT - execute arch specific stuff and squashfs {{{
850 [ -n "$BUILD_OUTPUT" ] || BUILD_OUTPUT="$OUTPUT/grml_cd"
851 mkdir -p "$BUILD_OUTPUT" || bailout 6 "Problem with creating $BUILD_OUTPUT for stage ARCH"
852
853 # prepare ISO
854 if [ "$ARCH" = i386 ] || [ "$ARCH" = amd64 ] ; then
855   if [ -n "$BOOTSTRAP_ONLY" ] ; then
856      log   "Skipping stage 'boot' as building with bootstrap only."
857      ewarn "Skipping stage 'boot' as building with bootstrap only." ; eend 0
858   else
859     if [ -d "$BUILD_OUTPUT"/boot/isolinux -a -z "$UPDATE" -a -z "$BUILD_ONLY" ] ; then
860        log   "Skipping stage 'boot' as $BUILD_OUTPUT/boot/isolinux exists already."
861        ewarn "Skipping stage 'boot' as $BUILD_OUTPUT/boot/isolinux exists already." ; eend 0
862     else
863        # booting stuff:
864        [ -d "$BUILD_OUTPUT"/boot/isolinux ] || mkdir -p "$BUILD_OUTPUT"/boot/isolinux
865        [ -d "$BUILD_OUTPUT"/boot/"${SHORT_NAME}" ] || mkdir -p "$BUILD_OUTPUT"/boot/"${SHORT_NAME}"
866
867        # if we don't have an initrd we a) can't boot and b) there was an error
868        # during build, so check for the file:
869        INITRD="$(ls $CHROOT_OUTPUT/boot/initrd* 2>/dev/null| grep -v '.bak$' | sort -r | head -1)"
870        if [ -n "$INITRD" ] ; then
871           cp $INITRD "$BUILD_OUTPUT"/boot/"${SHORT_NAME}"/initrd.img
872           find $CHROOT_OUTPUT/boot/ -name initrd\*.bak -exec rm {} \;
873        else
874           log    "Error: No initrd found inside $CHROOT_OUTPUT/boot/ - Exiting"
875           eerror "Error: No initrd found inside $CHROOT_OUTPUT/boot/ - Exiting" ; eend 1
876           bailout 10
877        fi
878
879        KERNEL_IMAGE="$(ls $CHROOT_OUTPUT/boot/vmlinuz* 2>/dev/null | sort -r | head -1)"
880        if [ -n "$KERNEL_IMAGE" ] ; then
881           cp "$KERNEL_IMAGE" "$BUILD_OUTPUT"/boot/"${SHORT_NAME}"/vmlinuz
882        else
883           log    "Error: No kernel found inside $CHROOT_OUTPUT/boot/ - Exiting"
884           eerror "Error: No kernel found inside $CHROOT_OUTPUT/boot/ - Exiting" ; eend 1
885           bailout 11
886        fi
887
888        # EFI boot files
889        if [ -r "${CHROOT_OUTPUT}/boot/efi.img" -a -r "${CHROOT_OUTPUT}/boot/bootx64.efi" ] ; then
890           einfo "Moving EFI boot files into ISO path."
891           log "Moving EFI boot files into ISO path."
892           RC=$0
893           mv "${CHROOT_OUTPUT}/boot/efi.img" "${BUILD_OUTPUT}/boot/" || RC=$?
894           mkdir -p "${BUILD_OUTPUT}/efi/boot/" || RC=$?
895           mv "${CHROOT_OUTPUT}/boot/bootx64.efi" "${BUILD_OUTPUT}/efi/boot/bootx64.efi" || RC=$?
896           eend $?
897        fi
898
899        [ -n "$TEMPLATE_DIRECTORY" ] || TEMPLATE_DIRECTORY='/usr/share/grml-live/templates'
900        if ! [ -d "${TEMPLATE_DIRECTORY}"/boot ] ; then
901           log    "Error: ${TEMPLATE_DIRECTORY}/boot does not exist. Exiting."
902           eerror "Error: ${TEMPLATE_DIRECTORY}/boot does not exist. Exiting." ; eend 1
903           bailout 8
904        fi
905
906        # copy _required_ isolinux files
907        if [ -d "${CHROOT_OUTPUT}/usr/lib/ISOLINUX" ] ; then
908          copy_addon_file isolinux.bin /usr/lib/ISOLINUX isolinux
909          for file in ${CHROOT_OUTPUT}/usr/lib/syslinux/modules/bios/*.c32 ; do
910            copy_addon_file "$(basename "$file")"  /usr/lib/syslinux/modules/bios/ isolinux
911          done
912        else # syslinux versions <= 3:4.05+dfsg-6+deb8u1
913          copy_addon_file isolinux.bin /usr/lib/syslinux isolinux
914          copy_addon_file ifcpu64.c32  /usr/lib/syslinux isolinux
915          copy_addon_file vesamenu.c32 /usr/lib/syslinux isolinux
916        fi
917
918        # *always* copy files to output directory so the variables
919        # get adjusted according to the build.
920        cp ${TEMPLATE_DIRECTORY}/boot/isolinux/*  "$BUILD_OUTPUT"/boot/isolinux/
921
922        mkdir -p "${BUILD_OUTPUT}/boot/grub"
923        cp -a ${TEMPLATE_DIRECTORY}/boot/grub/* "$BUILD_OUTPUT"/boot/grub/
924
925        if [ -n "$NO_ADDONS" ] ; then
926           rm -f "$BUILD_OUTPUT"/boot/grub/addons.cfg
927           log   "Skipping installation of boot addons as requested via \$NO_ADDONS."
928           einfo "Skipping installation of boot addons as requested via \$NO_ADDONS."; eend 0
929        else
930           if ! [ -d "$TEMPLATE_DIRECTORY"/boot/addons ] ; then
931             log   "Boot addons not found, skipping therefore. (Consider installing package grml-live-addons)"
932             ewarn "Boot addons not found, skipping therefore. (Consider installing package grml-live-addons)" ; eend 0
933           else
934             # copy addons from system packages or grml-live-compat
935             copy_addon_file ipxe.lkrn /usr/lib/ipxe addons
936             copy_addon_file pci.ids /usr/share/misc addons
937             copy_addon_file memtest86+.bin /boot addons
938
939             # since syslinux(-common) v3:6.03~pre1+dfsg-4 the files are in a
940             # different directory :(
941             if [ -d "${CHROOT_OUTPUT}/usr/lib/syslinux/modules/bios/" ] ; then
942               syslinux_modules_dir=/usr/lib/syslinux/modules/bios/
943             else
944               syslinux_modules_dir=/usr/lib/syslinux
945             fi
946             for file in chain.c32 hdt.c32 mboot.c32 menu.c32; do
947               copy_addon_file "${file}" "${syslinux_modules_dir}" addons
948             done
949
950             copy_addon_file memdisk /usr/lib/syslinux addons
951
952             # make memtest filename FAT16/8.3 compatible
953             mv "${BUILD_OUTPUT}/boot/addons/memtest86+.bin" \
954               "${BUILD_OUTPUT}/boot/addons/memtest"
955
956             # copy only files so we can handle bsd4grml on its own
957             for file in ${TEMPLATE_DIRECTORY}/boot/addons/* ; do
958               test -f $file && cp $file "$BUILD_OUTPUT"/boot/addons/
959             done
960
961             if [ -n "$NO_ADDONS_BSD4GRML" ] ; then
962                log   "Skipping installation of bsd4grml as requested via \$NO_ADDONS_BSD4GRML."
963                einfo "Skipping installation of bsd4grml as requested via \$NO_ADDONS_BSD4GRML."; eend 0
964             else
965                if [ -d "$TEMPLATE_DIRECTORY"/boot/addons/bsd4grml ] ; then
966                  cp -a ${TEMPLATE_DIRECTORY}/boot/addons/bsd4grml "$BUILD_OUTPUT"/boot/addons/
967                else
968                  log   "Missing addon file: bsd4grml"
969                  ewarn "Missing addon file: bsd4grml" ; eend 0
970                fi
971             fi
972
973           fi # no "$TEMPLATE_DIRECTORY"/boot/addons
974        fi # NO_ADDONS
975
976        # generate loopback.cfg config file without depending on grub's regexp module
977        # which isn't available in Debian/squeeze
978        echo "## grub2 loopback configuration" > "${BUILD_OUTPUT}"/boot/grub/loopback.cfg
979        echo "source /boot/grub/header.cfg" >> "${BUILD_OUTPUT}"/boot/grub/loopback.cfg
980        for config in "${BUILD_OUTPUT}"/boot/grub/*_default.cfg "${BUILD_OUTPUT}"/boot/grub/*_options.cfg ; do
981          [ -r "$config" ] || continue
982          echo "source ${config##$BUILD_OUTPUT}" >> "${BUILD_OUTPUT}"/boot/grub/loopback.cfg
983        done
984        if [ -z "$NO_ADDONS" ] ; then
985          echo "source /boot/grub/addons.cfg" >> "${BUILD_OUTPUT}"/boot/grub/loopback.cfg
986        fi
987        echo "source /boot/grub/footer.cfg" >> "${BUILD_OUTPUT}"/boot/grub/loopback.cfg
988
989        # copy grub files from target
990        mkdir -p "${BUILD_OUTPUT}"/boot/grub/i386-pc/
991        cp -a "${CHROOT_OUTPUT}"/usr/lib/grub/*-pc/*.mod "${BUILD_OUTPUT}"/boot/grub/i386-pc/
992        cp -a "${CHROOT_OUTPUT}"/usr/lib/grub/*-pc/*.o "${BUILD_OUTPUT}"/boot/grub/i386-pc/
993        cp -a "${CHROOT_OUTPUT}"/usr/lib/grub/*-pc/*.lst "${BUILD_OUTPUT}"/boot/grub/i386-pc/
994        cp -a "${CHROOT_OUTPUT}"/usr/share/grub/ascii.pf2 "${BUILD_OUTPUT}"/boot/grub/
995        cp -a "${CHROOT_OUTPUT}"/boot/grub/core.img "${BUILD_OUTPUT}"/boot/grub/
996        cp -a "${CHROOT_OUTPUT}"/boot/grub/grub.img "${BUILD_OUTPUT}"/boot/grub/
997
998        # copy modules for UEFI grub
999        mkdir -p "${BUILD_OUTPUT}"/boot/grub/x86_64-efi/
1000        cp -a "${CHROOT_OUTPUT}"/usr/lib/grub/x86_64-efi/*.{mod,lst} "${BUILD_OUTPUT}"/boot/grub/x86_64-efi/
1001
1002        if ! [ -d "${TEMPLATE_DIRECTORY}"/GRML ] ; then
1003           log    "Error: ${TEMPLATE_DIRECTORY}/GRML does not exist. Exiting."
1004           eerror "Error: ${TEMPLATE_DIRECTORY}/GRML does not exist. Exiting." ; eend 1
1005           bailout 9
1006        fi
1007
1008        mkdir -p "$BUILD_OUTPUT"/GRML/"${GRML_NAME}"/
1009        cp -a ${TEMPLATE_DIRECTORY}/GRML/* "$BUILD_OUTPUT"/GRML/"${GRML_NAME}"/
1010
1011        # adjust boot splash information:
1012        RELEASE_INFO="$GRML_NAME $VERSION - Release Codename $RELEASENAME"
1013        RELEASE_INFO="$(cut_string 68 "$RELEASE_INFO")"
1014        RELEASE_INFO="$(extend_string_end 68 "$RELEASE_INFO")"
1015
1016        if [ -r "$BUILD_OUTPUT"/GRML/"${GRML_NAME}"/grml-version ] ; then
1017           sed -i "s/%RELEASE_INFO%/$GRML_NAME $VERSION - $RELEASENAME/" "$BUILD_OUTPUT"/GRML/"${GRML_NAME}"/grml-version
1018           sed -i "s/%DATE%/$DATE/"                                      "$BUILD_OUTPUT"/GRML/"${GRML_NAME}"/grml-version
1019        fi
1020
1021        # make sure the squashfs filename is set accordingly:
1022        SQUASHFS_NAME="$GRML_NAME.squashfs"
1023
1024        if [ -n "$NO_BOOTID" ] ; then
1025           log   'Skipping bootid feature as requested via $NO_BOOTID.'
1026           einfo 'Skipping bootid feature as requested via $NO_BOOTID.'
1027        else
1028           [ -n "$BOOTID" ] || BOOTID="$(echo ${GRML_NAME}${VERSION} | tr -d ',./;\- ')"
1029           [ -d "$BUILD_OUTPUT"/conf ] || mkdir "$BUILD_OUTPUT"/conf
1030           einfo "Generating /conf/bootid.txt with entry ${BOOTID}."
1031           log   "Generating /conf/bootid.txt with entry ${BOOTID}."
1032           echo "$BOOTID" > "$BUILD_OUTPUT"/conf/bootid.txt
1033           eend $?
1034        fi
1035
1036        # adjust all variables in the templates with the according distribution information
1037        for file in "${BUILD_OUTPUT}"/boot/isolinux/*.cfg "${BUILD_OUTPUT}"/boot/isolinux/*.msg \
1038                    "${BUILD_OUTPUT}"/boot/grub/* ; do
1039          if [ -r "${file}" ] && [ -f "${file}" ] ; then
1040            sed -i "s/%ARCH%/$ARCH/g"                    "${file}"
1041            sed -i "s/%DATE%/$DATE/g"                    "${file}"
1042            sed -i "s/%DISTRI_INFO%/$DISTRI_INFO/g"      "${file}"
1043            sed -i "s/%DISTRI_NAME%/$DISTRI_NAME/g"      "${file}"
1044            sed -i "s/%DISTRI_SPLASH%/$DISTRI_SPLASH/g"  "${file}"
1045            sed -i "s/%GRML_NAME%/$GRML_NAME/g"          "${file}"
1046            sed -i "s/%SQUASHFS_NAME%/$SQUASHFS_NAME/g"  "${file}"
1047            sed -i "s/%RELEASE_INFO%/$RELEASE_INFO/g"    "${file}"
1048            sed -i "s/%SHORT_NAME%/$SHORT_NAME/g"        "${file}"
1049            sed -i "s/%VERSION%/$VERSION/g"              "${file}"
1050
1051            [ -n "$DEFAULT_BOOTOPTIONS" ] && sed -i "s; boot=live; boot=live $DEFAULT_BOOTOPTIONS;"  "${file}"
1052
1053            if [ -n "$NO_BOOTID" ] ; then
1054               sed -i "s/ bootid=%BOOTID%//g" "${file}" # drop bootid bootoption
1055            else
1056               sed -i "s/%BOOTID%/$BOOTID/g" "${file}" # adjust bootid=... argument
1057            fi
1058          fi
1059        done
1060
1061        for param in ARCH DATE DISTRI_INFO DISTRI_NAME DISTRI_SPLASH GRML_NAME SQUASHFS_NAME \
1062            RELEASE_INFO SHORT_NAME VERSION ; do
1063            for file in $(find "${BUILD_OUTPUT}" -name "*%$param%*") ; do
1064                value="$(eval echo '$'"$param")"
1065                mv ${file} ${file/\%${param}\%/$value}
1066            done
1067        done
1068
1069        # adjust bootsplash accordingly but make sure the string has the according lenght
1070        SQUASHFS_NAME="$(cut_string 20 "$SQUASHFS_NAME")"
1071        SQUASHFS_NAME="$(extend_string_end 20 "$SQUASHFS_NAME")"
1072        for file in f4 f5 ; do
1073           if [ -r "${BUILD_OUTPUT}/boot/isolinux/${file}" ] ; then
1074              sed -i "s/%SQUASHFS_NAME%/$SQUASHFS_NAME/" "${BUILD_OUTPUT}/boot/isolinux/${file}"
1075              sed -i "s/%SQUASHFS_NAME%/$SQUASHFS_NAME/" "${BUILD_OUTPUT}/boot/isolinux/${file}"
1076           fi
1077        done
1078
1079        # generate addon list
1080        rm -f "${BUILD_OUTPUT}/${ADDONS_LIST_FILE}"
1081        for name in "${BUILD_OUTPUT}"/boot/isolinux/addon_*.cfg ; do
1082          include_name=$(basename "$name")
1083          echo "include $include_name"  >> "${BUILD_OUTPUT}/${ADDONS_LIST_FILE}"
1084        done
1085
1086        if ! [ -r "${BUILD_OUTPUT}/boot/isolinux/${DISTRI_NAME}.cfg" ] || [ "$DISTRI_NAME" = "grml" ] ; then
1087           log "including grmlmain.cfg in ${BUILD_OUTPUT}/boot/isolinux/distri.cfg"
1088           echo "include grmlmain.cfg"    >  "${BUILD_OUTPUT}/boot/isolinux/distri.cfg"
1089           echo "include default.cfg"     >  "${BUILD_OUTPUT}/boot/isolinux/grmlmain.cfg"
1090           echo "include menuoptions.cfg" >> "${BUILD_OUTPUT}/boot/isolinux/grmlmain.cfg"
1091           echo "include grml.cfg"        >> "${BUILD_OUTPUT}/boot/isolinux/grmlmain.cfg"
1092
1093           for f in "${BUILD_OUTPUT}"/boot/isolinux/submenu*.cfg ; do
1094             echo "include $(basename $f)"     >> "${BUILD_OUTPUT}/boot/isolinux/grmlmain.cfg"
1095           done
1096
1097           echo "include options.cfg"     >> "${BUILD_OUTPUT}/boot/isolinux/grmlmain.cfg"
1098           if [ -z "$NO_ADDONS" ] ; then
1099             echo "include addons.cfg"    >> "${BUILD_OUTPUT}/boot/isolinux/grmlmain.cfg"
1100           fi
1101           echo "include isoprompt.cfg"   >> "${BUILD_OUTPUT}/boot/isolinux/grmlmain.cfg"
1102           echo "include hd.cfg"          >> "${BUILD_OUTPUT}/boot/isolinux/grmlmain.cfg"
1103           echo "include hidden.cfg"      >> "${BUILD_OUTPUT}/boot/isolinux/grmlmain.cfg"
1104        else # assume we are building a custom distribution:
1105           log "File ${BUILD_OUTPUT}/boot/isolinux/${DISTRI_NAME}.cfg found, using it."
1106           einfo "File ${BUILD_OUTPUT}/boot/isolinux/${DISTRI_NAME}.cfg found, using it."
1107           if grep -q "^include ${DISTRI_NAME}.cfg" "${BUILD_OUTPUT}/boot/isolinux/distri.cfg" ; then
1108             log "include for ${DISTRI_NAME}.cfg already present, nothing to do."
1109             eindent
1110             einfo "include for ${DISTRI_NAME}.cfg already present, nothing to do."
1111             eoutdent
1112             eend $?
1113          else
1114             log "including ${DISTRI_NAME}.cfg in ${BUILD_OUTPUT}/boot/isolinux/distri.cfg"
1115             echo "include ${DISTRI_NAME}.cfg" > "${BUILD_OUTPUT}/boot/isolinux/distri.cfg"
1116             if [ -z "$NO_ADDONS" ] ; then
1117               echo "include addons.cfg" >> "${BUILD_OUTPUT}/boot/isolinux/distri.cfg"
1118             fi
1119           fi
1120        fi
1121
1122        # use old style console based isolinux method only if requested:
1123        if [[ "${ISOLINUX_METHOD}" == "console" ]] ; then
1124           log 'Using console based isolinux method as requested via $ISOLINUX_METHOD.'
1125           einfo 'Using console based isolinux method as requested via $ISOLINUX_METHOD.'
1126           if grep -q '^include console.cfg' "${BUILD_OUTPUT}/boot/isolinux/distri.cfg" ; then
1127             einfo "include for console.cfg already found, nothing to do."
1128             eend 0
1129           else
1130             log "including console.cfg in ${BUILD_OUTPUT}/boot/isolinux/isolinux.cfg"
1131             einfo "including console.cfg in ${BUILD_OUTPUT}/boot/isolinux/isolinux.cfg"
1132             echo "include console.cfg" >> "${BUILD_OUTPUT}/boot/isolinux/isolinux.cfg"
1133             eend $?
1134           fi
1135        else
1136           log 'Using graphical boot menu.'
1137           if grep -q '^include vesamenu.cfg' "${BUILD_OUTPUT}/boot/isolinux/isolinux.cfg" ; then
1138             log "include for vesamenu.cfg already found, nothing to do."
1139           else
1140             log "including vesamenu.cfg in ${BUILD_OUTPUT}/boot/isolinux/isolinux.cfg"
1141             echo "include vesamenu.cfg" >> "${BUILD_OUTPUT}/boot/isolinux/isolinux.cfg"
1142           fi
1143        fi
1144
1145        if [ -e "$BUILD_OUTPUT"/boot/addons/bsd4grml/boot.6 ]; then
1146           sed -i "s/%RELEASE_INFO%/$GRML_NAME $VERSION - $RELEASENAME/" "$BUILD_OUTPUT"/boot/addons/bsd4grml/boot.6
1147        fi
1148
1149        DPKG_LIST="/var/log/fai/$HOSTNAME/last/dpkg.list" # the dpkg --list output of the chroot
1150        if ! [ -r "$DPKG_LIST" ] ; then
1151           ewarn "$DPKG_LIST could not be read, ignoring to store package information on ISO therefore."
1152        else
1153           einfo "Storing package list information as /GRML/${GRML_NAME}/packages.txt on ISO."
1154           cp "$DPKG_LIST" "${BUILD_OUTPUT}"/GRML/"${GRML_NAME}"/packages.txt
1155           eend $?
1156        fi
1157
1158        # autostart for Windows:
1159        if [ -d "${TEMPLATE_DIRECTORY}/windows/autostart/" ] ; then
1160           cp ${TEMPLATE_DIRECTORY}/windows/autostart/* "$BUILD_OUTPUT"/
1161        fi
1162
1163     FORCE_ISO_REBUILD=true
1164     einfo "Finished execution of stage 'boot'" ; eend 0
1165     fi
1166   fi # BOOTSTRAP_ONLY
1167 else
1168    log    'Error: Unsupported ARCH, sorry. Want to support it? Contribute!'
1169    eerror 'Error: Unsupported ARCH, sorry. Want to support it? Contribute!' ; eend 1
1170    bailout
1171 fi
1172
1173 # support installation of local files into the chroot/ISO
1174 if [ -n "$CHROOT_INSTALL" ] ; then
1175   if ! [ -d "$CHROOT_INSTALL" ] ; then
1176      log "Configuration variable \$CHROOT_INSTALL is set but not a directory; ignoring"
1177      ewarn "Configuration variable \$CHROOT_INSTALL is set but not a directory; ignoring"
1178   else
1179      log "Copying local files to chroot as requested via \$CHROOT_INSTALL"
1180      einfo "Copying local files to chroot as requested via \$CHROOT_INSTALL"
1181      rsync -avz --inplace "$CHROOT_INSTALL"/ "$CHROOT_OUTPUT/"
1182      eend $?
1183      einfo "Make sure to run squashfs stage, otherwise your local files won't be part of the ISO."
1184      FORCE_ISO_REBUILD=true
1185   fi
1186 fi
1187
1188 if [ -f "$BUILD_OUTPUT"/live/${GRML_NAME}.squashfs -a -z "$UPDATE" -a -z "$BUILD_ONLY" -a -z "$BUILD_DIRTY" ] ; then
1189    log   "Skipping stage 'squashfs' as $BUILD_OUTPUT/live exists already."
1190    ewarn "Skipping stage 'squashfs' as $BUILD_OUTPUT/live exists already." ; eend 0
1191 elif [ -n "$SKIP_MKSQUASHFS" ] ; then
1192    log   "Skipping stage 'squashfs' as requested via option -q or -N"
1193    ewarn "Skipping stage 'squashfs' as requested via option -q or -N" ; eend 0
1194 else
1195    mkdir -p "$BUILD_OUTPUT"/live/"${GRML_NAME}"/
1196    # make sure we don't leave (even an empty) base.tgz:
1197    [ -f "$CHROOT_OUTPUT/base.tgz" ] && rm -f "$CHROOT_OUTPUT/base.tgz"
1198
1199    # if unconfigured default to squashfs-tools' mksquashfs binary
1200    if [ -z "$SQUASHFS_BINARY" ] ; then
1201       SQUASHFS_BINARY='mksquashfs'
1202    fi
1203
1204    if which "$SQUASHFS_BINARY" >/dev/null 2>&1 ; then
1205       log    "Using mksquashfs binary ${SQUASHFS_BINARY}"
1206       einfo  "Using mksquashfs binary ${SQUASHFS_BINARY}" ; eend 0
1207    else
1208       log    "Error: mksquashfs binary ($SQUASHFS_BINARY) not found. Exiting."
1209       eerror "Error: mksquashfs binary ($SQUASHFS_BINARY) not found. Exiting." ; eend 1
1210       bailout
1211    fi
1212
1213    # use sane defaults if $SQUASHFS_OPTIONS isn't set
1214    if [ -z "$SQUASHFS_OPTIONS" ] ; then
1215      # use blocksize 256k as this gives best result with regards to time + compression
1216      SQUASHFS_OPTIONS="-b 256k"
1217
1218      # set lzma/xz compression by default, unless -z option has been specified on command line
1219      if [ -z "$SQUASHFS_ZLIB" ] ; then
1220         SQUASHFS_OPTIONS="$SQUASHFS_OPTIONS -comp xz"
1221      else
1222         SQUASHFS_OPTIONS="$SQUASHFS_OPTIONS -comp gzip"
1223      fi
1224    fi
1225
1226    # support exclusion of files via exclude-file:
1227    if [ -n "$SQUASHFS_EXCLUDES_FILE" -a "$SQUASHFS_EXCLUDES_FILE" ] ; then
1228       SQUASHFS_OPTIONS="$SQUASHFS_OPTIONS -ef $SQUASHFS_EXCLUDES_FILE -wildcards"
1229    fi
1230
1231    # get rid of unnecessary files when building grml-small for final release:
1232    if echo "$CLASSES" | grep -q GRML_SMALL ; then
1233       SQUASHFS_OPTIONS="$SQUASHFS_OPTIONS -e initrd.img* vmlinuz*"
1234    fi
1235
1236    # log stuff
1237    SQUASHFS_STDERR="$(mktemp -t grml-live.XXXXXX)"
1238
1239    # informational stuff
1240    [ -n "$SQUASHFS_OPTIONS" ]  && SQUASHFS_INFO_MSG="$SQUASHFS_OPTIONS"
1241    [ -n "$SQUASHFS_INFO_MSG" ] && SQUASHFS_INFO_MSG="using options: $SQUASHFS_INFO_MSG"
1242    einfo "Squashfs build information: running binary $SQUASHFS_BINARY $SQUASHFS_INFO_MSG"
1243
1244    log "$SQUASHFS_BINARY $CHROOT_OUTPUT/ $BUILD_OUTPUT/live/${GRML_NAME}/${GRML_NAME}.squashfs -noappend $SQUASHFS_OPTIONS"
1245
1246    if $SQUASHFS_BINARY $CHROOT_OUTPUT/ $BUILD_OUTPUT/live/"${GRML_NAME}"/"${GRML_NAME}".squashfs \
1247       -noappend $SQUASHFS_OPTIONS 2>"${SQUASHFS_STDERR}" ; then
1248       echo "${GRML_NAME}.squashfs" > $BUILD_OUTPUT/live/"${GRML_NAME}"/filesystem.module
1249       log "Finished execution of stage 'squashfs' [$(date)]"
1250       einfo "Finished execution of stage 'squashfs'" ; eend 0
1251    else
1252       log    "Error: there was a critical error executing stage 'squashfs' [$(date)]:"
1253       log    "$(cat $SQUASHFS_STDERR)"
1254       eerror "Error: there was a critical error executing stage 'squashfs':"
1255       cat    "${SQUASHFS_STDERR}"
1256       eend 1
1257       bailout
1258    fi
1259
1260    FORCE_ISO_REBUILD=true
1261 fi
1262
1263 # create md5sum file:
1264 if [ -z "$BOOTSTRAP_ONLY" ] ; then
1265   ( cd $BUILD_OUTPUT/GRML/"${GRML_NAME}" &&
1266   find ../.. -type f -not -name md5sums -not -name isolinux.bin -exec md5sum {} \; > md5sums )
1267 fi
1268 # }}}
1269
1270 # ISO_OUTPUT - mkisofs {{{
1271 [ -n "$ISO_OUTPUT" ] || ISO_OUTPUT="$OUTPUT/grml_isos"
1272 [ -n "$ISO_NAME" ] || ISO_NAME="${GRML_NAME}_${VERSION}.iso"
1273
1274 if [ "$BOOT_METHOD" = "isolinux" ] ; then
1275    BOOT_ARGS="-no-emul-boot -boot-load-size 4 -boot-info-table -b boot/isolinux/isolinux.bin -c boot/isolinux/boot.cat"
1276 elif [ "$BOOT_METHOD" = "grub2" ] ; then
1277    BOOT_ARGS="-no-emul-boot -boot-load-size 4 -b boot/grub/toriboot.bin"
1278 fi
1279
1280 # Work around http://bts.grml.org/grml/issue945
1281 if [[ $BOOT_METHOD != isolinux && ($HYBRID_METHOD = isohybrid || $HYBRID_METHOD = manifold) ]]; then
1282   log   "Setting HYBRID_METHOD to grub2 as hybrid mode does not work with isohybrid yet."
1283   ewarn "Setting HYBRID_METHOD to grub2 as hybrid mode does not work with isohybrid yet."
1284   HYBRID_METHOD='grub2'
1285   eend 0
1286 fi
1287
1288 if [ -f "${ISO_OUTPUT}/${ISO_NAME}" -a -z "$UPDATE" -a -z "$BUILD_ONLY" -a -z "$BUILD_DIRTY" -a "$FORCE_ISO_REBUILD" = "false" ]  ; then
1289    log   "Skipping stage 'iso build' as $ISO_OUTPUT/${ISO_NAME} exists already."
1290    ewarn "Skipping stage 'iso build' as $ISO_OUTPUT/${ISO_NAME} exists already." ; eend 0
1291 elif [ -n "$SKIP_MKISOFS" ] ; then
1292    log   "Skipping stage 'iso build' as requested via option -n or -N"
1293    ewarn "Skipping stage 'iso build' as requested via option -n or -N" ; eend 0
1294 else
1295    mkdir -p "$ISO_OUTPUT" || bailout 6 "Problem with creating $ISO_OUTPUT for stage 'iso build'"
1296
1297    if $FORCE_ISO_REBUILD && ! [ -f "${ISO_OUTPUT}/${ISO_NAME}" ] ; then
1298       log   "Forcing rebuild of ISO because files on ISO have been modified."
1299       einfo "Forcing rebuild of ISO because files on ISO have been modified."
1300    fi
1301
1302    # support xorriso as well mkisofs and genisoimage
1303    if which xorriso >/dev/null 2>&1 ; then
1304       MKISOFS='xorriso -as mkisofs'
1305     elif which mkisofs >/dev/null 2>&1; then
1306       MKISOFS='mkisofs'
1307    elif which genisoimage >/dev/null 2>&1; then
1308       MKISOFS='genisoimage'
1309    else
1310       log    "Error: neither xorriso nor mkisofs nor genisoimage available - can not create ISO."
1311       eerror "Error: neither xorriso nor mkisofs nor genisoimage available - can not create ISO." ; eend 1
1312       bailout
1313    fi
1314
1315    einfo "Using ${MKISOFS} to build ISO." ;  eend 0
1316    case "${ARCH}-${MKISOFS}" in
1317      # using -eltorito-alt-boot is limited to xorriso for now
1318      amd64-xorriso*)
1319        eindent
1320
1321        if ! dpkg --compare-versions $(dpkg-query -W -f='${Version}\n' xorriso 2>/dev/null) gt-nl 1.1.6-1 ; then
1322          log   "Disabling (U)EFI boot support because xorriso version is too old."
1323          ewarn "Disabling (U)EFI boot support because xorriso version is too old." ; eend 0
1324        else
1325          if [ -r "${BUILD_OUTPUT}"/boot/efi.img ] ; then
1326            einfo "Enabling (U)EFI boot."
1327            log   "Enabling (U)EFI boot."
1328            BOOT_ARGS="$BOOT_ARGS -boot-info-table -eltorito-alt-boot -e boot/efi.img -no-emul-boot"
1329            eend $?
1330          else
1331            log   "Disabling (U)EFI boot support because /boot/efi.img is missing."
1332            ewarn "Disabling (U)EFI boot support because /boot/efi.img is missing." ; eend 0
1333          fi
1334        fi
1335
1336        eoutdent
1337        ;;
1338    esac
1339
1340    CURRENT_DIR=$(pwd)
1341    if cd "$BUILD_OUTPUT" ; then
1342       if [ "$BOOT_METHOD" = "grub2" ]; then
1343          # make a 2048-byte bootsector for El Torito
1344          dd if=/dev/zero of=boot/grub/toriboot.bin bs=512 count=4 2>/dev/null
1345          # those are in 2048-byte sectors, so 1 16 matches 4 63 below
1346          echo 1 16 | mksh "${SCRIPTS_DIRECTORY}/bootgrub.mksh" -B 11 | \
1347             dd of=boot/grub/toriboot.bin conv=notrunc 2>/dev/null
1348       fi
1349       log "$MKISOFS -V '${GRML_NAME} ${VERSION}' -publisher 'grml-live | grml.org' -l -r -J $BOOT_ARGS -o ${ISO_OUTPUT}/${ISO_NAME} ."
1350       $MKISOFS -V "${GRML_NAME} ${VERSION}" -publisher 'grml-live | grml.org' \
1351               -l -r -J $BOOT_ARGS -no-pad \
1352               -o "${ISO_OUTPUT}/${ISO_NAME}" . ; RC=$?
1353       # both of these need core.img there, so it’s easier to write it here
1354       if [ "$BOOT_METHOD" = "grub2" ] || [ "$HYBRID_METHOD" = "grub2" ]; then
1355          # must be <= 30720 bytes
1356          dd if=boot/grub/core.img of="${ISO_OUTPUT}/${ISO_NAME}" \
1357            conv=notrunc bs=512 seek=4 2>/dev/null
1358       fi
1359
1360       # pad the output ISO to multiples of 256 KiB for partition table support
1361       siz=$($getfilesize "${ISO_OUTPUT}/${ISO_NAME}")
1362       cyls=$((siz / 512 / 32 / 16 + 1))   # C=$cyls H=16 S=32
1363       siz=$((cyls * 16 * 32 * 512))   # size after padding
1364       dd if=/dev/zero bs=1 count=1 seek=$((siz - 1)) \
1365          of="${ISO_OUTPUT}/${ISO_NAME}" 2>/dev/null
1366
1367       # support disabling hybrid ISO image
1368       if [ "$HYBRID_METHOD" = "disable" ] ; then
1369         log   "Skipping creation of hybrid ISO file as requested via HYBRID_METHOD=disable"
1370         einfo "Skipping creation of hybrid ISO file as requested via HYBRID_METHOD=disable"
1371         eend 0
1372       elif [ "$HYBRID_METHOD" = "manifold" ] || [ "$HYBRID_METHOD" = "grub2" ] ; then
1373         # isoinfo is part of both mkisofs and genisoimage so we're good
1374         bootoff=$(isoinfo -l -i "${ISO_OUTPUT}/${ISO_NAME}" | \
1375           sed -n '/^.*\[ *\([0-9]*\)[] ].* ISOLINUX.BIN[;1]* *$/s//\1/p')
1376
1377         if ! [ -r boot/grub/core.img ] ; then
1378           log   "boot/grub/core.img not found, not creating manifold boot ISO file"
1379           ewarn "boot/grub/core.img not found, not creating manifold boot ISO file"
1380         elif [ "${bootoff:-0}" -lt 1 ] ; then
1381           log   "isolinux.bin not found on the ISO file, disabling manifold boot"
1382           ewarn "isolinux.bin not found on the ISO file, disabling manifold boot"
1383         else
1384           if [ "$HYBRID_METHOD" = "grub2" ] ; then
1385             log   "Creating hybrid ISO file with manifold/grub2 method"
1386             einfo "Creating hybrid ISO file with manifold/grub2 method"
1387             # 512 bytes: MBR, partition table, load GRUB 2
1388             echo 4 63 | mksh "${SCRIPTS_DIRECTORY}/bootgrub.mksh" -A -M 4:0x96 -g $cyls:16:32
1389           else
1390             log   "Creating hybrid ISO file with manifold method"
1391             einfo "Creating hybrid ISO file with manifold method"
1392             # read only one but 2048-byte sized (scale: << 2) sector
1393             echo $bootoff $bootoff | \
1394               mksh ${SCRIPTS_DIRECTORY}/bootilnx.mksh -A -M 4:0x96 -g $cyls:16:32 -S 2
1395           fi | dd of="${ISO_OUTPUT}/${ISO_NAME}" conv=notrunc 2>/dev/null
1396           eend $?
1397         fi
1398       elif [ "$HYBRID_METHOD" = "isohybrid" ] ; then
1399         if ! which isohybrid >/dev/null 2>&1 ; then
1400           bailout 12 "isohybrid binary not found - please install syslinux/syslinux-common/syslinux-utils"
1401         else
1402           log   "Creating hybrid ISO file with isohybrid method"
1403           einfo "Creating hybrid ISO file with isohybrid method"
1404           # Notes for consideration:
1405           # "-entry 4 -type 1c"
1406           # * using 4 as the partition number is supposed to help with BIOSes
1407           #   that only support USB-Zip boot
1408           # * using 1c (i.e. hidden FAT32 LBA), instead of the default 0x17
1409           #   (hidden NTFS, IIRC), as the partition type is sometimes needed
1410           #   to get the BIOS even look at the partition created by isohybrid
1411           if isohybrid --help | grep -q -- --uefi ; then
1412             if echo $CLASSES | grep -qw I386 ; then
1413               log   "Detected uefi support for isohybrid but 32bit systems do not support it, ignoring."
1414               einfo "Detected uefi support for isohybrid but 32bit systems do not support it, ignoring."
1415             else
1416               log   "Detected uefi support for isohybrid, enabling"
1417               einfo "Detected uefi support for isohybrid, enabling"
1418               ISOHYBRID_OPTIONS=--uefi
1419             fi
1420           fi
1421
1422           log "isohybrid $ISOHYBRID_OPTIONS ${ISO_OUTPUT}/${ISO_NAME}"
1423           isohybrid $ISOHYBRID_OPTIONS "${ISO_OUTPUT}/${ISO_NAME}"
1424           eend $?
1425         fi
1426       else
1427         bailout 12 "Unknown HYBRID_METHOD [${HYBRID_METHOD}]. Supported values: disable, isohybrid, grub2, manifold"
1428       fi
1429
1430       # generate md5sum and sha1sum of ISO if we are using class 'RELEASE':
1431       case $CLASSES in *RELEASE*)
1432          [ "$RC" = 0 ] && \
1433          (
1434            if cd $ISO_OUTPUT ; then
1435              md5sum ${ISO_NAME} > ${ISO_NAME}.md5 && \
1436              touch -r ${ISO_NAME} ${ISO_NAME}.md5
1437              sha1sum ${ISO_NAME} > ${ISO_NAME}.sha1 && \
1438              touch -r ${ISO_NAME} ${ISO_NAME}.sha1
1439            fi
1440          )
1441          ;;
1442       esac
1443
1444       cd "$CURRENT_DIR"
1445    fi
1446
1447    if [ "$RC" = 0 ] ; then
1448       log   "Finished execution of stage 'iso build' [$(date)]"
1449       einfo "Finished execution of stage 'iso build'" ; eend 0
1450    else
1451       log    "Error: there was a critical error ($RC) executing stage 'iso build' [$(date)]"
1452       eerror "Error: there was a critical error executing stage 'iso build'" ; eend 1
1453       bailout $RC
1454    fi
1455 fi
1456 # }}}
1457
1458 # netboot package {{{
1459 create_netbootpackage() {
1460   local OUTPUT_FILE="${NETBOOT}/grml_netboot_package_${GRML_NAME}_${VERSION}.tar.bz2"
1461
1462   if [ -f "${OUTPUT_FILE}" -a -z "$UPDATE" -a -z "$BUILD_ONLY" -a -z "$BUILD_DIRTY" ] ; then
1463     log   "Skipping stage 'netboot' as $OUTPUT_FILE exists already."
1464     ewarn "Skipping stage 'netboot' as $OUTPUT_FILE exists already." ; eend 0
1465     return 0
1466   elif [ -n "$SKIP_NETBOOT" ] ; then
1467     log   "Skipping stage 'netboot' as requested via option -Q"
1468     ewarn "Skipping stage 'netboot' as requested via option -Q" ; eend 0
1469     return 0
1470   fi
1471
1472   mkdir -p "$NETBOOT"
1473
1474   # since syslinux v3:6.03~pre1+dfsg-4 the pxelinux.0 has been split into a
1475   # separate pxelinux package
1476   if [ -d "${CHROOT_OUTPUT}/usr/lib/PXELINUX/" ] ; then
1477     local pxelinux_dir=/usr/lib/PXELINUX
1478   else
1479     local pxelinux_dir=/usr/lib/syslinux
1480   fi
1481
1482   if ! [ -r "${CHROOT_OUTPUT}/${pxelinux_dir}/pxelinux.0" ] ; then
1483     ewarn "File ${pxelinux_dir}/pxelinux.0 not found in build chroot." ; eend 0
1484     eindent
1485     einfo "Install syslinux[-common]/pxelinux package in chroot to get a netboot package."
1486     eoutdent
1487     return 0
1488   fi
1489
1490   local OUTPUTDIR="${NETBOOT}/build_tmp"
1491   local WORKING_DIR="${OUTPUTDIR}/grml_netboot_package_${GRML_NAME}_${VERSION}/tftpboot/"
1492
1493   mkdir -p "$WORKING_DIR"
1494
1495   cp "${CHROOT_OUTPUT}"/boot/vmlinuz-*    "$WORKING_DIR"/vmlinuz
1496   cp "${CHROOT_OUTPUT}"/boot/initrd.img-* "$WORKING_DIR"/initrd.img
1497   cp "${CHROOT_OUTPUT}/${pxelinux_dir}/pxelinux.0" "${WORKING_DIR}/pxelinux.0"
1498
1499   if [ -r "${CHROOT_OUTPUT}"/usr/lib/syslinux/modules/bios/ldlinux.c32 ] ; then
1500     cp "${CHROOT_OUTPUT}"/usr/lib/syslinux/modules/bios/ldlinux.c32 "${WORKING_DIR}"/
1501   fi
1502
1503   mkdir -p "${WORKING_DIR}/pxelinux.cfg"
1504   if [ -r "${BUILD_OUTPUT}/boot/isolinux/netboot.cfg" ] ; then
1505     cp "${BUILD_OUTPUT}/boot/isolinux/netboot.cfg" "${WORKING_DIR}/pxelinux.cfg/default"
1506   else
1507     log   "File ${BUILD_OUTPUT}/boot/isolinux/netboot.cfg not found."
1508     ewarn "File ${BUILD_OUTPUT}/boot/isolinux/netboot.cfg not found."
1509     eindent
1510     log   "Hint: Are you using custom templates which do not provide netboot.cfg?"
1511     ewarn "Hint: Are you using custom templates which do not provide netboot.cfg?" ; eend 0
1512     eoutdent
1513   fi
1514
1515   if tar -C "$OUTPUTDIR" -jcf "${OUTPUT_FILE}" "grml_netboot_package_${GRML_NAME}_${VERSION}" ; then
1516     (
1517       cd $(dirname "${OUTPUT_FILE}")
1518       sha1sum $(basename "${OUTPUT_FILE}") > "${OUTPUT_FILE}.sha1"
1519     )
1520     einfo "Generated netboot package ${OUTPUT_FILE}" ; eend 0
1521     rm -rf "${OUTPUTDIR}"
1522   else
1523     rm -rf "${OUTPUTDIR}"
1524     eerror "Could not generate netboot package ${OUTPUT_FILE}" ; eend 1
1525     bailout 21
1526   fi
1527 }
1528
1529 create_netbootpackage
1530 # }}}
1531
1532 # log build information to database if grml-live-db is installed and enabled {{{
1533 dpkg_to_db() {
1534 if [ -d /usr/share/grml-live-db ] ; then
1535
1536   # safe defaults
1537   DPKG_LIST="/var/log/fai/$HOSTNAME/last/dpkg.list" # the dpkg --list output of the chroot:
1538   [ -n "$DPKG_DATABASE" ]  || DPKG_DATABASE=/var/log/grml-live.db
1539   [ -n "$DPKG_DBSCRIPT" ]  || DPKG_DBSCRIPT=/usr/share/grml-live-db/scripts/dpkg-to-db
1540   [ -n "$DPKG_DBOPTIONS" ] || DPKG_DBOPTIONS="--database $DPKG_DATABASE --logfile $LOGFILE --flavour $GRML_NAME --dpkg $DPKG_LIST"
1541
1542   if ! [ -x "$DPKG_DBSCRIPT" ] ; then
1543     log "Error: $DPKG_DBSCRIPT is not executable, can not log dpkg information."
1544     eerror "Error: $DPKG_DBSCRIPT is not executable, can not log dpkg information." ; eend 1
1545     bailout 14
1546   fi
1547
1548   # disable by default for now, not sure whether really everyone is using a local db file
1549   #if ! touch "$DPKG_DATABASE" ; then
1550   #  eerror "Error: can not write to ${DPKG_DATABASE}, can not log dpkg information." ; eend 1
1551   #  bailout 14
1552   #fi
1553
1554   if ! [ -r "$DPKG_LIST" ] ; then
1555      log   "Warning: can not read $DPKG_LIST - can not provide information to $DPKG_DBSCRIPT (dirty build?)"
1556      ewarn "Warning: can not read $DPKG_LIST - can not provide information to $DPKG_DBSCRIPT (dirty build?)" ; eend 0
1557   else
1558      einfo "Logging $DPKG_LIST to database $DPKG_DATABASE"
1559      log "Logging $DPKG_LIST to database $DPKG_DATABASE"
1560      log "Executing $DPKG_DBSCRIPT $DPKG_DBOPTIONS"
1561      eindent
1562
1563      if DB_INFO=$("$DPKG_DBSCRIPT" $DPKG_DBOPTIONS 2>&1) ; then
1564        einfo "$DB_INFO"
1565        eend 0
1566      else
1567        eerror "$DB_INFO"
1568        eend 1
1569      fi
1570
1571      eoutdent
1572   fi
1573
1574 fi
1575 }
1576 # }}}
1577
1578 # finalize {{{
1579 [ -n "$start_seconds" ] && SECONDS="$[$(cut -d . -f 1 /proc/uptime)-$start_seconds]" || SECONDS="unknown"
1580 log "Successfully finished execution of $PN [$(date) - running ${SECONDS} seconds]"
1581
1582 dpkg_to_db # make sure we catch the last log line as well, therefore execute between log + einfo
1583
1584 einfo "Successfully finished execution of $PN [$(date) - running ${SECONDS} seconds]" ; eend 0
1585 bailout 0
1586 # }}}
1587
1588 ## END OF FILE #################################################################
1589 # vim:foldmethod=marker ts=2 ft=sh ai expandtab tw=80 sw=2