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