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