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