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 ################################################################################
11 # some misc and global stuff {{{
15 # define function getfilesize before "set -e"
16 if stat --help >/dev/null 2>&1; then
17 getfilesize='stat -c %s' # GNU stat
19 getfilesize='stat -f %z' # BSD stat
23 # disable for now since it seems to cause some problems
27 GRML_LIVE_VERSION='0.17.0+autobuild1323909002'
30 ADDONS_LIST_FILE='/boot/isolinux/addons_list.cfg'
33 # usage information {{{
37 $PN - build process script for generating a (grml based) Linux Live-ISO
39 Usage: $PN [options, see as follows]
41 -a <architecture> architecture; available values: i386 and amd64
42 -A clean build directories before and after running
43 -b build the ISO without updating the chroot via FAI
44 -B build the ISO without touching the chroot (skips cleanup)
45 -c <classe[s]> classes to be used for building the ISO via FAI
46 -C <configfile> configuration file for grml-live
47 -d <date> use specified date instead of build time as date of release
48 -D <configdir> use specified configuration directory instead of /etc/grml/fai
49 -e <iso_name> extract ISO and squashfs contents from iso_name
50 -F force execution without prompting
51 -g <grml_name> set the grml flavour name
52 -h display short usage information and exit
53 -i <iso_name> name of ISO
54 -I <src_directory> directory which provides files that should become
55 part of the chroot/ISO
56 -n skip generation of ISO
57 -N bootstrap (build chroot) only, do not create files for ISO
58 -o <output_directory> main output directory of the build process
60 -Q skip netboot package build
61 -r <release_name> release name
62 -s <suite> Debian suite; values: etch, lenny, squeeze, sid
63 -t <template_directory> place of the templates
64 -u update existing chroot instead of rebuilding it from scratch
65 -U <username> arrange output to be owned by specified username
66 -v <version_number> specify version number of the release
67 -V increase verbosity in the build process
68 -z use ZLIB instead of LZMA/XZ compression
73 $PN -c GRMLBASE,GRML_FULL,AMD64 -o /dev/shm/grml
74 $PN -c GRMLBASE,GRML_FULL,AMD64 -i grml_0.0-1.iso -v 0.0-1
75 $PN -c GRMLBASE,GRML_FULL,AMD64 -s sid -V -r 'grml-live rocks'
77 More details: man grml-live + /usr/share/doc/grml-live/grml-live.html
78 http://grml.org/grml-live/
80 Please send your bug reports and feedback to the grml-team: http://grml.org/bugs/
84 # make sure it's possible to get usage information without being
85 # root or actually executing the script
86 if [ "$1" = '-h' -o "$1" = '--help' ] ; then
88 [ "$(id -u 2>/dev/null)" != 0 ] && echo "Please notice that this script requires root permissions."
93 # some runtime checks {{{
94 # we need root permissions for the build-process:
95 if [ "$(id -u 2>/dev/null)" != 0 ] ; then
96 echo "Error: please run this script with uid 0 (root)." >&2
100 if [ -r /var/run/fai/FAI_INSTALLATION_IN_PROGRESS ] ; then
101 echo "/usr/sbin/fai already running or was aborted before.">&2
102 echo "You may remove /var/run/fai/FAI_INSTALLATION_IN_PROGRESS and try again.">&2
107 if [ -r /var/run/fai/fai_softupdate_is_running ] ; then
108 echo "/usr/sbin/fai softupdate already running or was aborted before.">&2
109 echo "You may remove /var/run/fai/fai_softupdate_is_running and try again.">&2
114 # lsb-functions and configuration stuff {{{
115 # make sure they are not set by default
126 # don't use colors/escape sequences
127 if [ -r /lib/lsb/init-functions ] ; then
128 . /lib/lsb/init-functions
129 ! log_use_fancy_output && NOCOLORS=true
132 if [ -r /etc/grml/lsb-functions ] ; then
133 . /etc/grml/lsb-functions
135 einfo() { echo " [*] $*" ;}
136 eerror() { echo " [!] $*">&2 ;}
137 ewarn() { echo " [x] $*" ;}
139 eindent() { return 0 ;}
140 eoutdent() { return 0 ;}
143 # source main configuration file:
144 LIVE_CONF=/etc/grml/grml-live.conf
148 # umount all directories {{{
150 # make sure we don't leave any mounts - FAI doesn't remove them always
151 umount $CHROOT_OUTPUT/proc/sys/fs/binfmt_misc 2>/dev/null || /bin/true
152 umount $CHROOT_OUTPUT/proc 2>/dev/null || /bin/true
153 umount $CHROOT_OUTPUT/sys 2>/dev/null || /bin/true
154 umount $CHROOT_OUTPUT/dev/pts 2>/dev/null || /bin/true
155 umount $CHROOT_OUTPUT/dev 2>/dev/null || /bin/true
157 # certain FAI versions sadly leave a ramdisk behind, so better safe than sorry
158 if [ -x /usr/lib/fai/mkramdisk ] ; then
159 /usr/lib/fai/mkramdisk -u "$(readlink -f ${CHROOT_OUTPUT}/var/lib/dpkg)" >/dev/null 2>&1 || /bin/true
162 umount "${CHROOT_OUTPUT}/grml-live/sources/" 2>/dev/null || /bin/true
163 [ -n "$MIRROR_DIRECTORY" ] && umount "${CHROOT_OUTPUT}/${MIRROR_DIRECTORY}"
169 rm -f /var/run/fai/fai_softupdate_is_running \
170 /var/run/fai/FAI_INSTALLATION_IN_PROGRESS
171 [ -n "$CONFIGDUMP" ] && rm -f "$CONFIGDUMP"
172 [ -n "$SQUASHFS_STDERR" ] && rm -rf "$SQUASHFS_STDERR"
174 [ -n "$1" ] && EXIT="$1" || EXIT="1"
175 [ -n "$2" ] && eerror "$2">&2
176 if [ -n "$CLEAN_ARTIFACTS" ]; then
179 [ -n "${BUILD_OUTPUT}" -a -d "${BUILD_OUTPUT}" ] && rm -r "${BUILD_OUTPUT}"
180 [ -n "${CHROOT_OUTPUT}" -a -d "${CHROOT_OUTPUT}" ] && rm -r "${CHROOT_OUTPUT}"
183 if [ -n "$CHOWN_USER" ]; then
184 log "Setting ownership"
185 einfo "Setting ownership"
186 [ -n "${OUTPUT}" -a -d "${OUTPUT}" ] && chown -R "${CHOWN_USER}:" "${OUTPUT}"
187 [ -n "${BUILD_OUTPUT}" -a -d "${BUILD_OUTPUT}" ] && chown -R "${CHOWN_USER}:" "${BUILD_OUTPUT}"
188 [ -n "${CHROOT_OUTPUT}" -a -d "${CHROOT_OUTPUT}" ] && chown -R "${CHOWN_USER}:" "${CHROOT_OUTPUT}"
189 [ -n "${ISO_OUTPUT}" -a -d "${ISO_OUTPUT}" ] && chown -R "${CHOWN_USER}:" "${ISO_OUTPUT}"
190 [ -n "${LOG_OUTPUT}" -a -d "${LOG_OUTPUT}" ] && chown -R "${CHOWN_USER}:" "${LOG_OUTPUT}"
191 [ -n "${NETBOOT}" -a -d "${NETBOOT}" ] && chown -R "${CHOWN_USER}:" "${NETBOOT}"
194 log "------------------------------------------------------------------------------"
197 trap bailout 1 2 3 3 6 9 14 15
201 # some important functions {{{
204 # usage: log "string to log"
205 log() { [ -n "$LOGFILE" ] && echo "$*" >> $LOGFILE ; }
207 # cut string at character number int = $1
208 # usage: cut_string 5 "1234567890" will output "12345"
210 [ -n "$2" ] || return 1
211 echo "$2" | head -c "$1"; echo -ne "\n"
214 # prepend int = $1 spaces before string = $2
215 # usage: extend_string_begin 5 "123" will output " 123"
216 extend_string_begin() {
217 [ -n "$2" ] || return 1
218 local COUNT="$(echo $2 | wc -c)"
219 local FILL="$(expr $COUNT - $1)"
220 while [ "$FILL" -gt 1 ] ; do
222 local FILL=$(expr $FILL - 1)
224 while [ "$FILL" -lt 1 ] ; do
226 local FILL=$(expr $FILL + 1)
228 echo "$2" | head -c "$1"; echo -ne "\n"
231 # append int = $1 spaces to string = $2
232 # usage: extend_string_begin 5 "123" will output "123 "
233 extend_string_end() {
234 [ -n "$2" ] || return 1
235 echo -n "$2" | head -c "$1"
236 local COUNT="$(echo $2 | wc -c)"
237 local FILL="$(expr $COUNT - $1)"
238 while [ "$FILL" -gt 1 ] ; do
240 local FILL=$(expr $FILL - 1)
242 while [ "$FILL" -lt 1 ] ; do
244 local FILL=$(expr $FILL + 1)
249 # Copy addonfile $1 from either
250 # * the chroot (via $2, the system path),
251 # * or from TEMPLATE_DIRECTORY/compat (if exists),
252 # * or from the host system (again, using $2),
253 # or warn about the missing file.
256 # * We assume that the chroot always has a "good" version of
257 # the file. Also it makes sources handling easier.
258 # * On unstable, we Recommend the Debian packages containing
259 # these files. The user can override them by putting his
260 # "better" version into the chroot.
261 # * On stable, the Debian packages are probably not available,
262 # or outdated, so we look in TEMPLATE_DIRECTORY/compat first, where
263 # our grml-live-compat package installs current file versions.
265 DEST="${BUILD_OUTPUT}/boot/$3"
266 if [ ! -d "${DEST}/" ]; then
269 if [ -e "$CHROOT_OUTPUT/$2/$1" ]; then
270 log "Copying $1 from chroot"
271 cp "$CHROOT_OUTPUT/$2/$1" "${DEST}/"
274 if [ -e "${TEMPLATE_DIRECTORY}/compat/$3/$1" ]; then
275 log "Copying $1 from grml-live-compat"
276 cp "${TEMPLATE_DIRECTORY}/compat/$3/$1" "${DEST}/"
279 if [ -e "$2/$1" ]; then
280 log "Copying $1 from system"
281 cp "$2/$1" "${DEST}/"
285 msg="Missing addon file: \"$1\""
286 ewarn "$msg" ; eend 1
287 log "copy_addon_file: $msg"
291 # command line parsing {{{
292 while getopts "a:C:c:d:D:e:g:i:I:o:r:s:t:U:v:AbBFnNqQuVz" opt; do
295 A) CLEAN_ARTIFACTS=1 ;;
298 c) CLASSES="$OPTARG" ;;
299 C) LOCAL_CONFIG="$(readlink -f $OPTARG)" ;;
301 D) GRML_FAI_CONFIG="$(readlink -f $OPTARG)" ;;
302 e) EXTRACT_ISO_NAME="$(readlink -f $OPTARG)" ;;
303 g) GRML_NAME="$OPTARG" ;;
304 i) ISO_NAME="$OPTARG" ;;
305 I) CHROOT_INSTALL="$OPTARG" ;;
307 N) BOOTSTRAP_ONLY=1; SKIP_MKISOFS=1; SKIP_MKSQUASHFS=1 ;;
308 o) OUTPUT="$(readlink -f $OPTARG)" ;;
309 q) SKIP_MKSQUASHFS=1 ;;
311 r) RELEASENAME="$OPTARG" ;;
312 s) SUITE="$OPTARG" ;;
313 t) TEMPLATE_DIRECTORY="$OPTARG";;
314 v) VERSION="$OPTARG" ;;
317 U) CHOWN_USER="$OPTARG" ;;
319 z) SQUASHFS_ZLIB=1 ;;
320 ?) echo "invalid option -$OPTARG" >&2; bailout 1 ;;
323 shift $(($OPTIND - 1)) # set ARGV to the first not parsed commandline parameter
326 # read local (non-packaged) configuration {{{
327 if [ -z "$LOCAL_CONFIG" ]; then
328 if [ -r "/etc/grml/grml-live.local" ]; then
329 LOCAL_CONFIG="/etc/grml/grml-live.local"
332 if [ -n "$LOCAL_CONFIG" ]; then
333 if [ -r "$LOCAL_CONFIG" ]; then
336 eerror "Could not read specified local configuration file \"$LOCAL_CONFIG\"."
339 LOCAL_CONFIG=$(readlink -f "$LOCAL_CONFIG")
344 if [ -n "${GRML_LIVE_SOURCES:-}" ] ; then
345 eerror "Config variable \$GRML_LIVE_SOURCES is set. This variable has been deprecated."
346 ewarn "Please set up \${GRML_FAI_CONFIG}/config/files/etc/apt/sources.list.d/* instead."
351 # assume sane defaults (if not set already) {{{
352 [ -n "$ARCH" ] || ARCH="$(dpkg --print-architecture)"
353 [ -n "$BOOT_METHOD" ] || BOOT_METHOD='isolinux'
354 [ -n "$CLASSES" ] || CLASSES="GRMLBASE,GRML_FULL,$(echo ${ARCH} | tr 'a-z' 'A-Z')"
355 [ -n "$DATE" ] || DATE="$(date +%Y-%m-%d)"
356 [ -n "$DISTRI_INFO" ] || DISTRI_INFO='Grml - Live Linux for system administrators '
357 [ -n "$DISTRI_NAME" ] || DISTRI_NAME="grml"
358 [ -n "$DISTRI_SPLASH" ] || DISTRI_SPLASH='grml.png'
359 [ -n "$FORCE_ISO_REBUILD" ] || FORCE_ISO_REBUILD="false"
360 [ -n "$GRML_FAI_CONFIG" ] || GRML_FAI_CONFIG='/etc/grml/fai'
361 [ -n "$GRML_NAME" ] || GRML_NAME='grml'
362 [ -n "$HOSTNAME" ] || HOSTNAME='grml'
363 [ -n "$HYBRID_METHOD" ] || HYBRID_METHOD='manifold'
364 [ -n "$NFSROOT_CONF" ] || NFSROOT_CONF="${GRML_FAI_CONFIG}/make-fai-nfsroot.conf"
365 [ -n "$RELEASENAME" ] || RELEASENAME='grml-live rocks'
366 [ -n "$SQUASHFS_EXCLUDES_FILE" ] || SQUASHFS_EXCLUDES_FILE="${GRML_FAI_CONFIG}/config/grml/squashfs-excludes"
367 [ -n "$SUITE" ] || SUITE='squeeze'
368 [ -n "$TEMPLATE_DIRECTORY" ] || TEMPLATE_DIRECTORY='/usr/share/grml-live/templates'
369 [ -n "$USERNAME" ] || USERNAME='grml'
370 [ -n "$VERSION" ] || VERSION='0.0.1'
372 # output specific stuff, depends on $OUTPUT (iff not set):
373 [ -n "$OUTPUT" ] || OUTPUT='/grml/grml-live'
374 [ -n "$BUILD_OUTPUT" ] || BUILD_OUTPUT="$OUTPUT/grml_cd"
375 [ -n "$CHROOT_OUTPUT" ] || CHROOT_OUTPUT="$OUTPUT/grml_chroot"
376 [ -n "$ISO_OUTPUT" ] || ISO_OUTPUT="$OUTPUT/grml_isos"
377 [ -n "$LOG_OUTPUT" ] || LOG_OUTPUT="$OUTPUT/grml_logs"
378 [ -n "$REPORTS" ] || REPORTS="${LOG_OUTPUT}/reports/"
379 [ -n "$NETBOOT" ] || NETBOOT="${OUTPUT}/netboot/"
382 # some misc checks before executing FAI {{{
383 [ -n "$CLASSES" ] || bailout 1 "Error: \$CLASSES unset, please set it in $LIVE_CONF or
384 specify it on the command line using the -c option."
385 [ -n "$OUTPUT" ] || bailout 1 "Error: \$OUTPUT unset, please set it in $LIVE_CONF or
386 specify it on the command line using the -o option."
388 # trim characters that are known to cause problems inside $GRML_NAME;
389 # for example isolinux does not like '-' inside the directory name
390 [ -n "$GRML_NAME" ] && export SHORT_NAME="$(echo $GRML_NAME | tr -d ',./;\- ')"
392 # export variables to have them available in fai scripts:
393 [ -n "$GRML_NAME" ] && export GRML_NAME="$GRML_NAME"
394 [ -n "$RELEASENAME" ] && export RELEASENAME="$RELEASENAME"
397 # ZERO_LOGFILE - check for backwards compatibility reasons {{{
398 # this was default behaviour until grml-live 0.9.34:
399 if [ -n "$ZERO_LOGFILE" ] ; then
400 PRESERVE_LOGFILE='' # make sure it's cleaned then
401 ewarn "Please consider disabling the \$ZERO_LOGFILE option as grml-live clears..."
402 ewarn "... the logfile $LOGFILE by default (unless \$PRESERVE_LOGFILE is set) nowadays."
407 # ask user whether the setup is ok {{{
408 if [ -z "$FORCE" ] ; then
410 echo "${PN} [${GRML_LIVE_VERSION}]: check your configuration (or use -F to force execution):"
412 echo " FAI classes: $CLASSES"
413 [ -n "$LOCAL_CONFIG" ] && echo " Configuration: $LOCAL_CONFIG"
414 [ -n "$GRML_FAI_CONFIG" ] && echo " Config directory: $GRML_FAI_CONFIG"
415 echo " main directory: $OUTPUT"
416 [ -n "$EXTRACT_ISO_NAME" ] && echo " Extract ISO: $EXTRACT_ISO_NAME"
417 [ -n "$CHROOT_OUTPUT" ] && echo " Chroot target: $CHROOT_OUTPUT"
418 [ -n "$BUILD_OUTPUT" ] && echo " Build target: $BUILD_OUTPUT"
419 [ -n "$ISO_OUTPUT" ] && echo " ISO target: $ISO_OUTPUT"
420 [ -n "$GRML_NAME" ] && echo " Grml name: $GRML_NAME"
421 [ -n "$RELEASENAME" ] && echo " Release name: $RELEASENAME"
422 [ -n "$DATE" ] && echo " Build date: $DATE"
423 [ -n "$VERSION" ] && echo " Grml version: $VERSION"
424 [ -n "$SUITE" ] && echo " Debian suite: $SUITE"
425 [ -n "$ARCH" ] && echo " Architecture: $ARCH"
426 [ -n "$BOOT_METHOD" ] && echo " Boot method: $BOOT_METHOD"
427 [ -n "$HYBRID_METHOD" ] && echo " Hybrid method: $HYBRID_METHOD"
428 [ -n "$TEMPLATE_DIRECTORY" ] && echo " Template files: $TEMPLATE_DIRECTORY"
429 [ -n "$CHROOT_INSTALL" ] && echo " Install files from directory to chroot: $CHROOT_INSTALL"
430 [ -n "$BOOTID" ] && echo " Boot identifier: $BOOTID"
431 [ -n "$NO_BOOTID" ] && echo " Skipping bootid feature."
432 [ -n "$CHOWN_USER" ] && echo " Output owner: $CHOWN_USER"
433 [ -n "$DEFAULT_BOOTOPTIONS" ] && echo " Adding default bootoptions: \"$DEFAULT_BOOTOPTIONS\""
434 [ -n "$FAI_ARGS" ] && echo " Additional arguments for FAI: $FAI_ARGS"
435 [ -n "$LOGFILE" ] && echo " Logging to file: $LOGFILE"
436 [ -n "$SQUASHFS_ZLIB" ] && echo " Using ZLIB (instead of LZMA/XZ) compression."
437 [ -n "$SQUASHFS_OPTIONS" ] && echo " Using SQUASHFS_OPTIONS ${SQUASHFS_OPTIONS}"
438 [ -n "$VERBOSE" ] && echo " Using VERBOSE mode."
439 [ -n "$CLEAN_ARTIFACTS" ] && echo " Will clean output before and after running."
440 [ -n "$UPDATE" ] && echo " Executing UPDATE instead of fresh installation."
441 if [ -n "$BOOTSTRAP_ONLY" ] ; then
442 echo " Bootstrapping only and not building (files for) ISO."
444 [ -n "$SKIP_MKSQUASHFS" ] && echo " Skipping creation of SQUASHFS file."
445 [ -n "$SKIP_NETBOOT" ] && echo " Skipping creation of NETBOOT package."
446 [ -n "$SKIP_MKISOFS" ] && echo " Skipping creation of ISO file."
447 [ -n "$BUILD_ONLY" ] && echo " Executing BUILD_ONLY instead of fresh installation or UPDATE."
448 [ -n "$BUILD_DIRTY" ] && echo " Executing BUILD_DIRTY to leave chroot untouched."
451 echo -n "Is this ok for you? [y/N] "
453 if ! [ "$a" = 'y' -o "$a" = 'Y' ] ; then
454 bailout 1 "Exiting as requested."
460 # clean up before start {{{
461 if [ -n "${CLEAN_ARTIFACTS}" ]; then
462 echo "Wiping old artifacts"
463 [ -n "${CHROOT_OUTPUT}" -a -d "${CHROOT_OUTPUT}" ] && rm -r "${CHROOT_OUTPUT}"
464 [ -n "${BUILD_OUTPUT}" -a -d "${BUILD_OUTPUT}" ] && rm -r "${BUILD_OUTPUT}"
465 [ -n "${ISO_OUTPUT}" -a -d "${ISO_OUTPUT}" ] && rm -r "${ISO_OUTPUT}"
466 [ -n "${LOG_OUTPUT}" -a -d "${LOG_OUTPUT}" ] && rm -r "${LOG_OUTPUT}"
467 [ -n "${NETBOOT}" -a -d "${NETBOOT}" ] && rm -r "${NETBOOT}"
471 # create log file {{{
472 [ -n "$LOGFILE" ] || LOGFILE=${LOG_OUTPUT}/grml-live.log
473 mkdir -p $(dirname "${LOGFILE}")
475 chown root:adm $LOGFILE
479 # clean/zero/remove logfiles {{{
481 if [ -n "$PRESERVE_LOGFILE" ] ; then
482 echo "Preserving logfile $LOGFILE as requested via \$PRESERVE_LOGFILE"
484 # make sure it is empty (as it is e.g. appended to grml-live-db)
488 if [ -n "$ZERO_FAI_LOGFILE" ] ; then
489 if [ -d /var/log/fai/"$HOSTNAME" ] ; then
490 rm -rf /var/log/fai/"$HOSTNAME"/"$(readlink /var/log/fai/"$HOSTNAME"/last)"
491 rm -rf /var/log/fai/"$HOSTNAME"/"$(readlink /var/log/fai/"$HOSTNAME"/last-dirinstall)"
492 rm -rf /var/log/fai/"$HOSTNAME"/"$(readlink /var/log/fai/"$HOSTNAME"/last-softupdate)"
493 rm -f /var/log/fai/"$HOSTNAME"/last \
494 /var/log/fai/"$HOSTNAME"/last-dirinstall \
495 /var/log/fai/"$HOSTNAME"/last-softupdate
500 # source config and startup {{{
501 if [ -n "$CONFIG" ] ; then
502 if ! [ -f "$CONFIG" ] ; then
503 log "Error: $CONFIG could not be read. Exiting. [$(date)]"
504 eerror "Error: $CONFIG could not be read. Exiting." ; eend 1
507 log "Sourcing $CONFIG"
512 start_seconds=$(cut -d . -f 1 /proc/uptime)
513 log "------------------------------------------------------------------------------"
514 log "Starting grml-live [${GRML_LIVE_VERSION}] run on $(date)"
515 log "Using local config file: $LOCAL_CONFIG"
516 log "Executed grml-live command line:"
519 einfo "Logging actions to logfile $LOGFILE"
522 # dump config variables into file, for script access {{{
525 '^(GRML_NAME|RELEASENAME|DATE|VERSION|SUITE|ARCH|DISTRI_NAME|USERNAME|HOSTNAME|APT_PROXY)=' \
529 # unpack iso/squashfs {{{
531 if [ -n "$EXTRACT_ISO_NAME" ]; then
532 log "Unpacking ISO from ${EXTRACT_ISO_NAME}"
533 einfo "Unpacking ISO from ${EXTRACT_ISO_NAME}"
534 local mountpoint=$(mktemp -d)
536 mount -o loop "${EXTRACT_ISO_NAME}" "$mountpoint" ; rc=$?
537 if [ "$rc" != 0 ]; then
540 eerror "mount failed"
544 unsquashfs -d "${CHROOT_OUTPUT}" "${mountpoint}"/live/*.squashfs ; rc=$?
547 if [ "$rc" != 0 ]; then
548 log "unsquashfs failed"
549 eerror "unsquashfs failed"
558 # on-the-fly configuration {{{
559 if [ -n "$FAI_DEBOOTSTRAP" ] ; then
560 sed "s#^FAI_DEBOOTSTRAP=.*#FAI_DEBOOTSTRAP=\"$FAI_DEBOOTSTRAP\"#" "$NFSROOT_CONF" | sponge "$NFSROOT_CONF"
563 # does this suck? YES!
564 # /usr/share/debootstrap/scripts/unstable does not exist, instead use 'sid':
566 unstable) SUITE='sid' ; CLASSES="DEBIAN_UNSTABLE,$CLASSES" ;;
567 *) CLASSES="DEBIAN_$(echo $SUITE | tr 'a-z' 'A-Z'),$CLASSES";;
569 export SUITE # make sure it's available in FAI scripts
571 for file in "$LIVE_CONF" "$LOCAL_CONFIG" "$NFSROOT_CONF" ; do
572 if [ -n "$file" ] ; then
573 sed "s|^FAI_DEBOOTSTRAP=\"[a-z]* |FAI_DEBOOTSTRAP=\"$SUITE |" "$file" | sponge "$file"
577 # validate whether the specified architecture class matches the
578 # architecture (option), otherwise installation of kernel will fail
579 if echo $CLASSES | grep -qi i386 ; then
580 if ! [[ "$ARCH" == "i386" ]] ; then
581 log "Error: You specified the I386 class but are trying to build something else (AMD64?)."
582 eerror "Error: You specified the I386 class but are trying to build something else (AMD64?)."
583 eerror "Tip: Either invoke grml-live with '-a i386' or adjust the architecture class. Exiting."
587 elif echo $CLASSES | grep -qi amd64 ; then
588 if ! [[ "$ARCH" == "amd64" ]] ; then
589 log "Error: You specified the AMD64 class but are trying to build something else (I386?)."
590 eerror "Error: You specified the AMD64 class but are trying to build something else (I386?)."
591 eerror "Tip: Either invoke grml-live with '-a amd64' or adjust the architecture class. Exiting."
597 if grep -q -- 'FAI_DEBOOTSTRAP_OPTS.*--arch' "$NFSROOT_CONF" ; then
598 sed "s/--arch [a-z0-9]* /--arch $ARCH /" "$NFSROOT_CONF" | sponge "$NFSROOT_CONF"
600 sed "s|^FAI_DEBOOTSTRAP_OPTS=\"\(.*\)|FAI_DEBOOTSTRAP_OPTS=\"--arch $ARCH \1|" "$NFSROOT_CONF" | sponge "$NFSROOT_CONF"
604 # CHROOT_OUTPUT - execute FAI {{{
605 if [ -n "$BUILD_DIRTY" ]; then
606 log "Skipping stage 'fai' as requested via option -B"
607 ewarn "Skipping stage 'fai' as requested via option -B" ; eend 0
609 [ -n "$CHROOT_OUTPUT" ] || CHROOT_OUTPUT="$OUTPUT/grml_chroot"
611 # provide inform fai about the ISO we build
612 [ -d "$CHROOT_OUTPUT/etc/" ] || mkdir -p "$CHROOT_OUTPUT/etc/"
613 echo '# This file has been generated by grml-live.' > "$CHROOT_OUTPUT/etc/grml_live_version"
614 [ -n "$GRML_LIVE_VERSION" ] && echo "GRML_LIVE_VERSION=$GRML_LIVE_VERSION" >> "$CHROOT_OUTPUT/etc/grml_live_version"
615 [ -n "$SUITE" ] && echo "SUITE=$SUITE" >> "$CHROOT_OUTPUT/etc/grml_live_version"
617 if [ -n "$UPDATE" -o -n "$BUILD_ONLY" ] ; then
618 FAI_ACTION=softupdate
620 FAI_ACTION=dirinstall
623 if [ -n "$UPDATE" -o -n "$BUILD_ONLY" ] ; then
624 if ! [ -r "$CHROOT_OUTPUT/etc/debian_version" ] ; then
625 log "Error: does not look like you have a working chroot. Updating/building not possible."
626 eerror "Error: does not look like you have a working chroot. Updating/building not possible. (Drop -u/-b option?)"
632 if [ -d "$CHROOT_OUTPUT/bin" -a -z "$UPDATE" -a -z "$BUILD_ONLY" ] ; then
633 log "Skipping stage 'fai dirinstall' as $CHROOT_OUTPUT exists already."
634 ewarn "Skipping stage 'fai dirinstall' as $CHROOT_OUTPUT exists already." ; eend 0
636 mkdir -p "$CHROOT_OUTPUT" || bailout 5 "Problem with creating $CHROOT_OUTPUT for FAI"
638 if [ -n "${MIRROR_DIRECTORY}" ] ; then
639 mkdir -p "${CHROOT_OUTPUT}/${MIRROR_DIRECTORY}"
640 mount --bind "${MIRROR_DIRECTORY}" "${CHROOT_OUTPUT}/${MIRROR_DIRECTORY}"
643 mkdir -p "${OUTPUT}/grml_sources/" "${CHROOT_OUTPUT}/grml-live/sources/"
644 mount --bind "${OUTPUT}/grml_sources/" "${CHROOT_OUTPUT}/grml-live/sources/"
646 # tell dpkg to use "unsafe io" during the build
647 [ -d "$CHROOT_OUTPUT/etc/dpkg/dpkg.cfg.d" ] || mkdir -p "$CHROOT_OUTPUT/etc/dpkg/dpkg.cfg.d"
648 echo force-unsafe-io > "$CHROOT_OUTPUT/etc/dpkg/dpkg.cfg.d/unsafe-io"
650 log "Executed FAI command line:"
651 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"
652 BUILD_ONLY="$BUILD_ONLY" BOOTSTRAP_ONLY="$BOOTSTRAP_ONLY" GRML_LIVE_CONFIG="$CONFIGDUMP" fai $VERBOSE \
653 -C "$GRML_FAI_CONFIG" -s "file:///$GRML_FAI_CONFIG/config" -c"$CLASSES" \
654 -u "$HOSTNAME" "$FAI_ACTION" "$CHROOT_OUTPUT" $FAI_ARGS | tee -a $LOGFILE
655 RC="$PIPESTATUS" # notice: bash-only
657 rm -f "$CHROOT_OUTPUT/etc/dpkg/dpkg.cfg.d/unsafe-io"
659 FORCE_ISO_REBUILD=true
661 if [ "$RC" != 0 ] ; then
662 log "Error: critical error while executing fai [exit code ${RC}]. Exiting."
663 eerror "Error: critical error while executing fai [exit code ${RC}]. Exiting." ; eend 1
667 # move fai logs into grml_logs directory
668 mkdir -p "$LOG_OUTPUT"/fai/
669 cp -r "$CHROOT_OUTPUT"/var/log/fai/"$HOSTNAME"/last/* "$LOG_OUTPUT"/fai/
670 rm -rf "$CHROOT_OUTPUT"/var/log/fai
671 # copy fai package list
672 cp "$CHROOT_OUTPUT"/var/log/install_packages.list "$LOG_OUTPUT"/fai/
674 chown root:adm "$LOG_OUTPUT"/fai/*
675 chmod 664 "$LOG_OUTPUT"/fai/*
679 # notice: 'fai dirinstall' does not seem to exit appropriate, so:
681 CHECKLOG="$LOG_OUTPUT"/fai/
682 if [ -r "$CHECKLOG/software.log" ] ; then
683 # 1 errors during executing of commands
684 grep 'dpkg: error processing' $CHECKLOG/software.log >> $LOGFILE && ERROR=1
685 grep 'E: Method http has died unexpectedly!' $CHECKLOG/software.log >> $LOGFILE && ERROR=2
686 grep 'ERROR: chroot' $CHECKLOG/software.log >> $LOGFILE && ERROR=3
687 grep 'E: Failed to fetch' $CHECKLOG/software.log >> $LOGFILE && ERROR=4
688 grep 'Unable to write mmap - msync (28 No space left on device)' $CHECKLOG/software.log >> $LOGFILE && ERROR=5
691 if [ -r "$CHECKLOG/shell.log" ] ; then
692 grep 'FAILED with exit code' $CHECKLOG/shell.log >> $LOGFILE && ERROR=6
695 if [ -n "$ERROR" ] ; then
696 log "Error: there was a critical error [${ERROR}] during execution of stage 'fai dirinstall' [$(date)]"
697 eerror "Error: there was a critical error during execution of stage 'fai dirinstall'"
698 eerror "Note: check out ${CHECKLOG}/ for details. [exit ${ERROR}]"
702 log "Finished execution of stage 'fai dirinstall' [$(date)]"
703 einfo "Finished execution of stage 'fai dirinstall'"
709 # package validator {{{
710 CHECKLOG=/var/log/fai/$HOSTNAME/last
711 if [ -r "$CHECKLOG/dpkg.selections" ] ; then
712 package_count=$(wc -l "$CHECKLOG/dpkg.selections" | awk '{print $1}')
714 package_count="unknown"
718 REPORT_MISSING_PACKAGES="${REPORTS}/TEST-MissingPackages.xml"
720 # check for missing packages
721 if ! [ -s "$CHECKLOG/package_errors.log" ] ; then
722 einfo "No missing packages found, generating empty junit report."
724 cat > "${REPORT_MISSING_PACKAGES}" << EOF
725 <?xml version="1.0" encoding="UTF-8"?>
726 <testsuite name="grml-live-missing-packages" tests="${package_count}" time="1" failures="0" errors="0" skipped="0" assertions="0">
727 <testcase name="test_missing_packages" time="0" assertions="0">
737 einfo "Missing packages found, generating junit report."
739 if [ -r "$CHECKLOG/package_errors.log" ] ; then
740 package_errors=$(wc -l "$CHECKLOG/package_errors.log" | awk '{print $1}')
742 package_errors="unknown"
746 REPORT_MISSING_PACKAGES="${REPORTS}/TEST-MissingPackages.xml"
748 cat > "${REPORT_MISSING_PACKAGES}" << EOF
749 <?xml version="1.0" encoding="UTF-8"?>
750 <testsuite name="grml-live-missing-packages" tests="${package_count}" time="1" failures="${package_errors}" errors="${package_errors}" skipped="0" assertions="0">
753 for package in $(awk '{print $5}' "${CHECKLOG}/package_errors.log" | sed 's/\.$//') ; do
754 cat >> "${REPORT_MISSING_PACKAGES}" << EOF
755 <testcase name="test_missing_packages_${package}" time="0" assertions="0">
756 <failure type="RuntimeError" message="Package ${package} is missing">
757 Package $package is missing in chroot
763 cat >> "${REPORT_MISSING_PACKAGES}" << EOF
772 if [ -n "$EXIT_ON_MISSING_PACKAGES" -a -z "$BUILD_DIRTY" ] ; then
773 eerror "The following packages were requested for installation but could not be processed:"
774 cat "$CHECKLOG/package_errors.log"
775 eerror "... exiting as requested via \$EXIT_ON_MISSING_PACKAGES."
779 ewarn "The following packages were requested for installation but could not be processed:"
780 cat "$CHECKLOG/package_errors.log"
786 # BUILD_OUTPUT - execute arch specific stuff and squashfs {{{
787 [ -n "$BUILD_OUTPUT" ] || BUILD_OUTPUT="$OUTPUT/grml_cd"
788 mkdir -p "$BUILD_OUTPUT" || bailout 6 "Problem with creating $BUILD_OUTPUT for stage ARCH"
791 if [ "$ARCH" = i386 ] || [ "$ARCH" = amd64 ] ; then
792 if [ -n "$BOOTSTRAP_ONLY" ] ; then
793 log "Skipping stage 'boot' as building with bootstrap only."
794 ewarn "Skipping stage 'boot' as building with bootstrap only." ; eend 0
796 if [ -d "$BUILD_OUTPUT"/boot/isolinux -a -z "$UPDATE" -a -z "$BUILD_ONLY" ] ; then
797 log "Skipping stage 'boot' as $BUILD_OUTPUT/boot/isolinux exists already."
798 ewarn "Skipping stage 'boot' as $BUILD_OUTPUT/boot/isolinux exists already." ; eend 0
801 [ -d "$BUILD_OUTPUT"/boot/isolinux ] || mkdir -p "$BUILD_OUTPUT"/boot/isolinux
802 [ -d "$BUILD_OUTPUT"/boot/"${SHORT_NAME}" ] || mkdir -p "$BUILD_OUTPUT"/boot/"${SHORT_NAME}"
804 # if we don't have an initrd we a) can't boot and b) there was an error
805 # during build, so check for the file:
806 INITRD="$(ls $CHROOT_OUTPUT/boot/initrd* 2>/dev/null| grep -v '.bak$' | sort -r | head -1)"
807 if [ -n "$INITRD" ] ; then
808 cp $INITRD "$BUILD_OUTPUT"/boot/"${SHORT_NAME}"/initrd.gz
809 find $CHROOT_OUTPUT/boot/ -name initrd\*.bak -exec rm {} \;
811 log "Error: No initrd found inside $CHROOT_OUTPUT/boot/ - Exiting"
812 eerror "Error: No initrd found inside $CHROOT_OUTPUT/boot/ - Exiting" ; eend 1
816 KERNEL_IMAGE="$(ls $CHROOT_OUTPUT/boot/vmlinuz* 2>/dev/null | sort -r | head -1)"
817 if [ -n "$KERNEL_IMAGE" ] ; then
818 cp "$KERNEL_IMAGE" "$BUILD_OUTPUT"/boot/"${SHORT_NAME}"/linux26
820 log "Error: No kernel found inside $CHROOT_OUTPUT/boot/ - Exiting"
821 eerror "Error: No kernel found inside $CHROOT_OUTPUT/boot/ - Exiting" ; eend 1
825 [ -n "$TEMPLATE_DIRECTORY" ] || TEMPLATE_DIRECTORY='/usr/share/grml-live/templates'
826 if ! [ -d "${TEMPLATE_DIRECTORY}"/boot ] ; then
827 log "Error: ${TEMPLATE_DIRECTORY}/boot does not exist. Exiting."
828 eerror "Error: ${TEMPLATE_DIRECTORY}/boot does not exist. Exiting." ; eend 1
832 # copy _required_ isolinux files
833 for file in ifcpu64.c32 isolinux.bin vesamenu.c32; do
834 copy_addon_file "${file}" /usr/lib/syslinux isolinux
837 # *always* copy files to output directory so the variables
838 # get adjusted according to the build.
839 cp ${TEMPLATE_DIRECTORY}/boot/isolinux/* "$BUILD_OUTPUT"/boot/isolinux/
841 if [ -n "$NO_ADDONS" ] ; then
842 log "Skipping installation of boot addons as requested via \$NO_ADDONS."
843 einfo "Skipping installation of boot addons as requested via \$NO_ADDONS."; eend 0
845 if ! [ -d "$TEMPLATE_DIRECTORY"/boot/addons ] ; then
846 log "Boot addons not found, skipping therefore. (Consider installing package grml-live-addons)"
847 ewarn "Boot addons not found, skipping therefore. (Consider installing package grml-live-addons)" ; eend 0
849 # copy addons from system packages or grml-live-compat
850 copy_addon_file ipxe.lkrn /usr/lib/ipxe addons
851 copy_addon_file pci.ids /usr/share/misc addons
852 copy_addon_file memtest86+.bin /boot addons
853 for file in memdisk chain.c32 hdt.c32 menu.c32; do
854 copy_addon_file "${file}" /usr/lib/syslinux addons
857 # make memtest filename FAT16/8.3 compatible
858 mv "${BUILD_OUTPUT}/boot/addons/memtest86+.bin" \
859 "${BUILD_OUTPUT}/boot/addons/memtest"
861 # copy only files so we can handle bsd4grml on its own
862 for file in ${TEMPLATE_DIRECTORY}/boot/addons/* ; do
863 test -f $file && cp $file "$BUILD_OUTPUT"/boot/addons/
866 if [ -n "$NO_ADDONS_BSD4GRML" ] ; then
867 log "Skipping installation of bsd4grml as requested via \$NO_ADDONS_BSD4GRML."
868 einfo "Skipping installation of bsd4grml as requested via \$NO_ADDONS_BSD4GRML."; eend 0
870 if [ -d "$TEMPLATE_DIRECTORY"/boot/addons/bsd4grml ] ; then
871 cp -a ${TEMPLATE_DIRECTORY}/boot/addons/bsd4grml "$BUILD_OUTPUT"/boot/addons/
873 log "Missing addon file: bsd4grml"
874 ewarn "Missing addon file: bsd4grml" ; eend 0
878 fi # no "$TEMPLATE_DIRECTORY"/boot/addons
881 if ! [ -d "${BUILD_OUTPUT}/boot/grub" ] ; then
882 mkdir -p "${BUILD_OUTPUT}/boot/grub"
884 cp ${TEMPLATE_DIRECTORY}/boot/grub/* "$BUILD_OUTPUT"/boot/grub/
886 # copy grub files from target
887 cp -a "${CHROOT_OUTPUT}"/usr/lib/grub/*-pc/*.mod "${BUILD_OUTPUT}"/boot/grub/
888 cp -a "${CHROOT_OUTPUT}"/usr/lib/grub/*-pc/*.o "${BUILD_OUTPUT}"/boot/grub/
889 cp -a "${CHROOT_OUTPUT}"/usr/lib/grub/*-pc/*.lst "${BUILD_OUTPUT}"/boot/grub/
890 cp -a "${CHROOT_OUTPUT}"/usr/share/grub/ascii.pf2 "${BUILD_OUTPUT}"/boot/grub/
891 cp -a "${CHROOT_OUTPUT}"/boot/grub/core.img "${BUILD_OUTPUT}"/boot/grub/
893 if ! [ -d "${TEMPLATE_DIRECTORY}"/GRML ] ; then
894 log "Error: ${TEMPLATE_DIRECTORY}/GRML does not exist. Exiting."
895 eerror "Error: ${TEMPLATE_DIRECTORY}/GRML does not exist. Exiting." ; eend 1
899 [ -d "$BUILD_OUTPUT"/GRML ] || mkdir "$BUILD_OUTPUT"/GRML
900 cp -a ${TEMPLATE_DIRECTORY}/GRML/* "$BUILD_OUTPUT"/GRML/
902 # adjust boot splash information:
903 RELEASE_INFO="$GRML_NAME $VERSION - Release Codename $RELEASENAME"
904 RELEASE_INFO="$(cut_string 68 "$RELEASE_INFO")"
905 RELEASE_INFO="$(extend_string_end 68 "$RELEASE_INFO")"
907 if [ -r "$BUILD_OUTPUT"/GRML/grml-version ] ; then
908 sed -i "s/%RELEASE_INFO%/$GRML_NAME $VERSION - $RELEASENAME/" "$BUILD_OUTPUT"/GRML/grml-version
909 sed -i "s/%DATE%/$DATE/" "$BUILD_OUTPUT"/GRML/grml-version
912 # make sure the squashfs filename is set accordingly:
913 SQUASHFS_NAME="$GRML_NAME.squashfs"
915 if [ -n "$NO_BOOTID" ] ; then
916 log 'Skipping bootid feature as requested via $NO_BOOTID.'
917 einfo 'Skipping bootid feature as requested via $NO_BOOTID.'
919 [ -n "$BOOTID" ] || BOOTID="$(echo ${GRML_NAME}${VERSION} | tr -d ',./;\- ')"
920 [ -d "$BUILD_OUTPUT"/conf ] || mkdir "$BUILD_OUTPUT"/conf
921 einfo "Generating /conf/bootid.txt with entry ${BOOTID}."
922 log "Generating /conf/bootid.txt with entry ${BOOTID}."
923 echo "$BOOTID" > "$BUILD_OUTPUT"/conf/bootid.txt
927 # adjust all variables in the templates with the according distribution information
928 for file in "${BUILD_OUTPUT}"/boot/isolinux/*.cfg "${BUILD_OUTPUT}"/boot/isolinux/*.msg \
929 "${BUILD_OUTPUT}"/boot/grub/* ; do
930 if [ -r "${file}" ] ; then
931 sed -i "s/%ARCH%/$ARCH/g" "${file}"
932 sed -i "s/%DATE%/$DATE/g" "${file}"
933 sed -i "s/%DISTRI_INFO%/$DISTRI_INFO/g" "${file}"
934 sed -i "s/%DISTRI_NAME%/$DISTRI_NAME/g" "${file}"
935 sed -i "s/%DISTRI_SPLASH%/$DISTRI_SPLASH/g" "${file}"
936 sed -i "s/%GRML_NAME%/$GRML_NAME/g" "${file}"
937 sed -i "s/%SQUASHFS_NAME%/$SQUASHFS_NAME/g" "${file}"
938 sed -i "s/%RELEASE_INFO%/$RELEASE_INFO/g" "${file}"
939 sed -i "s/%SHORT_NAME%/$SHORT_NAME/g" "${file}"
940 sed -i "s/%VERSION%/$VERSION/g" "${file}"
942 [ -n "$DEFAULT_BOOTOPTIONS" ] && sed -i "s/ boot=live/ boot=live $DEFAULT_BOOTOPTIONS/" "${file}"
944 if [ -n "$NO_BOOTID" ] ; then
945 sed -i "s/ bootid=%BOOTID%//g" "${file}" # drop bootid bootoption
947 sed -i "s/%BOOTID%/$BOOTID/g" "${file}" # adjust bootid=... argument
952 # adjust bootsplash accordingly but make sure the string has the according lenght
953 SQUASHFS_NAME="$(cut_string 20 "$SQUASHFS_NAME")"
954 SQUASHFS_NAME="$(extend_string_end 20 "$SQUASHFS_NAME")"
955 for file in f4 f5 ; do
956 if [ -r "${BUILD_OUTPUT}/boot/isolinux/${file}" ] ; then
957 sed -i "s/%SQUASHFS_NAME%/$SQUASHFS_NAME/" "${BUILD_OUTPUT}/boot/isolinux/${file}"
958 sed -i "s/%SQUASHFS_NAME%/$SQUASHFS_NAME/" "${BUILD_OUTPUT}/boot/isolinux/${file}"
962 # generate addon list
963 rm -f "${BUILD_OUTPUT}/${ADDONS_LIST_FILE}"
964 for name in "${BUILD_OUTPUT}"/boot/isolinux/addon_*.cfg ; do
965 include_name=$(basename "$name")
966 echo "include $include_name" >> "${BUILD_OUTPUT}/${ADDONS_LIST_FILE}"
969 if ! [ -r "${BUILD_OUTPUT}/boot/isolinux/${DISTRI_NAME}.cfg" ] || [ "$DISTRI_NAME" = "grml" ] ; then
970 log "including grmlmain.cfg in ${BUILD_OUTPUT}/boot/isolinux/distri.cfg"
971 echo "include grmlmain.cfg" > "${BUILD_OUTPUT}/boot/isolinux/distri.cfg"
972 echo "include default.cfg" > "${BUILD_OUTPUT}/boot/isolinux/grmlmain.cfg"
973 echo "include menuoptions.cfg" >> "${BUILD_OUTPUT}/boot/isolinux/grmlmain.cfg"
974 echo "include grml.cfg" >> "${BUILD_OUTPUT}/boot/isolinux/grmlmain.cfg"
976 for f in "${BUILD_OUTPUT}"/boot/isolinux/submenu*.cfg ; do
977 echo "include $(basename $f)" >> "${BUILD_OUTPUT}/boot/isolinux/grmlmain.cfg"
980 echo "include options.cfg" >> "${BUILD_OUTPUT}/boot/isolinux/grmlmain.cfg"
981 if [ ! -n "$NO_ADDONS" ] ; then
982 echo "include addons.cfg" >> "${BUILD_OUTPUT}/boot/isolinux/grmlmain.cfg"
984 echo "include isoprompt.cfg" >> "${BUILD_OUTPUT}/boot/isolinux/grmlmain.cfg"
985 echo "include hd.cfg" >> "${BUILD_OUTPUT}/boot/isolinux/grmlmain.cfg"
986 echo "include hidden.cfg" >> "${BUILD_OUTPUT}/boot/isolinux/grmlmain.cfg"
987 else # assume we are building a custom distribution:
988 log "File ${BUILD_OUTPUT}/boot/isolinux/${DISTRI_NAME}.cfg found, using it."
989 einfo "File ${BUILD_OUTPUT}/boot/isolinux/${DISTRI_NAME}.cfg found, using it."
990 if grep -q "^include ${DISTRI_NAME}.cfg" "${BUILD_OUTPUT}/boot/isolinux/distri.cfg" ; then
991 log "include for ${DISTRI_NAME}.cfg already present, nothing to do."
993 einfo "include for ${DISTRI_NAME}.cfg already present, nothing to do."
997 log "including ${DISTRI_NAME}.cfg in ${BUILD_OUTPUT}/boot/isolinux/distri.cfg"
998 echo "include ${DISTRI_NAME}.cfg" > "${BUILD_OUTPUT}/boot/isolinux/distri.cfg"
999 [ -n "$NO_ADDONS" ] || echo "include addons.cfg" >> "${BUILD_OUTPUT}/boot/isolinux/distri.cfg"
1003 # use old style console based isolinux method only if requested:
1004 if [[ "${ISOLINUX_METHOD}" == "console" ]] ; then
1005 log 'Using console based isolinux method as requested via $ISOLINUX_METHOD.'
1006 einfo 'Using console based isolinux method as requested via $ISOLINUX_METHOD.'
1007 if grep -q '^include console.cfg' "${BUILD_OUTPUT}/boot/isolinux/distri.cfg" ; then
1008 einfo "include for console.cfg already found, nothing to do."
1011 log "including console.cfg in ${BUILD_OUTPUT}/boot/isolinux/isolinux.cfg"
1012 einfo "including console.cfg in ${BUILD_OUTPUT}/boot/isolinux/isolinux.cfg"
1013 echo "include console.cfg" >> "${BUILD_OUTPUT}/boot/isolinux/isolinux.cfg"
1017 log 'Using graphical boot menu.'
1018 if grep -q '^include vesamenu.cfg' "${BUILD_OUTPUT}/boot/isolinux/isolinux.cfg" ; then
1019 log "include for vesamenu.cfg already found, nothing to do."
1021 log "including vesamenu.cfg in ${BUILD_OUTPUT}/boot/isolinux/isolinux.cfg"
1022 echo "include vesamenu.cfg" >> "${BUILD_OUTPUT}/boot/isolinux/isolinux.cfg"
1026 if [ -e "$BUILD_OUTPUT"/boot/addons/bsd4grml/boot.6 ]; then
1027 sed -i "s/%RELEASE_INFO%/$GRML_NAME $VERSION - $RELEASENAME/" "$BUILD_OUTPUT"/boot/addons/bsd4grml/boot.6
1030 DPKG_LIST="/var/log/fai/$HOSTNAME/last/dpkg.list" # the dpkg --list output of the chroot
1031 if ! [ -r "$DPKG_LIST" ] ; then
1032 ewarn "$DPKG_LIST could not be read, ignoring to store package information on ISO therefore."
1034 einfo "Storing package list information as /GRML/${GRML_NAME}-packages.txt on ISO."
1035 cp "$DPKG_LIST" "${BUILD_OUTPUT}/GRML/${GRML_NAME}-packages.txt"
1039 # autostart for Windows:
1040 if [ -d "${TEMPLATE_DIRECTORY}/windows/autostart/" ] ; then
1041 cp ${TEMPLATE_DIRECTORY}/windows/autostart/* "$BUILD_OUTPUT"/
1044 FORCE_ISO_REBUILD=true
1045 einfo "Finished execution of stage 'boot'" ; eend 0
1049 log 'Error: Unsupported ARCH, sorry. Want to support it? Contribute!'
1050 eerror 'Error: Unsupported ARCH, sorry. Want to support it? Contribute!' ; eend 1
1054 # support installation of local files into the chroot/ISO
1055 if [ -n "$CHROOT_INSTALL" ] ; then
1056 if ! [ -d "$CHROOT_INSTALL" ] ; then
1057 log "Configuration variable \$CHROOT_INSTALL is set but not a directory; ignoring"
1058 ewarn "Configuration variable \$CHROOT_INSTALL is set but not a directory; ignoring"
1060 log "Copying local files to chroot as requested via \$CHROOT_INSTALL"
1061 einfo "Copying local files to chroot as requested via \$CHROOT_INSTALL"
1062 rsync -avz --inplace "$CHROOT_INSTALL"/ "$CHROOT_OUTPUT/"
1064 einfo "Make sure to run squashfs stage, otherwise your local files won't be part of the ISO."
1065 FORCE_ISO_REBUILD=true
1069 if [ -f "$BUILD_OUTPUT"/live/${GRML_NAME}.squashfs -a -z "$UPDATE" -a -z "$BUILD_ONLY" -a -z "$BUILD_DIRTY" ] ; then
1070 log "Skipping stage 'squashfs' as $BUILD_OUTPUT/live exists already."
1071 ewarn "Skipping stage 'squashfs' as $BUILD_OUTPUT/live exists already." ; eend 0
1072 elif [ -n "$SKIP_MKSQUASHFS" ] ; then
1073 log "Skipping stage 'squashfs' as requested via option -q or -N"
1074 ewarn "Skipping stage 'squashfs' as requested via option -q or -N" ; eend 0
1076 [ -d "$BUILD_OUTPUT"/live ] || mkdir "$BUILD_OUTPUT"/live
1077 # make sure we don't leave (even an empty) base.tgz:
1078 [ -f "$CHROOT_OUTPUT/base.tgz" ] && rm -f "$CHROOT_OUTPUT/base.tgz"
1080 # if unconfigured default to squashfs-tools' mksquashfs binary
1081 if [ -z "$SQUASHFS_BINARY" ] ; then
1082 SQUASHFS_BINARY='mksquashfs'
1085 if which "$SQUASHFS_BINARY" >/dev/null 2>&1 ; then
1086 log "Using mksquashfs binary ${SQUASHFS_BINARY}"
1087 einfo "Using mksquashfs binary ${SQUASHFS_BINARY}" ; eend 0
1089 log "Error: mksquashfs binary ($SQUASHFS_BINARY) not found. Exiting."
1090 eerror "Error: mksquashfs binary ($SQUASHFS_BINARY) not found. Exiting." ; eend 1
1094 # use sane defaults if $SQUASHFS_OPTIONS isn't set
1095 if [ -z "$SQUASHFS_OPTIONS" ] ; then
1096 # use blocksize 256k as this gives best result with regards to time + compression
1097 SQUASHFS_OPTIONS="-b 256k"
1099 # set lzma/xz compression by default, unless -z option has been specified on command line
1100 if [ -z "$SQUASHFS_ZLIB" ] ; then
1101 SQUASHFS_OPTIONS="$SQUASHFS_OPTIONS -comp xz"
1103 SQUASHFS_OPTIONS="$SQUASHFS_OPTIONS -comp gzip"
1107 # support exclusion of files via exclude-file:
1108 if [ -n "$SQUASHFS_EXCLUDES_FILE" -a "$SQUASHFS_EXCLUDES_FILE" ] ; then
1109 SQUASHFS_OPTIONS="$SQUASHFS_OPTIONS -ef $SQUASHFS_EXCLUDES_FILE -wildcards"
1112 # get rid of unnecessary files when building grml-small for final release:
1113 if echo "$CLASSES" | grep -q GRML_SMALL ; then
1114 SQUASHFS_OPTIONS="$SQUASHFS_OPTIONS -e initrd.img* vmlinuz*"
1118 SQUASHFS_STDERR="$(mktemp -t grml-live.XXXXXX)"
1120 # informational stuff
1121 [ -n "$SQUASHFS_OPTIONS" ] && SQUASHFS_INFO_MSG="$SQUASHFS_OPTIONS"
1122 [ -n "$SQUASHFS_INFO_MSG" ] && SQUASHFS_INFO_MSG="using options: $SQUASHFS_INFO_MSG"
1123 einfo "Squashfs build information: running binary $SQUASHFS_BINARY $SQUASHFS_INFO_MSG"
1125 log "$SQUASHFS_BINARY $CHROOT_OUTPUT/ $BUILD_OUTPUT/live/${GRML_NAME}.squashfs -noappend $SQUASHFS_OPTIONS"
1127 if $SQUASHFS_BINARY $CHROOT_OUTPUT/ $BUILD_OUTPUT/live/"${GRML_NAME}".squashfs \
1128 -noappend $SQUASHFS_OPTIONS 2>"${SQUASHFS_STDERR}" ; then
1129 echo "${GRML_NAME}.squashfs" > $BUILD_OUTPUT/live/filesystem.module
1130 log "Finished execution of stage 'squashfs' [$(date)]"
1131 einfo "Finished execution of stage 'squashfs'" ; eend 0
1133 log "Error: there was a critical error executing stage 'squashfs' [$(date)]:"
1134 log "$(cat $SQUASHFS_STDERR)"
1135 eerror "Error: there was a critical error executing stage 'squashfs':"
1136 cat "${SQUASHFS_STDERR}"
1141 FORCE_ISO_REBUILD=true
1144 # create md5sum file:
1145 if [ -z "$BOOTSTRAP_ONLY" ] ; then
1146 ( cd $BUILD_OUTPUT/GRML &&
1147 find .. -type f -not -name md5sums -not -name isolinux.bin -exec md5sum {} \; > md5sums )
1151 # ISO_OUTPUT - mkisofs {{{
1152 [ -n "$ISO_OUTPUT" ] || ISO_OUTPUT="$OUTPUT/grml_isos"
1153 [ -n "$ISO_NAME" ] || ISO_NAME="${GRML_NAME}_${VERSION}.iso"
1155 if [ "$BOOT_METHOD" = "isolinux" ] ; then
1156 BOOT_ARGS="-no-emul-boot -boot-load-size 4 -boot-info-table -b boot/isolinux/isolinux.bin -c boot/isolinux/boot.cat"
1157 elif [ "$BOOT_METHOD" = "grub2" ] ; then
1158 BOOT_ARGS="-no-emul-boot -boot-load-size 4 -b boot/grub/toriboot.bin"
1161 # Just until http://bts.grml.org/grml/issue945 has been resolved.
1162 # HYBRID_METHOD defaults to manifold, so make sure the default works OOTB.
1163 if [[ $BOOT_METHOD != isolinux && ($HYBRID_METHOD = isohybrid || $HYBRID_METHOD = manifold) ]]; then
1164 log "Setting HYBRID_METHOD to grub2 as hybrid mode does not work with isohybrid yet."
1165 ewarn "Setting HYBRID_METHOD to grub2 as hybrid mode does not work with isohybrid yet."
1166 HYBRID_METHOD='grub2'
1170 if [ -f "${ISO_OUTPUT}/${ISO_NAME}" -a -z "$UPDATE" -a -z "$BUILD_ONLY" -a -z "$BUILD_DIRTY" -a "$FORCE_ISO_REBUILD" = "false" ] ; then
1171 log "Skipping stage 'iso build' as $ISO_OUTPUT/${ISO_NAME} exists already."
1172 ewarn "Skipping stage 'iso build' as $ISO_OUTPUT/${ISO_NAME} exists already." ; eend 0
1173 elif [ -n "$SKIP_MKISOFS" ] ; then
1174 log "Skipping stage 'iso build' as requested via option -n or -N"
1175 ewarn "Skipping stage 'iso build' as requested via option -n or -N" ; eend 0
1177 mkdir -p "$ISO_OUTPUT" || bailout 6 "Problem with creating $ISO_OUTPUT for stage 'iso build'"
1179 if $FORCE_ISO_REBUILD && ! [ -f "${ISO_OUTPUT}/${ISO_NAME}" ] ; then
1180 log "Forcing rebuild of ISO because files on ISO have been modified."
1181 einfo "Forcing rebuild of ISO because files on ISO have been modified."
1184 # support xorriso as well mkisofs and genisoimage
1185 if which xorriso >/dev/null 2>&1 ; then
1186 MKISOFS='xorriso -as mkisofs'
1187 elif which mkisofs >/dev/null 2>&1; then
1189 elif which genisoimage >/dev/null 2>&1; then
1190 MKISOFS='genisoimage'
1192 log "Error: neither xorriso nor mkisofs nor genisoimage available - can not create ISO."
1193 eerror "Error: neither xorriso nor mkisofs nor genisoimage available - can not create ISO." ; eend 1
1199 # using -eltorito-alt-boot is limited to xorriso for now
1202 einfo "Using xorriso for ISO generation." ; eend 0
1205 if ! dpkg --compare-versions $(dpkg-query -W -f='${Version}\n' xorriso 2>/dev/null) gt-nl 1.1.6-1 ; then
1206 log "Disabling (U)EFI boot support since xorriso version is not recent enough."
1207 ewarn "Disabling (U)EFI boot support since xorriso version is not recent enough." ; eend 0
1209 log "xorriso with -eltorito-alt-boot present, enabling (U)EFI boot support."
1210 einfo "xorriso with -eltorito-alt-boot present, enabling (U)EFI boot support." ; eend 0
1212 if [ -r "${CHROOT_OUTPUT}/var/lib/grml_live_efi.img" ] ; then
1213 einfo "Found /var/lib/grml_live_efi.img - moving to /boot/efi.img for ISO."
1214 log "Found /var/lib/grml_live_efi.img - moving to /boot/efi.img for ISO."
1215 mv "${CHROOT_OUTPUT}/var/lib/grml_live_efi.img" "${BUILD_OUTPUT}/boot/efi.img"
1219 if [ -r "${CHROOT_OUTPUT}/var/lib/grml_live_bootx64.efi" ] ; then
1220 einfo "Found /var/lib/grml_live_bootx64.efi - moving to /efi/boot/bootx64.efi for ISO"
1221 log "Found /var/lib/grml_live_bootx64.efi - moving to /efi/boot/bootx64.efi for ISO"
1222 mkdir -p "${BUILD_OUTPUT}/efi/boot/"
1223 mv "${CHROOT_OUTPUT}/var/lib/grml_live_bootx64.efi" "${BUILD_OUTPUT}/efi/boot/bootx64.efi"
1227 if [ -r "${BUILD_OUTPUT}"/boot/efi.img ] ; then
1228 einfo "/boot/efi.img found and amd64 architecture present, extending boot arguments."
1229 log "/boot/efi.img found and amd64 architecture present, extending boot arguments."
1230 BOOT_ARGS="$BOOT_ARGS -boot-info-table -eltorito-alt-boot -e boot/efi.img -no-emul-boot"
1242 if cd "$BUILD_OUTPUT" ; then
1243 if [ "$BOOT_METHOD" = "grub2" ]; then
1244 # make a 2048-byte bootsector for El Torito
1245 dd if=/dev/zero of=boot/grub/toriboot.bin bs=512 count=4 2>/dev/null
1246 # those are in 2048-byte sectors, so 1 16 matches 4 63 below
1247 echo 1 16 | mksh /usr/share/grml-live/scripts/bootgrub.mksh -B 11 | \
1248 dd of=boot/grub/toriboot.bin conv=notrunc 2>/dev/null
1250 log "$MKISOFS -V '${GRML_NAME} ${VERSION}' -publisher 'grml-live | grml.org' -l -r -J $BOOT_ARGS -o ${ISO_OUTPUT}/${ISO_NAME} ."
1251 $MKISOFS -V "${GRML_NAME} ${VERSION}" -publisher 'grml-live | grml.org' \
1252 -l -r -J $BOOT_ARGS -no-pad \
1253 -o "${ISO_OUTPUT}/${ISO_NAME}" . ; RC=$?
1254 # both of these need core.img there, so it’s easier to write it here
1255 if [ "$BOOT_METHOD" = "grub2" ] || [ "$HYBRID_METHOD" = "grub2" ]; then
1256 # must be <= 30720 bytes
1257 dd if=boot/grub/core.img of="${ISO_OUTPUT}/${ISO_NAME}" \
1258 conv=notrunc bs=512 seek=4 2>/dev/null
1261 # pad the output ISO to multiples of 256 KiB for partition table support
1262 siz=$($getfilesize "${ISO_OUTPUT}/${ISO_NAME}")
1263 cyls=$((siz / 512 / 32 / 16 + 1)) # C=$cyls H=16 S=32
1264 siz=$((cyls * 16 * 32 * 512)) # size after padding
1265 dd if=/dev/zero bs=1 count=1 seek=$((siz - 1)) \
1266 of="${ISO_OUTPUT}/${ISO_NAME}" 2>/dev/null
1268 # support disabling hybrid ISO image
1269 if [ "$HYBRID_METHOD" = "disable" ] ; then\
1270 log "Skipping creation of hybrid ISO file as requested via HYBRID_METHOD=disable"
1271 einfo "Skipping creation of hybrid ISO file as requested via HYBRID_METHOD=disable"
1273 elif [ "$HYBRID_METHOD" = "manifold" ] ; then
1274 # isoinfo is part of both mkisofs and genisoimage so we're good
1275 bootoff=$(isoinfo -l -i "${ISO_OUTPUT}/${ISO_NAME}" | \
1276 sed -n '/^.*\[ *\([0-9]*\)[] ].* ISOLINUX.BIN[;1]* *$/s//\1/p')
1277 if ! [ -r boot/grub/core.img ] ; then
1278 ewarn "boot/grub/core.img not found, not creating manifold boot ISO file"
1279 elif [ "${bootoff:-0}" -lt 1 ] ; then
1280 ewarn "isolinux.bin not found on the ISO file, disabling manifold boot"
1282 log "Creating hybrid ISO file with manifold method"
1283 einfo "Creating hybrid ISO file with manifold method"
1284 if [ "$HYBRID_METHOD" = "grub2" ] ; then
1285 # 512 bytes: MBR, partition table, load GRUB 2
1286 echo 4 63 | mksh /usr/share/grml-live/scripts/bootgrub.mksh -A -M 4:0x96 -g $cyls:16:32
1288 # read only one but 2048-byte sized (scale: << 2) sector
1289 echo $bootoff $bootoff | \
1290 mksh /usr/share/grml-live/scripts/bootilnx.mksh -A -M 4:0x96 -g $cyls:16:32 -S 2
1291 fi | dd of="${ISO_OUTPUT}/${ISO_NAME}" conv=notrunc 2>/dev/null
1294 # use isohybrid as default
1296 if ! which isohybrid >/dev/null 2>&1 ; then
1297 bailout 12 "isohybrid binary not found - please install syslinux/syslinux-common"
1299 log "Creating hybrid ISO file with isohybrid method"
1300 einfo "Creating hybrid ISO file with isohybrid method"
1301 # Notes for consideration:
1302 # "-entry 4 -type 1c"
1303 # * using 4 as the partition number is supposed to help with BIOSes
1304 # that only support USB-Zip boot
1305 # * using 1c (i.e. hidden FAT32 LBA), instead of the default 0x17
1306 # (hidden NTFS, IIRC), as the partition type is sometimes needed
1307 # to get the BIOS even look at the partition created by isohybrid
1308 if isohybrid --help | grep -q -- --uefi ; then
1309 einfo "Detected uefi support for isohybrid, enabling."
1310 ISOHYBRID_OPTIONS=--uefi
1313 log "isohybrid $ISOHYBRID_OPTIONS ${ISO_OUTPUT}/${ISO_NAME}"
1314 isohybrid $ISOHYBRID_OPTIONS "${ISO_OUTPUT}/${ISO_NAME}"
1319 # generate md5sum and sha1sum of ISO if we are using class 'RELEASE':
1320 case $CLASSES in *RELEASE*)
1323 if cd $ISO_OUTPUT ; then
1324 md5sum ${ISO_NAME} > ${ISO_NAME}.md5 && \
1325 touch -r ${ISO_NAME} ${ISO_NAME}.md5
1326 sha1sum ${ISO_NAME} > ${ISO_NAME}.sha1 && \
1327 touch -r ${ISO_NAME} ${ISO_NAME}.sha1
1336 if [ "$RC" = 0 ] ; then
1337 log "Finished execution of stage 'iso build' [$(date)]"
1338 einfo "Finished execution of stage 'iso build'" ; eend 0
1340 log "Error: there was a critical error ($RC) executing stage 'iso build' [$(date)]"
1341 eerror "Error: there was a critical error executing stage 'iso build'" ; eend 1
1347 # netboot package {{{
1348 create_netbootpackage() {
1349 local OUTPUT_FILE="${NETBOOT}/grml_netboot_package_${GRML_NAME}_${VERSION}.tar.bz2"
1351 if [ -f "${OUTPUT_FILE}" -a -z "$UPDATE" -a -z "$BUILD_ONLY" -a -z "$BUILD_DIRTY" ] ; then
1352 log "Skipping stage 'netboot' as $OUTPUT_FILE exists already."
1353 ewarn "Skipping stage 'netboot' as $OUTPUT_FILE exists already." ; eend 0
1355 elif [ -n "$SKIP_NETBOOT" ] ; then
1356 log "Skipping stage 'netboot' as requested via option -Q"
1357 ewarn "Skipping stage 'netboot' as requested via option -Q" ; eend 0
1363 if ! [ -r "${CHROOT}/usr/lib/syslinux/pxelinux.0" ] ; then
1364 ewarn "File /usr/lib/syslinux/pxelinux.0 not found in build chroot." ; eend 0
1366 einfo "Install syslinux[-common] package in chroot to get a netboot package."
1371 local OUTPUTDIR="${NETBOOT}/build_tmp"
1372 local WORKING_DIR="${OUTPUTDIR}/grml_netboot_package_${GRML_NAME}_${VERSION}/tftpboot/"
1374 mkdir -p "$WORKING_DIR"
1376 cp "${CHROOT_OUTPUT}"/boot/vmlinuz-* "$WORKING_DIR"/linux26
1377 cp "${CHROOT_OUTPUT}"/boot/initrd.img-* "$WORKING_DIR"/initrd.img
1378 cp "${CHROOT_OUTPUT}"/usr/lib/syslinux/pxelinux.0 "${WORKING_DIR}/pxelinux.0"
1380 if [ -r "${BUILD_OUTPUT}/boot/isolinux/netboot.cfg" ] ; then
1381 mkdir -p "${WORKING_DIR}/pxelinux.cfg/default"
1382 cp "${BUILD_OUTPUT}/boot/isolinux/netboot.cfg" "${WORKING_DIR}/pxelinux.cfg/default"
1384 ewarn "File ${BUILD_OUTPUT}/boot/isolinux/netboot.cfg not found." ; eend 0
1387 mkdir -p "${WORKING_DIR}/pxelinux.cfg"
1389 if tar -C "$OUTPUTDIR" -jcf "${OUTPUT_FILE}" "grml_netboot_package_${GRML_NAME}_${VERSION}" ; then
1390 sha1sum "${OUTPUT_FILE}" > "${OUTPUT_FILE}.sha1"
1391 einfo "Generated netboot package ${OUTPUT_FILE}" ; eend 0
1392 rm -rf "${OUTPUTDIR}"
1394 rm -rf "${OUTPUTDIR}"
1395 eerror "Could not generate netboot package ${OUTPUT_FILE}" ; eend 1
1400 create_netbootpackage
1403 # log build information to database if grml-live-db is installed and enabled {{{
1405 if [ -d /usr/share/grml-live-db ] ; then
1408 DPKG_LIST="/var/log/fai/$HOSTNAME/last/dpkg.list" # the dpkg --list output of the chroot:
1409 [ -n "$DPKG_DATABASE" ] || DPKG_DATABASE=/var/log/grml-live.db
1410 [ -n "$DPKG_DBSCRIPT" ] || DPKG_DBSCRIPT=/usr/share/grml-live-db/scripts/dpkg-to-db
1411 [ -n "$DPKG_DBOPTIONS" ] || DPKG_DBOPTIONS="--database $DPKG_DATABASE --logfile $LOGFILE --flavour $GRML_NAME --dpkg $DPKG_LIST"
1413 if ! [ -x "$DPKG_DBSCRIPT" ] ; then
1414 log "Error: $DPKG_DBSCRIPT is not executable, can not log dpkg information."
1415 eerror "Error: $DPKG_DBSCRIPT is not executable, can not log dpkg information." ; eend 1
1419 # disable by default for now, not sure whether really everyone is using a local db file
1420 #if ! touch "$DPKG_DATABASE" ; then
1421 # eerror "Error: can not write to ${DPKG_DATABASE}, can not log dpkg information." ; eend 1
1425 if ! [ -r "$DPKG_LIST" ] ; then
1426 log "Warning: can not read $DPKG_LIST - can not provide information to $DPKG_DBSCRIPT (dirty build?)"
1427 ewarn "Warning: can not read $DPKG_LIST - can not provide information to $DPKG_DBSCRIPT (dirty build?)" ; eend 0
1429 einfo "Logging $DPKG_LIST to database $DPKG_DATABASE"
1430 log "Logging $DPKG_LIST to database $DPKG_DATABASE"
1431 log "Executing $DPKG_DBSCRIPT $DPKG_DBOPTIONS"
1434 if DB_INFO=$("$DPKG_DBSCRIPT" $DPKG_DBOPTIONS 2>&1) ; then
1450 [ -n "$start_seconds" ] && SECONDS="$[$(cut -d . -f 1 /proc/uptime)-$start_seconds]" || SECONDS="unknown"
1451 log "Successfully finished execution of $PN [$(date) - running ${SECONDS} seconds]"
1453 dpkg_to_db # make sure we catch the last log line as well, therefore execute between log + einfo
1455 einfo "Successfully finished execution of $PN [$(date) - running ${SECONDS} seconds]" ; eend 0
1459 ## END OF FILE #################################################################
1460 # vim:foldmethod=marker ts=2 ft=sh ai expandtab tw=80 sw=2