Update FILE_RC + systemd classes for aptitude->apt switch
[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   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 EFI boot files into ISO path."
895           log "Moving 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        fi
902
903        [ -n "$TEMPLATE_DIRECTORY" ] || TEMPLATE_DIRECTORY='/usr/share/grml-live/templates'
904        if ! [ -d "${TEMPLATE_DIRECTORY}"/boot ] ; then
905           log    "Error: ${TEMPLATE_DIRECTORY}/boot does not exist. Exiting."
906           eerror "Error: ${TEMPLATE_DIRECTORY}/boot does not exist. Exiting." ; eend 1
907           bailout 8
908        fi
909
910        # copy _required_ isolinux files
911        if [ -d "${CHROOT_OUTPUT}/usr/lib/ISOLINUX" ] ; then
912          copy_addon_file isolinux.bin /usr/lib/ISOLINUX isolinux
913          for file in ${CHROOT_OUTPUT}/usr/lib/syslinux/modules/bios/*.c32 ; do
914            copy_addon_file "$(basename "$file")"  /usr/lib/syslinux/modules/bios/ isolinux
915          done
916        else # syslinux versions <= 3:4.05+dfsg-6+deb8u1
917          copy_addon_file isolinux.bin /usr/lib/syslinux isolinux
918          copy_addon_file ifcpu64.c32  /usr/lib/syslinux isolinux
919          copy_addon_file vesamenu.c32 /usr/lib/syslinux isolinux
920        fi
921
922        # *always* copy files to output directory so the variables
923        # get adjusted according to the build.
924        cp ${TEMPLATE_DIRECTORY}/boot/isolinux/*  "$BUILD_OUTPUT"/boot/isolinux/
925
926        mkdir -p "${BUILD_OUTPUT}/boot/grub"
927        cp -a ${TEMPLATE_DIRECTORY}/boot/grub/* "$BUILD_OUTPUT"/boot/grub/
928
929        if [ -n "$NO_ADDONS" ] ; then
930           rm -f "$BUILD_OUTPUT"/boot/grub/addons.cfg
931           log   "Skipping installation of boot addons as requested via \$NO_ADDONS."
932           einfo "Skipping installation of boot addons as requested via \$NO_ADDONS."; eend 0
933        else
934           if ! [ -d "$TEMPLATE_DIRECTORY"/boot/addons ] ; then
935             log   "Boot addons not found, skipping therefore. (Consider installing package grml-live-addons)"
936             ewarn "Boot addons not found, skipping therefore. (Consider installing package grml-live-addons)" ; eend 0
937           else
938             # copy addons from system packages or grml-live-compat
939             copy_addon_file ipxe.lkrn /usr/lib/ipxe addons
940             copy_addon_file pci.ids /usr/share/misc addons
941             copy_addon_file memtest86+.bin /boot addons
942
943             # since syslinux(-common) v3:6.03~pre1+dfsg-4 the files are in a
944             # different directory :(
945             if [ -d "${CHROOT_OUTPUT}/usr/lib/syslinux/modules/bios/" ] ; then
946               syslinux_modules_dir=/usr/lib/syslinux/modules/bios/
947             else
948               syslinux_modules_dir=/usr/lib/syslinux
949             fi
950             for file in chain.c32 hdt.c32 mboot.c32 menu.c32; do
951               copy_addon_file "${file}" "${syslinux_modules_dir}" addons
952             done
953
954             copy_addon_file memdisk /usr/lib/syslinux addons
955
956             # make memtest filename FAT16/8.3 compatible
957             mv "${BUILD_OUTPUT}/boot/addons/memtest86+.bin" \
958               "${BUILD_OUTPUT}/boot/addons/memtest"
959
960             # copy only files so we can handle bsd4grml on its own
961             for file in ${TEMPLATE_DIRECTORY}/boot/addons/* ; do
962               test -f $file && cp $file "$BUILD_OUTPUT"/boot/addons/
963             done
964
965             if [ -n "$NO_ADDONS_BSD4GRML" ] ; then
966                log   "Skipping installation of bsd4grml as requested via \$NO_ADDONS_BSD4GRML."
967                einfo "Skipping installation of bsd4grml as requested via \$NO_ADDONS_BSD4GRML."; eend 0
968             else
969                if [ -d "$TEMPLATE_DIRECTORY"/boot/addons/bsd4grml ] ; then
970                  cp -a ${TEMPLATE_DIRECTORY}/boot/addons/bsd4grml "$BUILD_OUTPUT"/boot/addons/
971                else
972                  log   "Missing addon file: bsd4grml"
973                  ewarn "Missing addon file: bsd4grml" ; eend 0
974                fi
975             fi
976
977           fi # no "$TEMPLATE_DIRECTORY"/boot/addons
978        fi # NO_ADDONS
979
980        # generate loopback.cfg config file without depending on grub's regexp module
981        # which isn't available in Debian/squeeze
982        echo "## grub2 loopback configuration" > "${BUILD_OUTPUT}"/boot/grub/loopback.cfg
983        echo "source /boot/grub/header.cfg" >> "${BUILD_OUTPUT}"/boot/grub/loopback.cfg
984        for config in "${BUILD_OUTPUT}"/boot/grub/*_default.cfg "${BUILD_OUTPUT}"/boot/grub/*_options.cfg ; do
985          [ -r "$config" ] || continue
986          echo "source ${config##$BUILD_OUTPUT}" >> "${BUILD_OUTPUT}"/boot/grub/loopback.cfg
987        done
988        if [ -z "$NO_ADDONS" ] ; then
989          echo "source /boot/grub/addons.cfg" >> "${BUILD_OUTPUT}"/boot/grub/loopback.cfg
990        fi
991        echo "source /boot/grub/footer.cfg" >> "${BUILD_OUTPUT}"/boot/grub/loopback.cfg
992
993        # copy grub files from target
994        mkdir -p "${BUILD_OUTPUT}"/boot/grub/i386-pc/
995        cp -a "${CHROOT_OUTPUT}"/usr/lib/grub/*-pc/*.mod "${BUILD_OUTPUT}"/boot/grub/i386-pc/
996        cp -a "${CHROOT_OUTPUT}"/usr/lib/grub/*-pc/*.o "${BUILD_OUTPUT}"/boot/grub/i386-pc/
997        cp -a "${CHROOT_OUTPUT}"/usr/lib/grub/*-pc/*.lst "${BUILD_OUTPUT}"/boot/grub/i386-pc/
998        cp -a "${CHROOT_OUTPUT}"/usr/share/grub/ascii.pf2 "${BUILD_OUTPUT}"/boot/grub/
999        cp -a "${CHROOT_OUTPUT}"/boot/grub/core.img "${BUILD_OUTPUT}"/boot/grub/
1000        cp -a "${CHROOT_OUTPUT}"/boot/grub/grub.img "${BUILD_OUTPUT}"/boot/grub/
1001
1002        # copy modules for UEFI grub
1003        mkdir -p "${BUILD_OUTPUT}"/boot/grub/x86_64-efi/
1004        cp -a "${CHROOT_OUTPUT}"/usr/lib/grub/x86_64-efi/*.{mod,lst} "${BUILD_OUTPUT}"/boot/grub/x86_64-efi/
1005
1006        if ! [ -d "${TEMPLATE_DIRECTORY}"/GRML ] ; then
1007           log    "Error: ${TEMPLATE_DIRECTORY}/GRML does not exist. Exiting."
1008           eerror "Error: ${TEMPLATE_DIRECTORY}/GRML does not exist. Exiting." ; eend 1
1009           bailout 9
1010        fi
1011
1012        mkdir -p "$BUILD_OUTPUT"/GRML/"${GRML_NAME}"/
1013        cp -a ${TEMPLATE_DIRECTORY}/GRML/* "$BUILD_OUTPUT"/GRML/"${GRML_NAME}"/
1014
1015        # adjust boot splash information:
1016        RELEASE_INFO="$GRML_NAME $VERSION - Release Codename $RELEASENAME"
1017        RELEASE_INFO="$(cut_string 68 "$RELEASE_INFO")"
1018        RELEASE_INFO="$(extend_string_end 68 "$RELEASE_INFO")"
1019
1020        if [ -r "$BUILD_OUTPUT"/GRML/"${GRML_NAME}"/grml-version ] ; then
1021           sed -i "s/%RELEASE_INFO%/$GRML_NAME $VERSION - $RELEASENAME/" "$BUILD_OUTPUT"/GRML/"${GRML_NAME}"/grml-version
1022           sed -i "s/%DATE%/$DATE/"                                      "$BUILD_OUTPUT"/GRML/"${GRML_NAME}"/grml-version
1023        fi
1024
1025        # make sure the squashfs filename is set accordingly:
1026        SQUASHFS_NAME="$GRML_NAME.squashfs"
1027
1028        if [ -n "$NO_BOOTID" ] ; then
1029           log   'Skipping bootid feature as requested via $NO_BOOTID.'
1030           einfo 'Skipping bootid feature as requested via $NO_BOOTID.'
1031        else
1032           [ -n "$BOOTID" ] || BOOTID="$(echo ${GRML_NAME}${VERSION} | tr -d ',./;\- ')"
1033           [ -d "$BUILD_OUTPUT"/conf ] || mkdir "$BUILD_OUTPUT"/conf
1034           einfo "Generating /conf/bootid.txt with entry ${BOOTID}."
1035           log   "Generating /conf/bootid.txt with entry ${BOOTID}."
1036           echo "$BOOTID" > "$BUILD_OUTPUT"/conf/bootid.txt
1037           eend $?
1038        fi
1039
1040        # adjust all variables in the templates with the according distribution information
1041        for file in "${BUILD_OUTPUT}"/boot/isolinux/*.cfg "${BUILD_OUTPUT}"/boot/isolinux/*.msg \
1042                    "${BUILD_OUTPUT}"/boot/grub/* ; do
1043          if [ -r "${file}" ] && [ -f "${file}" ] ; then
1044            sed -i "s/%ARCH%/$ARCH/g"                    "${file}"
1045            sed -i "s/%DATE%/$DATE/g"                    "${file}"
1046            sed -i "s/%DISTRI_INFO%/$DISTRI_INFO/g"      "${file}"
1047            sed -i "s/%DISTRI_NAME%/$DISTRI_NAME/g"      "${file}"
1048            sed -i "s/%DISTRI_SPLASH%/$DISTRI_SPLASH/g"  "${file}"
1049            sed -i "s/%GRML_NAME%/$GRML_NAME/g"          "${file}"
1050            sed -i "s/%SQUASHFS_NAME%/$SQUASHFS_NAME/g"  "${file}"
1051            sed -i "s/%RELEASE_INFO%/$RELEASE_INFO/g"    "${file}"
1052            sed -i "s/%SHORT_NAME%/$SHORT_NAME/g"        "${file}"
1053            sed -i "s/%VERSION%/$VERSION/g"              "${file}"
1054
1055            [ -n "$DEFAULT_BOOTOPTIONS" ] && sed -i "s; boot=live; boot=live $DEFAULT_BOOTOPTIONS;"  "${file}"
1056
1057            if [ -n "$NO_BOOTID" ] ; then
1058               sed -i "s/ bootid=%BOOTID%//g" "${file}" # drop bootid bootoption
1059            else
1060               sed -i "s/%BOOTID%/$BOOTID/g" "${file}" # adjust bootid=... argument
1061            fi
1062          fi
1063        done
1064
1065        for param in ARCH DATE DISTRI_INFO DISTRI_NAME DISTRI_SPLASH GRML_NAME SQUASHFS_NAME \
1066            RELEASE_INFO SHORT_NAME VERSION ; do
1067            for file in $(find "${BUILD_OUTPUT}" -name "*%$param%*") ; do
1068                value="$(eval echo '$'"$param")"
1069                mv ${file} ${file/\%${param}\%/$value}
1070            done
1071        done
1072
1073        # adjust bootsplash accordingly but make sure the string has the according lenght
1074        SQUASHFS_NAME="$(cut_string 20 "$SQUASHFS_NAME")"
1075        SQUASHFS_NAME="$(extend_string_end 20 "$SQUASHFS_NAME")"
1076        for file in f4 f5 ; do
1077           if [ -r "${BUILD_OUTPUT}/boot/isolinux/${file}" ] ; then
1078              sed -i "s/%SQUASHFS_NAME%/$SQUASHFS_NAME/" "${BUILD_OUTPUT}/boot/isolinux/${file}"
1079              sed -i "s/%SQUASHFS_NAME%/$SQUASHFS_NAME/" "${BUILD_OUTPUT}/boot/isolinux/${file}"
1080           fi
1081        done
1082
1083        # generate addon list
1084        rm -f "${BUILD_OUTPUT}/${ADDONS_LIST_FILE}"
1085        for name in "${BUILD_OUTPUT}"/boot/isolinux/addon_*.cfg ; do
1086          include_name=$(basename "$name")
1087          echo "include $include_name"  >> "${BUILD_OUTPUT}/${ADDONS_LIST_FILE}"
1088        done
1089
1090        if ! [ -r "${BUILD_OUTPUT}/boot/isolinux/${DISTRI_NAME}.cfg" ] || [ "$DISTRI_NAME" = "grml" ] ; then
1091           log "including grmlmain.cfg in ${BUILD_OUTPUT}/boot/isolinux/distri.cfg"
1092           echo "include grmlmain.cfg"    >  "${BUILD_OUTPUT}/boot/isolinux/distri.cfg"
1093           echo "include default.cfg"     >  "${BUILD_OUTPUT}/boot/isolinux/grmlmain.cfg"
1094           echo "include menuoptions.cfg" >> "${BUILD_OUTPUT}/boot/isolinux/grmlmain.cfg"
1095           echo "include grml.cfg"        >> "${BUILD_OUTPUT}/boot/isolinux/grmlmain.cfg"
1096
1097           for f in "${BUILD_OUTPUT}"/boot/isolinux/submenu*.cfg ; do
1098             echo "include $(basename $f)"     >> "${BUILD_OUTPUT}/boot/isolinux/grmlmain.cfg"
1099           done
1100
1101           echo "include options.cfg"     >> "${BUILD_OUTPUT}/boot/isolinux/grmlmain.cfg"
1102           if [ -z "$NO_ADDONS" ] ; then
1103             echo "include addons.cfg"    >> "${BUILD_OUTPUT}/boot/isolinux/grmlmain.cfg"
1104           fi
1105           echo "include isoprompt.cfg"   >> "${BUILD_OUTPUT}/boot/isolinux/grmlmain.cfg"
1106           echo "include hd.cfg"          >> "${BUILD_OUTPUT}/boot/isolinux/grmlmain.cfg"
1107           echo "include hidden.cfg"      >> "${BUILD_OUTPUT}/boot/isolinux/grmlmain.cfg"
1108        else # assume we are building a custom distribution:
1109           log "File ${BUILD_OUTPUT}/boot/isolinux/${DISTRI_NAME}.cfg found, using it."
1110           einfo "File ${BUILD_OUTPUT}/boot/isolinux/${DISTRI_NAME}.cfg found, using it."
1111           if grep -q "^include ${DISTRI_NAME}.cfg" "${BUILD_OUTPUT}/boot/isolinux/distri.cfg" ; then
1112             log "include for ${DISTRI_NAME}.cfg already present, nothing to do."
1113             eindent
1114             einfo "include for ${DISTRI_NAME}.cfg already present, nothing to do."
1115             eoutdent
1116             eend $?
1117          else
1118             log "including ${DISTRI_NAME}.cfg in ${BUILD_OUTPUT}/boot/isolinux/distri.cfg"
1119             echo "include ${DISTRI_NAME}.cfg" > "${BUILD_OUTPUT}/boot/isolinux/distri.cfg"
1120             if [ -z "$NO_ADDONS" ] ; then
1121               echo "include addons.cfg" >> "${BUILD_OUTPUT}/boot/isolinux/distri.cfg"
1122             fi
1123           fi
1124        fi
1125
1126        # use old style console based isolinux method only if requested:
1127        if [[ "${ISOLINUX_METHOD}" == "console" ]] ; then
1128           log 'Using console based isolinux method as requested via $ISOLINUX_METHOD.'
1129           einfo 'Using console based isolinux method as requested via $ISOLINUX_METHOD.'
1130           if grep -q '^include console.cfg' "${BUILD_OUTPUT}/boot/isolinux/distri.cfg" ; then
1131             einfo "include for console.cfg already found, nothing to do."
1132             eend 0
1133           else
1134             log "including console.cfg in ${BUILD_OUTPUT}/boot/isolinux/isolinux.cfg"
1135             einfo "including console.cfg in ${BUILD_OUTPUT}/boot/isolinux/isolinux.cfg"
1136             echo "include console.cfg" >> "${BUILD_OUTPUT}/boot/isolinux/isolinux.cfg"
1137             eend $?
1138           fi
1139        else
1140           log 'Using graphical boot menu.'
1141           if grep -q '^include vesamenu.cfg' "${BUILD_OUTPUT}/boot/isolinux/isolinux.cfg" ; then
1142             log "include for vesamenu.cfg already found, nothing to do."
1143           else
1144             log "including vesamenu.cfg in ${BUILD_OUTPUT}/boot/isolinux/isolinux.cfg"
1145             echo "include vesamenu.cfg" >> "${BUILD_OUTPUT}/boot/isolinux/isolinux.cfg"
1146           fi
1147        fi
1148
1149        if [ -e "$BUILD_OUTPUT"/boot/addons/bsd4grml/boot.6 ]; then
1150           sed -i "s/%RELEASE_INFO%/$GRML_NAME $VERSION - $RELEASENAME/" "$BUILD_OUTPUT"/boot/addons/bsd4grml/boot.6
1151        fi
1152
1153        DPKG_LIST="/var/log/fai/$HOSTNAME/last/dpkg.list" # the dpkg --list output of the chroot
1154        if ! [ -r "$DPKG_LIST" ] ; then
1155           ewarn "$DPKG_LIST could not be read, ignoring to store package information on ISO therefore."
1156        else
1157           einfo "Storing package list information as /GRML/${GRML_NAME}/packages.txt on ISO."
1158           cp "$DPKG_LIST" "${BUILD_OUTPUT}"/GRML/"${GRML_NAME}"/packages.txt
1159           eend $?
1160        fi
1161
1162        # autostart for Windows:
1163        if [ -d "${TEMPLATE_DIRECTORY}/windows/autostart/" ] ; then
1164           cp ${TEMPLATE_DIRECTORY}/windows/autostart/* "$BUILD_OUTPUT"/
1165        fi
1166
1167     FORCE_ISO_REBUILD=true
1168     einfo "Finished execution of stage 'boot'" ; eend 0
1169     fi
1170   fi # BOOTSTRAP_ONLY
1171 else
1172    log    'Error: Unsupported ARCH, sorry. Want to support it? Contribute!'
1173    eerror 'Error: Unsupported ARCH, sorry. Want to support it? Contribute!' ; eend 1
1174    bailout
1175 fi
1176
1177 # support installation of local files into the chroot/ISO
1178 if [ -n "$CHROOT_INSTALL" ] ; then
1179   if ! [ -d "$CHROOT_INSTALL" ] ; then
1180      log "Configuration variable \$CHROOT_INSTALL is set but not a directory; ignoring"
1181      ewarn "Configuration variable \$CHROOT_INSTALL is set but not a directory; ignoring"
1182   else
1183      log "Copying local files to chroot as requested via \$CHROOT_INSTALL"
1184      einfo "Copying local files to chroot as requested via \$CHROOT_INSTALL"
1185      rsync -avz --inplace "$CHROOT_INSTALL"/ "$CHROOT_OUTPUT/"
1186      eend $?
1187      einfo "Make sure to run squashfs stage, otherwise your local files won't be part of the ISO."
1188      FORCE_ISO_REBUILD=true
1189   fi
1190 fi
1191
1192 if [ -f "$BUILD_OUTPUT"/live/${GRML_NAME}.squashfs -a -z "$UPDATE" -a -z "$BUILD_ONLY" -a -z "$BUILD_DIRTY" ] ; then
1193    log   "Skipping stage 'squashfs' as $BUILD_OUTPUT/live exists already."
1194    ewarn "Skipping stage 'squashfs' as $BUILD_OUTPUT/live exists already." ; eend 0
1195 elif [ -n "$SKIP_MKSQUASHFS" ] ; then
1196    log   "Skipping stage 'squashfs' as requested via option -q or -N"
1197    ewarn "Skipping stage 'squashfs' as requested via option -q or -N" ; eend 0
1198 else
1199    mkdir -p "$BUILD_OUTPUT"/live/"${GRML_NAME}"/
1200    # make sure we don't leave (even an empty) base.tgz:
1201    [ -f "$CHROOT_OUTPUT/base.tgz" ] && rm -f "$CHROOT_OUTPUT/base.tgz"
1202
1203    # if unconfigured default to squashfs-tools' mksquashfs binary
1204    if [ -z "$SQUASHFS_BINARY" ] ; then
1205       SQUASHFS_BINARY='mksquashfs'
1206    fi
1207
1208    if which "$SQUASHFS_BINARY" >/dev/null 2>&1 ; then
1209       log    "Using mksquashfs binary ${SQUASHFS_BINARY}"
1210       einfo  "Using mksquashfs binary ${SQUASHFS_BINARY}" ; eend 0
1211    else
1212       log    "Error: mksquashfs binary ($SQUASHFS_BINARY) not found. Exiting."
1213       eerror "Error: mksquashfs binary ($SQUASHFS_BINARY) not found. Exiting." ; eend 1
1214       bailout
1215    fi
1216
1217    # use sane defaults if $SQUASHFS_OPTIONS isn't set
1218    if [ -z "$SQUASHFS_OPTIONS" ] ; then
1219      # use blocksize 256k as this gives best result with regards to time + compression
1220      SQUASHFS_OPTIONS="-b 256k"
1221
1222      # set lzma/xz compression by default, unless -z option has been specified on command line
1223      if [ -z "$SQUASHFS_ZLIB" ] ; then
1224         SQUASHFS_OPTIONS="$SQUASHFS_OPTIONS -comp xz"
1225      else
1226         SQUASHFS_OPTIONS="$SQUASHFS_OPTIONS -comp gzip"
1227      fi
1228    fi
1229
1230    # support exclusion of files via exclude-file:
1231    if [ -n "$SQUASHFS_EXCLUDES_FILE" -a "$SQUASHFS_EXCLUDES_FILE" ] ; then
1232       SQUASHFS_OPTIONS="$SQUASHFS_OPTIONS -ef $SQUASHFS_EXCLUDES_FILE -wildcards"
1233    fi
1234
1235    # get rid of unnecessary files when building grml-small for final release:
1236    if echo "$CLASSES" | grep -q GRML_SMALL ; then
1237       SQUASHFS_OPTIONS="$SQUASHFS_OPTIONS -e initrd.img* vmlinuz*"
1238    fi
1239
1240    # log stuff
1241    SQUASHFS_STDERR="$(mktemp -t grml-live.XXXXXX)"
1242
1243    # informational stuff
1244    [ -n "$SQUASHFS_OPTIONS" ]  && SQUASHFS_INFO_MSG="$SQUASHFS_OPTIONS"
1245    [ -n "$SQUASHFS_INFO_MSG" ] && SQUASHFS_INFO_MSG="using options: $SQUASHFS_INFO_MSG"
1246    einfo "Squashfs build information: running binary $SQUASHFS_BINARY $SQUASHFS_INFO_MSG"
1247
1248    log "$SQUASHFS_BINARY $CHROOT_OUTPUT/ $BUILD_OUTPUT/live/${GRML_NAME}/${GRML_NAME}.squashfs -noappend $SQUASHFS_OPTIONS"
1249
1250    if $SQUASHFS_BINARY $CHROOT_OUTPUT/ $BUILD_OUTPUT/live/"${GRML_NAME}"/"${GRML_NAME}".squashfs \
1251       -noappend $SQUASHFS_OPTIONS 2>"${SQUASHFS_STDERR}" ; then
1252       echo "${GRML_NAME}.squashfs" > $BUILD_OUTPUT/live/"${GRML_NAME}"/filesystem.module
1253       log "Finished execution of stage 'squashfs' [$(date)]"
1254       einfo "Finished execution of stage 'squashfs'" ; eend 0
1255    else
1256       log    "Error: there was a critical error executing stage 'squashfs' [$(date)]:"
1257       log    "$(cat $SQUASHFS_STDERR)"
1258       eerror "Error: there was a critical error executing stage 'squashfs':"
1259       cat    "${SQUASHFS_STDERR}"
1260       eend 1
1261       bailout
1262    fi
1263
1264    FORCE_ISO_REBUILD=true
1265 fi
1266
1267 # create md5sum file:
1268 if [ -z "$BOOTSTRAP_ONLY" ] ; then
1269   ( cd $BUILD_OUTPUT/GRML/"${GRML_NAME}" &&
1270   find ../.. -type f -not -name md5sums -not -name isolinux.bin -exec md5sum {} \; > md5sums )
1271 fi
1272 # }}}
1273
1274 # ISO_OUTPUT - mkisofs {{{
1275 [ -n "$ISO_OUTPUT" ] || ISO_OUTPUT="$OUTPUT/grml_isos"
1276 [ -n "$ISO_NAME" ] || ISO_NAME="${GRML_NAME}_${VERSION}.iso"
1277
1278 if [ "$BOOT_METHOD" = "isolinux" ] ; then
1279    BOOT_ARGS="-no-emul-boot -boot-load-size 4 -boot-info-table -b boot/isolinux/isolinux.bin -c boot/isolinux/boot.cat"
1280 elif [ "$BOOT_METHOD" = "grub2" ] ; then
1281    BOOT_ARGS="-no-emul-boot -boot-load-size 4 -b boot/grub/toriboot.bin"
1282 fi
1283
1284 # Work around http://bts.grml.org/grml/issue945
1285 if [[ $BOOT_METHOD != isolinux && ($HYBRID_METHOD = isohybrid || $HYBRID_METHOD = manifold) ]]; then
1286   log   "Setting HYBRID_METHOD to grub2 as hybrid mode does not work with isohybrid yet."
1287   ewarn "Setting HYBRID_METHOD to grub2 as hybrid mode does not work with isohybrid yet."
1288   HYBRID_METHOD='grub2'
1289   eend 0
1290 fi
1291
1292 if [ -f "${ISO_OUTPUT}/${ISO_NAME}" -a -z "$UPDATE" -a -z "$BUILD_ONLY" -a -z "$BUILD_DIRTY" -a "$FORCE_ISO_REBUILD" = "false" ]  ; then
1293    log   "Skipping stage 'iso build' as $ISO_OUTPUT/${ISO_NAME} exists already."
1294    ewarn "Skipping stage 'iso build' as $ISO_OUTPUT/${ISO_NAME} exists already." ; eend 0
1295 elif [ -n "$SKIP_MKISOFS" ] ; then
1296    log   "Skipping stage 'iso build' as requested via option -n or -N"
1297    ewarn "Skipping stage 'iso build' as requested via option -n or -N" ; eend 0
1298 else
1299    mkdir -p "$ISO_OUTPUT" || bailout 6 "Problem with creating $ISO_OUTPUT for stage 'iso build'"
1300
1301    if $FORCE_ISO_REBUILD && ! [ -f "${ISO_OUTPUT}/${ISO_NAME}" ] ; then
1302       log   "Forcing rebuild of ISO because files on ISO have been modified."
1303       einfo "Forcing rebuild of ISO because files on ISO have been modified."
1304    fi
1305
1306    # support xorriso as well mkisofs and genisoimage
1307    if which xorriso >/dev/null 2>&1 ; then
1308       MKISOFS='xorriso -as mkisofs'
1309     elif which mkisofs >/dev/null 2>&1; then
1310       MKISOFS='mkisofs'
1311    elif which genisoimage >/dev/null 2>&1; then
1312       MKISOFS='genisoimage'
1313    else
1314       log    "Error: neither xorriso nor mkisofs nor genisoimage available - can not create ISO."
1315       eerror "Error: neither xorriso nor mkisofs nor genisoimage available - can not create ISO." ; eend 1
1316       bailout
1317    fi
1318
1319    einfo "Using ${MKISOFS} to build ISO." ;  eend 0
1320    case "${ARCH}-${MKISOFS}" in
1321      # using -eltorito-alt-boot is limited to xorriso for now
1322      amd64-xorriso*)
1323        eindent
1324
1325        if ! dpkg --compare-versions $(dpkg-query -W -f='${Version}\n' xorriso 2>/dev/null) gt-nl 1.1.6-1 ; then
1326          log   "Disabling (U)EFI boot support because xorriso version is too old."
1327          ewarn "Disabling (U)EFI boot support because xorriso version is too old." ; eend 0
1328        else
1329          if [ -r "${BUILD_OUTPUT}"/boot/efi.img ] ; then
1330            einfo "Enabling (U)EFI boot."
1331            log   "Enabling (U)EFI boot."
1332            BOOT_ARGS="$BOOT_ARGS -boot-info-table -eltorito-alt-boot -e boot/efi.img -no-emul-boot"
1333            eend $?
1334          else
1335            log   "Disabling (U)EFI boot support because /boot/efi.img is missing."
1336            ewarn "Disabling (U)EFI boot support because /boot/efi.img is missing." ; eend 0
1337          fi
1338        fi
1339
1340        eoutdent
1341        ;;
1342    esac
1343
1344    CURRENT_DIR=$(pwd)
1345    if cd "$BUILD_OUTPUT" ; then
1346       if [ "$BOOT_METHOD" = "grub2" ]; then
1347          # make a 2048-byte bootsector for El Torito
1348          dd if=/dev/zero of=boot/grub/toriboot.bin bs=512 count=4 2>/dev/null
1349          # those are in 2048-byte sectors, so 1 16 matches 4 63 below
1350          echo 1 16 | mksh "${SCRIPTS_DIRECTORY}/bootgrub.mksh" -B 11 | \
1351             dd of=boot/grub/toriboot.bin conv=notrunc 2>/dev/null
1352       fi
1353       log "$MKISOFS -V '${GRML_NAME} ${VERSION}' -publisher 'grml-live | grml.org' -l -r -J $BOOT_ARGS -o ${ISO_OUTPUT}/${ISO_NAME} ."
1354       $MKISOFS -V "${GRML_NAME} ${VERSION}" -publisher 'grml-live | grml.org' \
1355               -l -r -J $BOOT_ARGS -no-pad \
1356               -o "${ISO_OUTPUT}/${ISO_NAME}" . ; RC=$?
1357       # both of these need core.img there, so it’s easier to write it here
1358       if [ "$BOOT_METHOD" = "grub2" ] || [ "$HYBRID_METHOD" = "grub2" ]; then
1359          # must be <= 30720 bytes
1360          dd if=boot/grub/core.img of="${ISO_OUTPUT}/${ISO_NAME}" \
1361            conv=notrunc bs=512 seek=4 2>/dev/null
1362       fi
1363
1364       # pad the output ISO to multiples of 256 KiB for partition table support
1365       siz=$($getfilesize "${ISO_OUTPUT}/${ISO_NAME}")
1366       cyls=$((siz / 512 / 32 / 16 + 1))   # C=$cyls H=16 S=32
1367       siz=$((cyls * 16 * 32 * 512))   # size after padding
1368       dd if=/dev/zero bs=1 count=1 seek=$((siz - 1)) \
1369          of="${ISO_OUTPUT}/${ISO_NAME}" 2>/dev/null
1370
1371       # support disabling hybrid ISO image
1372       if [ "$HYBRID_METHOD" = "disable" ] ; then
1373         log   "Skipping creation of hybrid ISO file as requested via HYBRID_METHOD=disable"
1374         einfo "Skipping creation of hybrid ISO file as requested via HYBRID_METHOD=disable"
1375         eend 0
1376       elif [ "$HYBRID_METHOD" = "manifold" ] || [ "$HYBRID_METHOD" = "grub2" ] ; then
1377         # isoinfo is part of both mkisofs and genisoimage so we're good
1378         bootoff=$(isoinfo -l -i "${ISO_OUTPUT}/${ISO_NAME}" | \
1379           sed -n '/^.*\[ *\([0-9]*\)[] ].* ISOLINUX.BIN[;1]* *$/s//\1/p')
1380
1381         if ! [ -r boot/grub/core.img ] ; then
1382           log   "boot/grub/core.img not found, not creating manifold boot ISO file"
1383           ewarn "boot/grub/core.img not found, not creating manifold boot ISO file"
1384         elif [ "${bootoff:-0}" -lt 1 ] ; then
1385           log   "isolinux.bin not found on the ISO file, disabling manifold boot"
1386           ewarn "isolinux.bin not found on the ISO file, disabling manifold boot"
1387         else
1388           if [ "$HYBRID_METHOD" = "grub2" ] ; then
1389             log   "Creating hybrid ISO file with manifold/grub2 method"
1390             einfo "Creating hybrid ISO file with manifold/grub2 method"
1391             # 512 bytes: MBR, partition table, load GRUB 2
1392             echo 4 63 | mksh "${SCRIPTS_DIRECTORY}/bootgrub.mksh" -A -M 4:0x96 -g $cyls:16:32
1393           else
1394             log   "Creating hybrid ISO file with manifold method"
1395             einfo "Creating hybrid ISO file with manifold method"
1396             # read only one but 2048-byte sized (scale: << 2) sector
1397             echo $bootoff $bootoff | \
1398               mksh ${SCRIPTS_DIRECTORY}/bootilnx.mksh -A -M 4:0x96 -g $cyls:16:32 -S 2
1399           fi | dd of="${ISO_OUTPUT}/${ISO_NAME}" conv=notrunc 2>/dev/null
1400           eend $?
1401         fi
1402       elif [ "$HYBRID_METHOD" = "isohybrid" ] ; then
1403         if ! which isohybrid >/dev/null 2>&1 ; then
1404           bailout 12 "isohybrid binary not found - please install syslinux/syslinux-common/syslinux-utils"
1405         else
1406           log   "Creating hybrid ISO file with isohybrid method"
1407           einfo "Creating hybrid ISO file with isohybrid method"
1408           # Notes for consideration:
1409           # "-entry 4 -type 1c"
1410           # * using 4 as the partition number is supposed to help with BIOSes
1411           #   that only support USB-Zip boot
1412           # * using 1c (i.e. hidden FAT32 LBA), instead of the default 0x17
1413           #   (hidden NTFS, IIRC), as the partition type is sometimes needed
1414           #   to get the BIOS even look at the partition created by isohybrid
1415           if isohybrid --help | grep -q -- --uefi ; then
1416             if echo $CLASSES | grep -qw I386 ; then
1417               log   "Detected uefi support for isohybrid but 32bit systems do not support it, ignoring."
1418               einfo "Detected uefi support for isohybrid but 32bit systems do not support it, ignoring."
1419             else
1420               log   "Detected uefi support for isohybrid, enabling"
1421               einfo "Detected uefi support for isohybrid, enabling"
1422               ISOHYBRID_OPTIONS=--uefi
1423             fi
1424           fi
1425
1426           log "isohybrid $ISOHYBRID_OPTIONS ${ISO_OUTPUT}/${ISO_NAME}"
1427           isohybrid $ISOHYBRID_OPTIONS "${ISO_OUTPUT}/${ISO_NAME}"
1428           eend $?
1429         fi
1430       else
1431         bailout 12 "Unknown HYBRID_METHOD [${HYBRID_METHOD}]. Supported values: disable, isohybrid, grub2, manifold"
1432       fi
1433
1434       # generate md5sum and sha1sum of ISO if we are using class 'RELEASE':
1435       case $CLASSES in *RELEASE*)
1436          [ "$RC" = 0 ] && \
1437          (
1438            if cd $ISO_OUTPUT ; then
1439              md5sum ${ISO_NAME} > ${ISO_NAME}.md5 && \
1440              touch -r ${ISO_NAME} ${ISO_NAME}.md5
1441              sha1sum ${ISO_NAME} > ${ISO_NAME}.sha1 && \
1442              touch -r ${ISO_NAME} ${ISO_NAME}.sha1
1443            fi
1444          )
1445          ;;
1446       esac
1447
1448       cd "$CURRENT_DIR"
1449    fi
1450
1451    if [ "$RC" = 0 ] ; then
1452       log   "Finished execution of stage 'iso build' [$(date)]"
1453       einfo "Finished execution of stage 'iso build'" ; eend 0
1454    else
1455       log    "Error: there was a critical error ($RC) executing stage 'iso build' [$(date)]"
1456       eerror "Error: there was a critical error executing stage 'iso build'" ; eend 1
1457       bailout $RC
1458    fi
1459 fi
1460 # }}}
1461
1462 # netboot package {{{
1463 create_netbootpackage() {
1464   local OUTPUT_FILE="${NETBOOT}/grml_netboot_package_${GRML_NAME}_${VERSION}.tar.bz2"
1465
1466   if [ -f "${OUTPUT_FILE}" -a -z "$UPDATE" -a -z "$BUILD_ONLY" -a -z "$BUILD_DIRTY" ] ; then
1467     log   "Skipping stage 'netboot' as $OUTPUT_FILE exists already."
1468     ewarn "Skipping stage 'netboot' as $OUTPUT_FILE exists already." ; eend 0
1469     return 0
1470   elif [ -n "$SKIP_NETBOOT" ] ; then
1471     log   "Skipping stage 'netboot' as requested via option -Q"
1472     ewarn "Skipping stage 'netboot' as requested via option -Q" ; eend 0
1473     return 0
1474   fi
1475
1476   mkdir -p "$NETBOOT"
1477
1478   # since syslinux v3:6.03~pre1+dfsg-4 the pxelinux.0 has been split into a
1479   # separate pxelinux package
1480   if [ -d "${CHROOT_OUTPUT}/usr/lib/PXELINUX/" ] ; then
1481     local pxelinux_dir=/usr/lib/PXELINUX
1482   else
1483     local pxelinux_dir=/usr/lib/syslinux
1484   fi
1485
1486   if ! [ -r "${CHROOT_OUTPUT}/${pxelinux_dir}/pxelinux.0" ] ; then
1487     ewarn "File ${pxelinux_dir}/pxelinux.0 not found in build chroot." ; eend 0
1488     eindent
1489     einfo "Install syslinux[-common]/pxelinux package in chroot to get a netboot package."
1490     eoutdent
1491     return 0
1492   fi
1493
1494   local OUTPUTDIR="${NETBOOT}/build_tmp"
1495   local WORKING_DIR="${OUTPUTDIR}/grml_netboot_package_${GRML_NAME}_${VERSION}/tftpboot/"
1496
1497   mkdir -p "$WORKING_DIR"
1498
1499   cp "${CHROOT_OUTPUT}"/boot/vmlinuz-*    "$WORKING_DIR"/vmlinuz
1500   cp "${CHROOT_OUTPUT}"/boot/initrd.img-* "$WORKING_DIR"/initrd.img
1501   cp "${CHROOT_OUTPUT}/${pxelinux_dir}/pxelinux.0" "${WORKING_DIR}/pxelinux.0"
1502
1503   if [ -r "${CHROOT_OUTPUT}"/usr/lib/syslinux/modules/bios/ldlinux.c32 ] ; then
1504     cp "${CHROOT_OUTPUT}"/usr/lib/syslinux/modules/bios/ldlinux.c32 "${WORKING_DIR}"/
1505   fi
1506
1507   mkdir -p "${WORKING_DIR}/pxelinux.cfg"
1508   if [ -r "${BUILD_OUTPUT}/boot/isolinux/netboot.cfg" ] ; then
1509     cp "${BUILD_OUTPUT}/boot/isolinux/netboot.cfg" "${WORKING_DIR}/pxelinux.cfg/default"
1510   else
1511     log   "File ${BUILD_OUTPUT}/boot/isolinux/netboot.cfg not found."
1512     ewarn "File ${BUILD_OUTPUT}/boot/isolinux/netboot.cfg not found."
1513     eindent
1514     log   "Hint: Are you using custom templates which do not provide netboot.cfg?"
1515     ewarn "Hint: Are you using custom templates which do not provide netboot.cfg?" ; eend 0
1516     eoutdent
1517   fi
1518
1519   if tar -C "$OUTPUTDIR" -jcf "${OUTPUT_FILE}" "grml_netboot_package_${GRML_NAME}_${VERSION}" ; then
1520     (
1521       cd $(dirname "${OUTPUT_FILE}")
1522       sha1sum $(basename "${OUTPUT_FILE}") > "${OUTPUT_FILE}.sha1"
1523     )
1524     einfo "Generated netboot package ${OUTPUT_FILE}" ; eend 0
1525     rm -rf "${OUTPUTDIR}"
1526   else
1527     rm -rf "${OUTPUTDIR}"
1528     eerror "Could not generate netboot package ${OUTPUT_FILE}" ; eend 1
1529     bailout 21
1530   fi
1531 }
1532
1533 create_netbootpackage
1534 # }}}
1535
1536 # log build information to database if grml-live-db is installed and enabled {{{
1537 dpkg_to_db() {
1538 if [ -d /usr/share/grml-live-db ] ; then
1539
1540   # safe defaults
1541   DPKG_LIST="/var/log/fai/$HOSTNAME/last/dpkg.list" # the dpkg --list output of the chroot:
1542   [ -n "$DPKG_DATABASE" ]  || DPKG_DATABASE=/var/log/grml-live.db
1543   [ -n "$DPKG_DBSCRIPT" ]  || DPKG_DBSCRIPT=/usr/share/grml-live-db/scripts/dpkg-to-db
1544   [ -n "$DPKG_DBOPTIONS" ] || DPKG_DBOPTIONS="--database $DPKG_DATABASE --logfile $LOGFILE --flavour $GRML_NAME --dpkg $DPKG_LIST"
1545
1546   if ! [ -x "$DPKG_DBSCRIPT" ] ; then
1547     log "Error: $DPKG_DBSCRIPT is not executable, can not log dpkg information."
1548     eerror "Error: $DPKG_DBSCRIPT is not executable, can not log dpkg information." ; eend 1
1549     bailout 14
1550   fi
1551
1552   # disable by default for now, not sure whether really everyone is using a local db file
1553   #if ! touch "$DPKG_DATABASE" ; then
1554   #  eerror "Error: can not write to ${DPKG_DATABASE}, can not log dpkg information." ; eend 1
1555   #  bailout 14
1556   #fi
1557
1558   if ! [ -r "$DPKG_LIST" ] ; then
1559      log   "Warning: can not read $DPKG_LIST - can not provide information to $DPKG_DBSCRIPT (dirty build?)"
1560      ewarn "Warning: can not read $DPKG_LIST - can not provide information to $DPKG_DBSCRIPT (dirty build?)" ; eend 0
1561   else
1562      einfo "Logging $DPKG_LIST to database $DPKG_DATABASE"
1563      log "Logging $DPKG_LIST to database $DPKG_DATABASE"
1564      log "Executing $DPKG_DBSCRIPT $DPKG_DBOPTIONS"
1565      eindent
1566
1567      if DB_INFO=$("$DPKG_DBSCRIPT" $DPKG_DBOPTIONS 2>&1) ; then
1568        einfo "$DB_INFO"
1569        eend 0
1570      else
1571        eerror "$DB_INFO"
1572        eend 1
1573      fi
1574
1575      eoutdent
1576   fi
1577
1578 fi
1579 }
1580 # }}}
1581
1582 # finalize {{{
1583 [ -n "$start_seconds" ] && SECONDS="$[$(cut -d . -f 1 /proc/uptime)-$start_seconds]" || SECONDS="unknown"
1584 log "Successfully finished execution of $PN [$(date) - running ${SECONDS} seconds]"
1585
1586 dpkg_to_db # make sure we catch the last log line as well, therefore execute between log + einfo
1587
1588 einfo "Successfully finished execution of $PN [$(date) - running ${SECONDS} seconds]" ; eend 0
1589 bailout 0
1590 # }}}
1591
1592 ## END OF FILE #################################################################
1593 # vim:foldmethod=marker ts=2 ft=sh ai expandtab tw=80 sw=2