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