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