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
26 # The line following this line is patched by debian/rules.
27 GRML_LIVE_VERSION='***UNRELEASED***'
32 ADDONS_LIST_FILE='/boot/isolinux/addons_list.cfg'
35 # usage information {{{
39 $PN - build process script for generating a (grml based) Linux Live-ISO
41 Usage: $PN [options, see as follows]
43 -a <architecture> architecture; available values: i386 and amd64
44 -A clean build directories before and after running
45 -b build the ISO without updating the chroot via FAI
46 -B build the ISO without touching the chroot (skips cleanup)
47 -c <classe[s]> classes to be used for building the ISO via FAI
48 -C <configfile> configuration file for grml-live
49 -d <date> use specified date instead of build time as date of release
50 -D <configdir> use specified configuration directory instead of /etc/grml/fai
51 -e <iso_name> extract ISO and squashfs contents from iso_name
52 -F force execution without prompting
53 -g <grml_name> set the grml flavour name
54 -h display short usage information and exit
55 -i <iso_name> name of ISO
56 -I <src_directory> directory which provides files that should become
57 part of the chroot/ISO
58 -n skip generation of ISO
59 -N bootstrap (build chroot) only, do not create files for ISO
60 -o <output_directory> main output directory of the build process
62 -Q skip netboot package build
63 -r <release_name> release name
64 -s <suite> Debian suite; values: etch, lenny, squeeze, sid
65 -t <template_directory> place of the templates
66 -u update existing chroot instead of rebuilding it from scratch
67 -U <username> arrange output to be owned by specified username
68 -v <version_number> specify version number of the release
69 -V increase verbosity in the build process
70 -z use ZLIB instead of LZMA/XZ compression
75 $PN -c GRMLBASE,GRML_FULL,AMD64 -o /dev/shm/grml
76 $PN -c GRMLBASE,GRML_FULL,AMD64 -i grml_0.0-1.iso -v 0.0-1
77 $PN -c GRMLBASE,GRML_FULL,AMD64 -s sid -V -r 'grml-live rocks'
79 More details: man grml-live + /usr/share/doc/grml-live/grml-live.html
80 http://grml.org/grml-live/
82 Please send your bug reports and feedback to the grml-team: http://grml.org/bugs/
86 # make sure it's possible to get usage information without being
87 # root or actually executing the script
88 if [ "$1" = '-h' -o "$1" = '--help' ] ; then
90 [ "$(id -u 2>/dev/null)" != 0 ] && echo "Please notice that this script requires root permissions."
95 # some runtime checks {{{
96 # we need root permissions for the build-process:
97 if [ "$(id -u 2>/dev/null)" != 0 ] ; then
98 echo "Error: please run this script with uid 0 (root)." >&2
102 if [ -r /var/run/fai/FAI_INSTALLATION_IN_PROGRESS ] ; then
103 echo "/usr/sbin/fai already running or was aborted before.">&2
104 echo "You may remove /var/run/fai/FAI_INSTALLATION_IN_PROGRESS and try again.">&2
109 if [ -r /var/run/fai/fai_softupdate_is_running ] ; then
110 echo "/usr/sbin/fai softupdate already running or was aborted before.">&2
111 echo "You may remove /var/run/fai/fai_softupdate_is_running and try again.">&2
116 # lsb-functions and configuration stuff {{{
117 # make sure they are not set by default
128 # don't use colors/escape sequences
129 if [ -r /lib/lsb/init-functions ] ; then
130 . /lib/lsb/init-functions
131 ! log_use_fancy_output && NOCOLORS=true
134 if [ -r /etc/grml/lsb-functions ] ; then
135 . /etc/grml/lsb-functions
137 einfo() { echo " [*] $*" ;}
138 eerror() { echo " [!] $*">&2 ;}
139 ewarn() { echo " [x] $*" ;}
141 eindent() { return 0 ;}
142 eoutdent() { return 0 ;}
145 # source main configuration file:
146 LIVE_CONF=/etc/grml/grml-live.conf
150 # umount all directories {{{
152 # make sure we don't leave any mounts - FAI doesn't remove them always
153 umount $CHROOT_OUTPUT/proc/sys/fs/binfmt_misc 2>/dev/null || /bin/true
154 umount $CHROOT_OUTPUT/proc 2>/dev/null || /bin/true
155 umount $CHROOT_OUTPUT/sys 2>/dev/null || /bin/true
156 umount $CHROOT_OUTPUT/dev/pts 2>/dev/null || /bin/true
157 umount $CHROOT_OUTPUT/dev 2>/dev/null || /bin/true
159 # certain FAI versions sadly leave a ramdisk behind, so better safe than sorry
160 if [ -x /usr/lib/fai/mkramdisk ] ; then
161 /usr/lib/fai/mkramdisk -u "$(readlink -f ${CHROOT_OUTPUT}/var/lib/dpkg)" >/dev/null 2>&1 || /bin/true
164 umount "${CHROOT_OUTPUT}/grml-live/sources/" 2>/dev/null || /bin/true
165 [ -n "$MIRROR_DIRECTORY" ] && umount "${CHROOT_OUTPUT}/${MIRROR_DIRECTORY}"
171 rm -f /var/run/fai/fai_softupdate_is_running \
172 /var/run/fai/FAI_INSTALLATION_IN_PROGRESS
173 [ -n "$CONFIGDUMP" ] && rm -f "$CONFIGDUMP"
174 [ -n "$SQUASHFS_STDERR" ] && rm -rf "$SQUASHFS_STDERR"
176 [ -n "$1" ] && EXIT="$1" || EXIT="1"
177 [ -n "$2" ] && eerror "$2">&2
178 if [ -n "$CLEAN_ARTIFACTS" ]; then
181 [ -n "${BUILD_OUTPUT}" -a -d "${BUILD_OUTPUT}" ] && rm -r "${BUILD_OUTPUT}"
182 [ -n "${CHROOT_OUTPUT}" -a -d "${CHROOT_OUTPUT}" ] && rm -r "${CHROOT_OUTPUT}"
185 if [ -n "$CHOWN_USER" ]; then
186 log "Setting ownership"
187 einfo "Setting ownership"
188 [ -n "${OUTPUT}" -a -d "${OUTPUT}" ] && chown -R "${CHOWN_USER}:" "${OUTPUT}"
189 [ -n "${BUILD_OUTPUT}" -a -d "${BUILD_OUTPUT}" ] && chown -R "${CHOWN_USER}:" "${BUILD_OUTPUT}"
190 [ -n "${CHROOT_OUTPUT}" -a -d "${CHROOT_OUTPUT}" ] && chown -R "${CHOWN_USER}:" "${CHROOT_OUTPUT}"
191 [ -n "${ISO_OUTPUT}" -a -d "${ISO_OUTPUT}" ] && chown -R "${CHOWN_USER}:" "${ISO_OUTPUT}"
192 [ -n "${LOG_OUTPUT}" -a -d "${LOG_OUTPUT}" ] && chown -R "${CHOWN_USER}:" "${LOG_OUTPUT}"
193 [ -n "${NETBOOT}" -a -d "${NETBOOT}" ] && chown -R "${CHOWN_USER}:" "${NETBOOT}"
196 log "------------------------------------------------------------------------------"
199 trap bailout 1 2 3 3 6 9 14 15
203 # some important functions {{{
206 # usage: log "string to log"
207 log() { [ -n "$LOGFILE" ] && echo "$*" >> $LOGFILE ; }
209 # cut string at character number int = $1
210 # usage: cut_string 5 "1234567890" will output "12345"
212 [ -n "$2" ] || return 1
213 echo "$2" | head -c "$1"; echo -ne "\n"
216 # prepend int = $1 spaces before string = $2
217 # usage: extend_string_begin 5 "123" will output " 123"
218 extend_string_begin() {
219 [ -n "$2" ] || return 1
220 local COUNT="$(echo $2 | wc -c)"
221 local FILL="$(expr $COUNT - $1)"
222 while [ "$FILL" -gt 1 ] ; do
224 local FILL=$(expr $FILL - 1)
226 while [ "$FILL" -lt 1 ] ; do
228 local FILL=$(expr $FILL + 1)
230 echo "$2" | head -c "$1"; echo -ne "\n"
233 # append int = $1 spaces to string = $2
234 # usage: extend_string_begin 5 "123" will output "123 "
235 extend_string_end() {
236 [ -n "$2" ] || return 1
237 echo -n "$2" | head -c "$1"
238 local COUNT="$(echo $2 | wc -c)"
239 local FILL="$(expr $COUNT - $1)"
240 while [ "$FILL" -gt 1 ] ; do
242 local FILL=$(expr $FILL - 1)
244 while [ "$FILL" -lt 1 ] ; do
246 local FILL=$(expr $FILL + 1)
251 # Copy addonfile $1 from either
252 # * the chroot (via $2, the system path),
253 # * or from TEMPLATE_DIRECTORY/compat (if exists),
254 # * or from the host system (again, using $2),
255 # or warn about the missing file.
258 # * We assume that the chroot always has a "good" version of
259 # the file. Also it makes sources handling easier.
260 # * On unstable, we Recommend the Debian packages containing
261 # these files. The user can override them by putting his
262 # "better" version into the chroot.
263 # * On stable, the Debian packages are probably not available,
264 # or outdated, so we look in TEMPLATE_DIRECTORY/compat first, where
265 # our grml-live-compat package installs current file versions.
267 DEST="${BUILD_OUTPUT}/boot/$3"
268 if [ ! -d "${DEST}/" ]; then
271 if [ -e "$CHROOT_OUTPUT/$2/$1" ]; then
272 log "Copying $1 from chroot"
273 cp "$CHROOT_OUTPUT/$2/$1" "${DEST}/"
276 if [ -e "${TEMPLATE_DIRECTORY}/compat/$3/$1" ]; then
277 log "Copying $1 from grml-live-compat"
278 cp "${TEMPLATE_DIRECTORY}/compat/$3/$1" "${DEST}/"
281 if [ -e "$2/$1" ]; then
282 log "Copying $1 from system"
283 cp "$2/$1" "${DEST}/"
287 msg="Missing addon file: \"$1\""
288 ewarn "$msg" ; eend 1
289 log "copy_addon_file: $msg"
293 # command line parsing {{{
294 while getopts "a:C:c:d:D:e:g:i:I:o:r:s:t:U:v:AbBFnNqQuVz" opt; do
297 A) CLEAN_ARTIFACTS=1 ;;
300 c) CLASSES="$OPTARG" ;;
301 C) LOCAL_CONFIG="$(readlink -f $OPTARG)" ;;
303 D) GRML_FAI_CONFIG="$(readlink -f $OPTARG)" ;;
304 e) EXTRACT_ISO_NAME="$(readlink -f $OPTARG)" ;;
305 g) GRML_NAME="$OPTARG" ;;
306 i) ISO_NAME="$OPTARG" ;;
307 I) CHROOT_INSTALL="$OPTARG" ;;
309 N) BOOTSTRAP_ONLY=1; SKIP_MKISOFS=1; SKIP_MKSQUASHFS=1 ;;
310 o) OUTPUT="$(readlink -f $OPTARG)" ;;
311 q) SKIP_MKSQUASHFS=1 ;;
313 r) RELEASENAME="$OPTARG" ;;
314 s) SUITE="$OPTARG" ;;
315 t) TEMPLATE_DIRECTORY="$OPTARG";;
316 v) VERSION="$OPTARG" ;;
319 U) CHOWN_USER="$OPTARG" ;;
321 z) SQUASHFS_ZLIB=1 ;;
322 ?) echo "invalid option -$OPTARG" >&2; bailout 1 ;;
325 shift $(($OPTIND - 1)) # set ARGV to the first not parsed commandline parameter
328 # read local (non-packaged) configuration {{{
329 if [ -z "$LOCAL_CONFIG" ]; then
330 if [ -r "/etc/grml/grml-live.local" ]; then
331 LOCAL_CONFIG="/etc/grml/grml-live.local"
334 if [ -n "$LOCAL_CONFIG" ]; then
335 if [ -r "$LOCAL_CONFIG" ]; then
338 eerror "Could not read specified local configuration file \"$LOCAL_CONFIG\"."
341 LOCAL_CONFIG=$(readlink -f "$LOCAL_CONFIG")
346 if [ -n "${GRML_LIVE_SOURCES:-}" ] ; then
347 eerror "Config variable \$GRML_LIVE_SOURCES is set. This variable has been deprecated."
348 ewarn "Please set up \${GRML_FAI_CONFIG}/config/files/etc/apt/sources.list.d/* instead."
353 # assume sane defaults (if not set already) {{{
354 [ -n "$ARCH" ] || ARCH="$(dpkg --print-architecture)"
355 [ -n "$BOOT_METHOD" ] || BOOT_METHOD='isolinux'
356 [ -n "$CLASSES" ] || CLASSES="GRMLBASE,GRML_FULL,$(echo ${ARCH} | tr 'a-z' 'A-Z')"
357 [ -n "$DATE" ] || DATE="$(date +%Y-%m-%d)"
358 [ -n "$DISTRI_INFO" ] || DISTRI_INFO='Grml - Live Linux for system administrators'
359 [ -n "$DISTRI_NAME" ] || DISTRI_NAME="grml"
360 [ -n "$DISTRI_SPLASH" ] || DISTRI_SPLASH='grml.png'
361 [ -n "$FORCE_ISO_REBUILD" ] || FORCE_ISO_REBUILD="false"
362 [ -n "$GRML_FAI_CONFIG" ] || GRML_FAI_CONFIG='/etc/grml/fai'
363 [ -n "$GRML_NAME" ] || GRML_NAME='grml'
364 [ -n "$HOSTNAME" ] || HOSTNAME='grml'
365 [ -n "$HYBRID_METHOD" ] || HYBRID_METHOD='manifold'
366 [ -n "$NFSROOT_CONF" ] || NFSROOT_CONF="${GRML_FAI_CONFIG}/make-fai-nfsroot.conf"
367 [ -n "$RELEASENAME" ] || RELEASENAME='grml-live rocks'
368 [ -n "$SQUASHFS_EXCLUDES_FILE" ] || SQUASHFS_EXCLUDES_FILE="${GRML_FAI_CONFIG}/config/grml/squashfs-excludes"
369 [ -n "$SUITE" ] || SUITE='squeeze'
370 [ -n "$TEMPLATE_DIRECTORY" ] || TEMPLATE_DIRECTORY='/usr/share/grml-live/templates'
371 [ -n "$USERNAME" ] || USERNAME='grml'
372 [ -n "$VERSION" ] || VERSION='0.0.1'
374 # output specific stuff, depends on $OUTPUT (iff not set):
375 [ -n "$OUTPUT" ] || OUTPUT='/grml/grml-live'
376 [ -n "$BUILD_OUTPUT" ] || BUILD_OUTPUT="$OUTPUT/grml_cd"
377 [ -n "$CHROOT_OUTPUT" ] || CHROOT_OUTPUT="$OUTPUT/grml_chroot"
378 [ -n "$ISO_OUTPUT" ] || ISO_OUTPUT="$OUTPUT/grml_isos"
379 [ -n "$LOG_OUTPUT" ] || LOG_OUTPUT="$OUTPUT/grml_logs"
380 [ -n "$REPORTS" ] || REPORTS="${LOG_OUTPUT}/reports/"
381 [ -n "$NETBOOT" ] || NETBOOT="${OUTPUT}/netboot/"
384 # some misc checks before executing FAI {{{
385 [ -n "$CLASSES" ] || bailout 1 "Error: \$CLASSES unset, please set it in $LIVE_CONF or
386 specify it on the command line using the -c option."
387 [ -n "$OUTPUT" ] || bailout 1 "Error: \$OUTPUT unset, please set it in $LIVE_CONF or
388 specify it on the command line using the -o option."
390 # trim characters that are known to cause problems inside $GRML_NAME;
391 # for example isolinux does not like '-' inside the directory name
392 [ -n "$GRML_NAME" ] && export SHORT_NAME="$(echo $GRML_NAME | tr -d ',./;\- ')"
394 # export variables to have them available in fai scripts:
395 [ -n "$GRML_NAME" ] && export GRML_NAME="$GRML_NAME"
396 [ -n "$RELEASENAME" ] && export RELEASENAME="$RELEASENAME"
399 # ZERO_LOGFILE - check for backwards compatibility reasons {{{
400 # this was default behaviour until grml-live 0.9.34:
401 if [ -n "$ZERO_LOGFILE" ] ; then
402 PRESERVE_LOGFILE='' # make sure it's cleaned then
403 ewarn "Please consider disabling the \$ZERO_LOGFILE option as grml-live clears..."
404 ewarn "... the logfile $LOGFILE by default (unless \$PRESERVE_LOGFILE is set) nowadays."
409 # ask user whether the setup is ok {{{
410 if [ -z "$FORCE" ] ; then
412 echo "${PN} [${GRML_LIVE_VERSION}]: check your configuration (or use -F to force execution):"
414 echo " FAI classes: $CLASSES"
415 [ -n "$LOCAL_CONFIG" ] && echo " Configuration: $LOCAL_CONFIG"
416 [ -n "$GRML_FAI_CONFIG" ] && echo " Config directory: $GRML_FAI_CONFIG"
417 echo " main directory: $OUTPUT"
418 [ -n "$EXTRACT_ISO_NAME" ] && echo " Extract ISO: $EXTRACT_ISO_NAME"
419 [ -n "$CHROOT_OUTPUT" ] && echo " Chroot target: $CHROOT_OUTPUT"
420 [ -n "$BUILD_OUTPUT" ] && echo " Build target: $BUILD_OUTPUT"
421 [ -n "$ISO_OUTPUT" ] && echo " ISO target: $ISO_OUTPUT"
422 [ -n "$GRML_NAME" ] && echo " Grml name: $GRML_NAME"
423 [ -n "$RELEASENAME" ] && echo " Release name: $RELEASENAME"
424 [ -n "$DATE" ] && echo " Build date: $DATE"
425 [ -n "$VERSION" ] && echo " Grml version: $VERSION"
426 [ -n "$SUITE" ] && echo " Debian suite: $SUITE"
427 [ -n "$ARCH" ] && echo " Architecture: $ARCH"
428 [ -n "$BOOT_METHOD" ] && echo " Boot method: $BOOT_METHOD"
429 [ -n "$HYBRID_METHOD" ] && echo " Hybrid method: $HYBRID_METHOD"
430 [ -n "$TEMPLATE_DIRECTORY" ] && echo " Template files: $TEMPLATE_DIRECTORY"
431 [ -n "$CHROOT_INSTALL" ] && echo " Install files from directory to chroot: $CHROOT_INSTALL"
432 [ -n "$BOOTID" ] && echo " Boot identifier: $BOOTID"
433 [ -n "$NO_BOOTID" ] && echo " Skipping bootid feature."
434 [ -n "$CHOWN_USER" ] && echo " Output owner: $CHOWN_USER"
435 [ -n "$DEFAULT_BOOTOPTIONS" ] && echo " Adding default bootoptions: \"$DEFAULT_BOOTOPTIONS\""
436 [ -n "$FAI_ARGS" ] && echo " Additional arguments for FAI: $FAI_ARGS"
437 [ -n "$LOGFILE" ] && echo " Logging to file: $LOGFILE"
438 [ -n "$SQUASHFS_ZLIB" ] && echo " Using ZLIB (instead of LZMA/XZ) compression."
439 [ -n "$SQUASHFS_OPTIONS" ] && echo " Using SQUASHFS_OPTIONS ${SQUASHFS_OPTIONS}"
440 [ -n "$VERBOSE" ] && echo " Using VERBOSE mode."
441 [ -n "$CLEAN_ARTIFACTS" ] && echo " Will clean output before and after running."
442 [ -n "$UPDATE" ] && echo " Executing UPDATE instead of fresh installation."
443 if [ -n "$BOOTSTRAP_ONLY" ] ; then
444 echo " Bootstrapping only and not building (files for) ISO."
446 [ -n "$SKIP_MKSQUASHFS" ] && echo " Skipping creation of SQUASHFS file."
447 [ -n "$SKIP_NETBOOT" ] && echo " Skipping creation of NETBOOT package."
448 [ -n "$SKIP_MKISOFS" ] && echo " Skipping creation of ISO file."
449 [ -n "$BUILD_ONLY" ] && echo " Executing BUILD_ONLY instead of fresh installation or UPDATE."
450 [ -n "$BUILD_DIRTY" ] && echo " Executing BUILD_DIRTY to leave chroot untouched."
453 echo -n "Is this ok for you? [y/N] "
455 if ! [ "$a" = 'y' -o "$a" = 'Y' ] ; then
456 bailout 1 "Exiting as requested."
462 # clean up before start {{{
463 if [ -n "${CLEAN_ARTIFACTS}" ]; then
464 echo "Wiping old artifacts"
465 [ -n "${CHROOT_OUTPUT}" -a -d "${CHROOT_OUTPUT}" ] && rm -r "${CHROOT_OUTPUT}"
466 [ -n "${BUILD_OUTPUT}" -a -d "${BUILD_OUTPUT}" ] && rm -r "${BUILD_OUTPUT}"
467 [ -n "${ISO_OUTPUT}" -a -d "${ISO_OUTPUT}" ] && rm -r "${ISO_OUTPUT}"
468 [ -n "${LOG_OUTPUT}" -a -d "${LOG_OUTPUT}" ] && rm -r "${LOG_OUTPUT}"
469 [ -n "${NETBOOT}" -a -d "${NETBOOT}" ] && rm -r "${NETBOOT}"
473 # create log file {{{
474 [ -n "$LOGFILE" ] || LOGFILE=${LOG_OUTPUT}/grml-live.log
475 mkdir -p $(dirname "${LOGFILE}")
477 chown root:adm $LOGFILE
481 # clean/zero/remove logfiles {{{
483 if [ -n "$PRESERVE_LOGFILE" ] ; then
484 echo "Preserving logfile $LOGFILE as requested via \$PRESERVE_LOGFILE"
486 # make sure it is empty (as it is e.g. appended to grml-live-db)
490 if [ -n "$ZERO_FAI_LOGFILE" ] ; then
491 if [ -d /var/log/fai/"$HOSTNAME" ] ; then
492 rm -rf /var/log/fai/"$HOSTNAME"/"$(readlink /var/log/fai/"$HOSTNAME"/last)"
493 rm -rf /var/log/fai/"$HOSTNAME"/"$(readlink /var/log/fai/"$HOSTNAME"/last-dirinstall)"
494 rm -rf /var/log/fai/"$HOSTNAME"/"$(readlink /var/log/fai/"$HOSTNAME"/last-softupdate)"
495 rm -f /var/log/fai/"$HOSTNAME"/last \
496 /var/log/fai/"$HOSTNAME"/last-dirinstall \
497 /var/log/fai/"$HOSTNAME"/last-softupdate
502 # source config and startup {{{
503 if [ -n "$CONFIG" ] ; then
504 if ! [ -f "$CONFIG" ] ; then
505 log "Error: $CONFIG could not be read. Exiting. [$(date)]"
506 eerror "Error: $CONFIG could not be read. Exiting." ; eend 1
509 log "Sourcing $CONFIG"
514 start_seconds=$(cut -d . -f 1 /proc/uptime)
515 log "------------------------------------------------------------------------------"
516 log "Starting grml-live [${GRML_LIVE_VERSION}] run on $(date)"
517 log "Using local config file: $LOCAL_CONFIG"
518 log "Executed grml-live command line:"
521 einfo "Logging actions to logfile $LOGFILE"
524 # dump config variables into file, for script access {{{
527 '^(GRML_NAME|RELEASENAME|DATE|VERSION|SUITE|ARCH|DISTRI_NAME|USERNAME|HOSTNAME|APT_PROXY)=' \
531 # unpack iso/squashfs {{{
533 if [ -n "$EXTRACT_ISO_NAME" ]; then
534 log "Unpacking ISO from ${EXTRACT_ISO_NAME}"
535 einfo "Unpacking ISO from ${EXTRACT_ISO_NAME}"
536 local mountpoint=$(mktemp -d)
538 mount -o loop "${EXTRACT_ISO_NAME}" "$mountpoint" ; rc=$?
539 if [ "$rc" != 0 ]; then
542 eerror "mount failed"
546 unsquashfs -d "${CHROOT_OUTPUT}" "${mountpoint}"/live/*/*.squashfs ; rc=$?
549 if [ "$rc" != 0 ]; then
550 log "unsquashfs failed"
551 eerror "unsquashfs failed"
560 # on-the-fly configuration {{{
561 if [ -n "$FAI_DEBOOTSTRAP" ] ; then
562 sed "s#^FAI_DEBOOTSTRAP=.*#FAI_DEBOOTSTRAP=\"$FAI_DEBOOTSTRAP\"#" "$NFSROOT_CONF" | sponge "$NFSROOT_CONF"
565 # does this suck? YES!
566 # /usr/share/debootstrap/scripts/unstable does not exist, instead use 'sid':
568 unstable) SUITE='sid' ; CLASSES="DEBIAN_UNSTABLE,$CLASSES" ;;
569 *) CLASSES="DEBIAN_$(echo $SUITE | tr 'a-z' 'A-Z'),$CLASSES";;
571 export SUITE # make sure it's available in FAI scripts
573 for file in "$LIVE_CONF" "$LOCAL_CONFIG" "$NFSROOT_CONF" ; do
574 if [ -n "$file" ] ; then
575 sed "s|^FAI_DEBOOTSTRAP=\"[a-z]* |FAI_DEBOOTSTRAP=\"$SUITE |" "$file" | sponge "$file"
579 # validate whether the specified architecture class matches the
580 # architecture (option), otherwise installation of kernel will fail
581 if echo $CLASSES | grep -qi i386 ; then
582 if ! [[ "$ARCH" == "i386" ]] ; then
583 log "Error: You specified the I386 class but are trying to build something else (AMD64?)."
584 eerror "Error: You specified the I386 class but are trying to build something else (AMD64?)."
585 eerror "Tip: Either invoke grml-live with '-a i386' or adjust the architecture class. Exiting."
589 elif echo $CLASSES | grep -qi amd64 ; then
590 if ! [[ "$ARCH" == "amd64" ]] ; then
591 log "Error: You specified the AMD64 class but are trying to build something else (I386?)."
592 eerror "Error: You specified the AMD64 class but are trying to build something else (I386?)."
593 eerror "Tip: Either invoke grml-live with '-a amd64' or adjust the architecture class. Exiting."
599 if grep -q -- 'FAI_DEBOOTSTRAP_OPTS.*--arch' "$NFSROOT_CONF" ; then
600 sed "s/--arch [a-z0-9]* /--arch $ARCH /" "$NFSROOT_CONF" | sponge "$NFSROOT_CONF"
602 sed "s|^FAI_DEBOOTSTRAP_OPTS=\"\(.*\)|FAI_DEBOOTSTRAP_OPTS=\"--arch $ARCH \1|" "$NFSROOT_CONF" | sponge "$NFSROOT_CONF"
606 # CHROOT_OUTPUT - execute FAI {{{
607 if [ -n "$BUILD_DIRTY" ]; then
608 log "Skipping stage 'fai' as requested via option -B"
609 ewarn "Skipping stage 'fai' as requested via option -B" ; eend 0
611 [ -n "$CHROOT_OUTPUT" ] || CHROOT_OUTPUT="$OUTPUT/grml_chroot"
613 # provide inform fai about the ISO we build
614 [ -d "$CHROOT_OUTPUT/etc/" ] || mkdir -p "$CHROOT_OUTPUT/etc/"
615 echo '# This file has been generated by grml-live.' > "$CHROOT_OUTPUT/etc/grml_live_version"
616 [ -n "$GRML_LIVE_VERSION" ] && echo "GRML_LIVE_VERSION=$GRML_LIVE_VERSION" >> "$CHROOT_OUTPUT/etc/grml_live_version"
617 [ -n "$SUITE" ] && echo "SUITE=$SUITE" >> "$CHROOT_OUTPUT/etc/grml_live_version"
619 if [ -n "$UPDATE" -o -n "$BUILD_ONLY" ] ; then
620 FAI_ACTION=softupdate
622 FAI_ACTION=dirinstall
625 if [ -n "$UPDATE" -o -n "$BUILD_ONLY" ] ; then
626 if ! [ -r "$CHROOT_OUTPUT/etc/debian_version" ] ; then
627 log "Error: does not look like you have a working chroot. Updating/building not possible."
628 eerror "Error: does not look like you have a working chroot. Updating/building not possible. (Drop -u/-b option?)"
634 if [ -d "$CHROOT_OUTPUT/bin" -a -z "$UPDATE" -a -z "$BUILD_ONLY" ] ; then
635 log "Skipping stage 'fai dirinstall' as $CHROOT_OUTPUT exists already."
636 ewarn "Skipping stage 'fai dirinstall' as $CHROOT_OUTPUT exists already." ; eend 0
638 mkdir -p "$CHROOT_OUTPUT" || bailout 5 "Problem with creating $CHROOT_OUTPUT for FAI"
640 if [ -n "${MIRROR_DIRECTORY}" ] ; then
641 mkdir -p "${CHROOT_OUTPUT}/${MIRROR_DIRECTORY}"
642 mount --bind "${MIRROR_DIRECTORY}" "${CHROOT_OUTPUT}/${MIRROR_DIRECTORY}"
645 mkdir -p "${OUTPUT}/grml_sources/" "${CHROOT_OUTPUT}/grml-live/sources/"
646 mount --bind "${OUTPUT}/grml_sources/" "${CHROOT_OUTPUT}/grml-live/sources/"
648 # tell dpkg to use "unsafe io" during the build
649 [ -d "$CHROOT_OUTPUT/etc/dpkg/dpkg.cfg.d" ] || mkdir -p "$CHROOT_OUTPUT/etc/dpkg/dpkg.cfg.d"
650 echo force-unsafe-io > "$CHROOT_OUTPUT/etc/dpkg/dpkg.cfg.d/unsafe-io"
652 log "Executed FAI command line:"
653 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"
654 BUILD_ONLY="$BUILD_ONLY" BOOTSTRAP_ONLY="$BOOTSTRAP_ONLY" GRML_LIVE_CONFIG="$CONFIGDUMP" fai $VERBOSE \
655 -C "$GRML_FAI_CONFIG" -s "file:///$GRML_FAI_CONFIG/config" -c"$CLASSES" \
656 -u "$HOSTNAME" "$FAI_ACTION" "$CHROOT_OUTPUT" $FAI_ARGS | tee -a $LOGFILE
657 RC="$PIPESTATUS" # notice: bash-only
659 rm -f "$CHROOT_OUTPUT/etc/dpkg/dpkg.cfg.d/unsafe-io"
661 FORCE_ISO_REBUILD=true
663 if [ "$RC" != 0 ] ; then
664 log "Error: critical error while executing fai [exit code ${RC}]. Exiting."
665 eerror "Error: critical error while executing fai [exit code ${RC}]. Exiting." ; eend 1
669 # move fai logs into grml_logs directory
670 mkdir -p "$LOG_OUTPUT"/fai/
671 cp -r "$CHROOT_OUTPUT"/var/log/fai/"$HOSTNAME"/last/* "$LOG_OUTPUT"/fai/
672 rm -rf "$CHROOT_OUTPUT"/var/log/fai
673 # copy fai package list
674 cp "$CHROOT_OUTPUT"/var/log/install_packages.list "$LOG_OUTPUT"/fai/
676 chown root:adm "$LOG_OUTPUT"/fai/*
677 chmod 664 "$LOG_OUTPUT"/fai/*
681 # notice: 'fai dirinstall' does not seem to exit appropriate, so:
683 CHECKLOG="$LOG_OUTPUT"/fai/
684 if [ -r "$CHECKLOG/software.log" ] ; then
685 # 1 errors during executing of commands
686 grep 'dpkg: error processing' $CHECKLOG/software.log >> $LOGFILE && ERROR=1
687 grep 'E: Method http has died unexpectedly!' $CHECKLOG/software.log >> $LOGFILE && ERROR=2
688 grep 'ERROR: chroot' $CHECKLOG/software.log >> $LOGFILE && ERROR=3
689 grep 'E: Failed to fetch' $CHECKLOG/software.log >> $LOGFILE && ERROR=4
690 grep 'Unable to write mmap - msync (28 No space left on device)' $CHECKLOG/software.log >> $LOGFILE && ERROR=5
693 if [ -r "$CHECKLOG/shell.log" ] ; then
694 grep 'FAILED with exit code' $CHECKLOG/shell.log >> $LOGFILE && ERROR=6
697 if [ -n "$ERROR" ] ; then
698 log "Error: there was a critical error [${ERROR}] during execution of stage 'fai dirinstall' [$(date)]"
699 eerror "Error: there was a critical error during execution of stage 'fai dirinstall'"
700 eerror "Note: check out ${CHECKLOG}/ for details. [exit ${ERROR}]"
704 log "Finished execution of stage 'fai dirinstall' [$(date)]"
705 einfo "Finished execution of stage 'fai dirinstall'"
711 # package validator {{{
712 CHECKLOG=/var/log/fai/$HOSTNAME/last
713 if [ -r "$CHECKLOG/dpkg.selections" ] ; then
714 package_count=$(wc -l "$CHECKLOG/dpkg.selections" | awk '{print $1}')
716 package_count="unknown"
720 REPORT_MISSING_PACKAGES="${REPORTS}/TEST-MissingPackages.xml"
722 # check for missing packages
723 if ! [ -s "$CHECKLOG/package_errors.log" ] ; then
724 einfo "No missing packages found, generating empty junit report."
726 cat > "${REPORT_MISSING_PACKAGES}" << EOF
727 <?xml version="1.0" encoding="UTF-8"?>
728 <testsuite name="grml-live-missing-packages" tests="${package_count}" time="1" failures="0" errors="0" skipped="0" assertions="0">
729 <testcase name="test_missing_packages" time="0" assertions="0">
739 einfo "Missing packages found, generating junit report."
741 if [ -r "$CHECKLOG/package_errors.log" ] ; then
742 package_errors=$(wc -l "$CHECKLOG/package_errors.log" | awk '{print $1}')
744 package_errors="unknown"
748 REPORT_MISSING_PACKAGES="${REPORTS}/TEST-MissingPackages.xml"
750 cat > "${REPORT_MISSING_PACKAGES}" << EOF
751 <?xml version="1.0" encoding="UTF-8"?>
752 <testsuite name="grml-live-missing-packages" tests="${package_count}" time="1" failures="${package_errors}" errors="${package_errors}" skipped="0" assertions="0">
755 for package in $(awk '{print $5}' "${CHECKLOG}/package_errors.log" | sed 's/\.$//') ; do
756 cat >> "${REPORT_MISSING_PACKAGES}" << EOF
757 <testcase name="test_missing_packages_${package}" time="0" assertions="0">
758 <failure type="RuntimeError" message="Package ${package} is missing">
759 Package $package is missing in chroot
765 cat >> "${REPORT_MISSING_PACKAGES}" << EOF
774 if [ -n "$EXIT_ON_MISSING_PACKAGES" -a -z "$BUILD_DIRTY" ] ; then
775 eerror "The following packages were requested for installation but could not be processed:"
776 cat "$CHECKLOG/package_errors.log"
777 eerror "... exiting as requested via \$EXIT_ON_MISSING_PACKAGES."
781 ewarn "The following packages were requested for installation but could not be processed:"
782 cat "$CHECKLOG/package_errors.log"
788 # BUILD_OUTPUT - execute arch specific stuff and squashfs {{{
789 [ -n "$BUILD_OUTPUT" ] || BUILD_OUTPUT="$OUTPUT/grml_cd"
790 mkdir -p "$BUILD_OUTPUT" || bailout 6 "Problem with creating $BUILD_OUTPUT for stage ARCH"
793 if [ "$ARCH" = i386 ] || [ "$ARCH" = amd64 ] ; then
794 if [ -n "$BOOTSTRAP_ONLY" ] ; then
795 log "Skipping stage 'boot' as building with bootstrap only."
796 ewarn "Skipping stage 'boot' as building with bootstrap only." ; eend 0
798 if [ -d "$BUILD_OUTPUT"/boot/isolinux -a -z "$UPDATE" -a -z "$BUILD_ONLY" ] ; then
799 log "Skipping stage 'boot' as $BUILD_OUTPUT/boot/isolinux exists already."
800 ewarn "Skipping stage 'boot' as $BUILD_OUTPUT/boot/isolinux exists already." ; eend 0
803 [ -d "$BUILD_OUTPUT"/boot/isolinux ] || mkdir -p "$BUILD_OUTPUT"/boot/isolinux
804 [ -d "$BUILD_OUTPUT"/boot/"${SHORT_NAME}" ] || mkdir -p "$BUILD_OUTPUT"/boot/"${SHORT_NAME}"
806 # if we don't have an initrd we a) can't boot and b) there was an error
807 # during build, so check for the file:
808 INITRD="$(ls $CHROOT_OUTPUT/boot/initrd* 2>/dev/null| grep -v '.bak$' | sort -r | head -1)"
809 if [ -n "$INITRD" ] ; then
810 cp $INITRD "$BUILD_OUTPUT"/boot/"${SHORT_NAME}"/initrd.img
811 find $CHROOT_OUTPUT/boot/ -name initrd\*.bak -exec rm {} \;
813 log "Error: No initrd found inside $CHROOT_OUTPUT/boot/ - Exiting"
814 eerror "Error: No initrd found inside $CHROOT_OUTPUT/boot/ - Exiting" ; eend 1
818 KERNEL_IMAGE="$(ls $CHROOT_OUTPUT/boot/vmlinuz* 2>/dev/null | sort -r | head -1)"
819 if [ -n "$KERNEL_IMAGE" ] ; then
820 cp "$KERNEL_IMAGE" "$BUILD_OUTPUT"/boot/"${SHORT_NAME}"/vmlinuz
822 log "Error: No kernel found inside $CHROOT_OUTPUT/boot/ - Exiting"
823 eerror "Error: No kernel found inside $CHROOT_OUTPUT/boot/ - Exiting" ; eend 1
828 if [ -r "${CHROOT_OUTPUT}/boot/efi.img" -a -r "${CHROOT_OUTPUT}/boot/bootx64.efi" ] ; then
829 einfo "Moving EFI boot files into ISO path."
830 log "Moving EFI boot files into ISO path."
832 mv "${CHROOT_OUTPUT}/boot/efi.img" "${BUILD_OUTPUT}/boot/" || RC=$?
833 mkdir -p "${BUILD_OUTPUT}/efi/boot/" || RC=$?
834 mv "${CHROOT_OUTPUT}/boot/bootx64.efi" "${BUILD_OUTPUT}/efi/boot/bootx64.efi" || RC=$?
838 [ -n "$TEMPLATE_DIRECTORY" ] || TEMPLATE_DIRECTORY='/usr/share/grml-live/templates'
839 if ! [ -d "${TEMPLATE_DIRECTORY}"/boot ] ; then
840 log "Error: ${TEMPLATE_DIRECTORY}/boot does not exist. Exiting."
841 eerror "Error: ${TEMPLATE_DIRECTORY}/boot does not exist. Exiting." ; eend 1
845 # copy _required_ isolinux files
846 for file in ifcpu64.c32 isolinux.bin vesamenu.c32; do
847 copy_addon_file "${file}" /usr/lib/syslinux isolinux
850 # *always* copy files to output directory so the variables
851 # get adjusted according to the build.
852 cp ${TEMPLATE_DIRECTORY}/boot/isolinux/* "$BUILD_OUTPUT"/boot/isolinux/
854 if [ -n "$NO_ADDONS" ] ; then
855 log "Skipping installation of boot addons as requested via \$NO_ADDONS."
856 einfo "Skipping installation of boot addons as requested via \$NO_ADDONS."; eend 0
858 if ! [ -d "$TEMPLATE_DIRECTORY"/boot/addons ] ; then
859 log "Boot addons not found, skipping therefore. (Consider installing package grml-live-addons)"
860 ewarn "Boot addons not found, skipping therefore. (Consider installing package grml-live-addons)" ; eend 0
862 # copy addons from system packages or grml-live-compat
863 copy_addon_file ipxe.lkrn /usr/lib/ipxe addons
864 copy_addon_file pci.ids /usr/share/misc addons
865 copy_addon_file memtest86+.bin /boot addons
866 for file in memdisk chain.c32 hdt.c32 menu.c32; do
867 copy_addon_file "${file}" /usr/lib/syslinux addons
870 # make memtest filename FAT16/8.3 compatible
871 mv "${BUILD_OUTPUT}/boot/addons/memtest86+.bin" \
872 "${BUILD_OUTPUT}/boot/addons/memtest"
874 # copy only files so we can handle bsd4grml on its own
875 for file in ${TEMPLATE_DIRECTORY}/boot/addons/* ; do
876 test -f $file && cp $file "$BUILD_OUTPUT"/boot/addons/
879 if [ -n "$NO_ADDONS_BSD4GRML" ] ; then
880 log "Skipping installation of bsd4grml as requested via \$NO_ADDONS_BSD4GRML."
881 einfo "Skipping installation of bsd4grml as requested via \$NO_ADDONS_BSD4GRML."; eend 0
883 if [ -d "$TEMPLATE_DIRECTORY"/boot/addons/bsd4grml ] ; then
884 cp -a ${TEMPLATE_DIRECTORY}/boot/addons/bsd4grml "$BUILD_OUTPUT"/boot/addons/
886 log "Missing addon file: bsd4grml"
887 ewarn "Missing addon file: bsd4grml" ; eend 0
891 fi # no "$TEMPLATE_DIRECTORY"/boot/addons
894 if ! [ -d "${BUILD_OUTPUT}/boot/grub" ] ; then
895 mkdir -p "${BUILD_OUTPUT}/boot/grub"
897 cp -a ${TEMPLATE_DIRECTORY}/boot/grub/* "$BUILD_OUTPUT"/boot/grub/
899 # copy grub files from target
900 cp -a "${CHROOT_OUTPUT}"/usr/lib/grub/*-pc/*.mod "${BUILD_OUTPUT}"/boot/grub/
901 cp -a "${CHROOT_OUTPUT}"/usr/lib/grub/*-pc/*.o "${BUILD_OUTPUT}"/boot/grub/
902 cp -a "${CHROOT_OUTPUT}"/usr/lib/grub/*-pc/*.lst "${BUILD_OUTPUT}"/boot/grub/
903 cp -a "${CHROOT_OUTPUT}"/usr/share/grub/ascii.pf2 "${BUILD_OUTPUT}"/boot/grub/
904 cp -a "${CHROOT_OUTPUT}"/boot/grub/core.img "${BUILD_OUTPUT}"/boot/grub/
906 if ! [ -d "${TEMPLATE_DIRECTORY}"/GRML ] ; then
907 log "Error: ${TEMPLATE_DIRECTORY}/GRML does not exist. Exiting."
908 eerror "Error: ${TEMPLATE_DIRECTORY}/GRML does not exist. Exiting." ; eend 1
912 mkdir -p "$BUILD_OUTPUT"/GRML/"${GRML_NAME}"/
913 cp -a ${TEMPLATE_DIRECTORY}/GRML/* "$BUILD_OUTPUT"/GRML/"${GRML_NAME}"/
915 # adjust boot splash information:
916 RELEASE_INFO="$GRML_NAME $VERSION - Release Codename $RELEASENAME"
917 RELEASE_INFO="$(cut_string 68 "$RELEASE_INFO")"
918 RELEASE_INFO="$(extend_string_end 68 "$RELEASE_INFO")"
920 if [ -r "$BUILD_OUTPUT"/GRML/"${GRML_NAME}"/grml-version ] ; then
921 sed -i "s/%RELEASE_INFO%/$GRML_NAME $VERSION - $RELEASENAME/" "$BUILD_OUTPUT"/GRML/"${GRML_NAME}"/grml-version
922 sed -i "s/%DATE%/$DATE/" "$BUILD_OUTPUT"/GRML/"${GRML_NAME}"/grml-version
925 # make sure the squashfs filename is set accordingly:
926 SQUASHFS_NAME="$GRML_NAME.squashfs"
928 if [ -n "$NO_BOOTID" ] ; then
929 log 'Skipping bootid feature as requested via $NO_BOOTID.'
930 einfo 'Skipping bootid feature as requested via $NO_BOOTID.'
932 [ -n "$BOOTID" ] || BOOTID="$(echo ${GRML_NAME}${VERSION} | tr -d ',./;\- ')"
933 [ -d "$BUILD_OUTPUT"/conf ] || mkdir "$BUILD_OUTPUT"/conf
934 einfo "Generating /conf/bootid.txt with entry ${BOOTID}."
935 log "Generating /conf/bootid.txt with entry ${BOOTID}."
936 echo "$BOOTID" > "$BUILD_OUTPUT"/conf/bootid.txt
940 # adjust all variables in the templates with the according distribution information
941 for file in "${BUILD_OUTPUT}"/boot/isolinux/*.cfg "${BUILD_OUTPUT}"/boot/isolinux/*.msg \
942 "${BUILD_OUTPUT}"/boot/grub/* ; do
943 if [ -r "${file}" ] && [ -f "${file}" ] ; then
944 sed -i "s/%ARCH%/$ARCH/g" "${file}"
945 sed -i "s/%DATE%/$DATE/g" "${file}"
946 sed -i "s/%DISTRI_INFO%/$DISTRI_INFO/g" "${file}"
947 sed -i "s/%DISTRI_NAME%/$DISTRI_NAME/g" "${file}"
948 sed -i "s/%DISTRI_SPLASH%/$DISTRI_SPLASH/g" "${file}"
949 sed -i "s/%GRML_NAME%/$GRML_NAME/g" "${file}"
950 sed -i "s/%SQUASHFS_NAME%/$SQUASHFS_NAME/g" "${file}"
951 sed -i "s/%RELEASE_INFO%/$RELEASE_INFO/g" "${file}"
952 sed -i "s/%SHORT_NAME%/$SHORT_NAME/g" "${file}"
953 sed -i "s/%VERSION%/$VERSION/g" "${file}"
955 [ -n "$DEFAULT_BOOTOPTIONS" ] && sed -i "s/ boot=live/ boot=live $DEFAULT_BOOTOPTIONS/" "${file}"
957 if [ -n "$NO_BOOTID" ] ; then
958 sed -i "s/ bootid=%BOOTID%//g" "${file}" # drop bootid bootoption
960 sed -i "s/%BOOTID%/$BOOTID/g" "${file}" # adjust bootid=... argument
965 for param in ARCH DATE DISTRI_INFO DISTRI_NAME DISTRI_SPLASH GRML_NAME SQUASHFS_NAME \
966 RELEASE_INFO SHORT_NAME VERSION ; do
967 for file in $(find "${BUILD_OUTPUT}" -name "*%$param%*") ; do
968 value="$(eval echo '$'"$param")"
969 mv ${file} ${file/\%${param}\%/$value}
973 # adjust bootsplash accordingly but make sure the string has the according lenght
974 SQUASHFS_NAME="$(cut_string 20 "$SQUASHFS_NAME")"
975 SQUASHFS_NAME="$(extend_string_end 20 "$SQUASHFS_NAME")"
976 for file in f4 f5 ; do
977 if [ -r "${BUILD_OUTPUT}/boot/isolinux/${file}" ] ; then
978 sed -i "s/%SQUASHFS_NAME%/$SQUASHFS_NAME/" "${BUILD_OUTPUT}/boot/isolinux/${file}"
979 sed -i "s/%SQUASHFS_NAME%/$SQUASHFS_NAME/" "${BUILD_OUTPUT}/boot/isolinux/${file}"
983 # generate addon list
984 rm -f "${BUILD_OUTPUT}/${ADDONS_LIST_FILE}"
985 for name in "${BUILD_OUTPUT}"/boot/isolinux/addon_*.cfg ; do
986 include_name=$(basename "$name")
987 echo "include $include_name" >> "${BUILD_OUTPUT}/${ADDONS_LIST_FILE}"
990 if ! [ -r "${BUILD_OUTPUT}/boot/isolinux/${DISTRI_NAME}.cfg" ] || [ "$DISTRI_NAME" = "grml" ] ; then
991 log "including grmlmain.cfg in ${BUILD_OUTPUT}/boot/isolinux/distri.cfg"
992 echo "include grmlmain.cfg" > "${BUILD_OUTPUT}/boot/isolinux/distri.cfg"
993 echo "include default.cfg" > "${BUILD_OUTPUT}/boot/isolinux/grmlmain.cfg"
994 echo "include menuoptions.cfg" >> "${BUILD_OUTPUT}/boot/isolinux/grmlmain.cfg"
995 echo "include grml.cfg" >> "${BUILD_OUTPUT}/boot/isolinux/grmlmain.cfg"
997 for f in "${BUILD_OUTPUT}"/boot/isolinux/submenu*.cfg ; do
998 echo "include $(basename $f)" >> "${BUILD_OUTPUT}/boot/isolinux/grmlmain.cfg"
1001 echo "include options.cfg" >> "${BUILD_OUTPUT}/boot/isolinux/grmlmain.cfg"
1002 if [ ! -n "$NO_ADDONS" ] ; then
1003 echo "include addons.cfg" >> "${BUILD_OUTPUT}/boot/isolinux/grmlmain.cfg"
1005 echo "include isoprompt.cfg" >> "${BUILD_OUTPUT}/boot/isolinux/grmlmain.cfg"
1006 echo "include hd.cfg" >> "${BUILD_OUTPUT}/boot/isolinux/grmlmain.cfg"
1007 echo "include hidden.cfg" >> "${BUILD_OUTPUT}/boot/isolinux/grmlmain.cfg"
1008 else # assume we are building a custom distribution:
1009 log "File ${BUILD_OUTPUT}/boot/isolinux/${DISTRI_NAME}.cfg found, using it."
1010 einfo "File ${BUILD_OUTPUT}/boot/isolinux/${DISTRI_NAME}.cfg found, using it."
1011 if grep -q "^include ${DISTRI_NAME}.cfg" "${BUILD_OUTPUT}/boot/isolinux/distri.cfg" ; then
1012 log "include for ${DISTRI_NAME}.cfg already present, nothing to do."
1014 einfo "include for ${DISTRI_NAME}.cfg already present, nothing to do."
1018 log "including ${DISTRI_NAME}.cfg in ${BUILD_OUTPUT}/boot/isolinux/distri.cfg"
1019 echo "include ${DISTRI_NAME}.cfg" > "${BUILD_OUTPUT}/boot/isolinux/distri.cfg"
1020 [ -n "$NO_ADDONS" ] || echo "include addons.cfg" >> "${BUILD_OUTPUT}/boot/isolinux/distri.cfg"
1024 # use old style console based isolinux method only if requested:
1025 if [[ "${ISOLINUX_METHOD}" == "console" ]] ; then
1026 log 'Using console based isolinux method as requested via $ISOLINUX_METHOD.'
1027 einfo 'Using console based isolinux method as requested via $ISOLINUX_METHOD.'
1028 if grep -q '^include console.cfg' "${BUILD_OUTPUT}/boot/isolinux/distri.cfg" ; then
1029 einfo "include for console.cfg already found, nothing to do."
1032 log "including console.cfg in ${BUILD_OUTPUT}/boot/isolinux/isolinux.cfg"
1033 einfo "including console.cfg in ${BUILD_OUTPUT}/boot/isolinux/isolinux.cfg"
1034 echo "include console.cfg" >> "${BUILD_OUTPUT}/boot/isolinux/isolinux.cfg"
1038 log 'Using graphical boot menu.'
1039 if grep -q '^include vesamenu.cfg' "${BUILD_OUTPUT}/boot/isolinux/isolinux.cfg" ; then
1040 log "include for vesamenu.cfg already found, nothing to do."
1042 log "including vesamenu.cfg in ${BUILD_OUTPUT}/boot/isolinux/isolinux.cfg"
1043 echo "include vesamenu.cfg" >> "${BUILD_OUTPUT}/boot/isolinux/isolinux.cfg"
1047 if [ -e "$BUILD_OUTPUT"/boot/addons/bsd4grml/boot.6 ]; then
1048 sed -i "s/%RELEASE_INFO%/$GRML_NAME $VERSION - $RELEASENAME/" "$BUILD_OUTPUT"/boot/addons/bsd4grml/boot.6
1051 DPKG_LIST="/var/log/fai/$HOSTNAME/last/dpkg.list" # the dpkg --list output of the chroot
1052 if ! [ -r "$DPKG_LIST" ] ; then
1053 ewarn "$DPKG_LIST could not be read, ignoring to store package information on ISO therefore."
1055 einfo "Storing package list information as /GRML/${GRML_NAME}/packages.txt on ISO."
1056 cp "$DPKG_LIST" "${BUILD_OUTPUT}"/GRML/"${GRML_NAME}"/packages.txt
1060 # autostart for Windows:
1061 if [ -d "${TEMPLATE_DIRECTORY}/windows/autostart/" ] ; then
1062 cp ${TEMPLATE_DIRECTORY}/windows/autostart/* "$BUILD_OUTPUT"/
1065 FORCE_ISO_REBUILD=true
1066 einfo "Finished execution of stage 'boot'" ; eend 0
1070 log 'Error: Unsupported ARCH, sorry. Want to support it? Contribute!'
1071 eerror 'Error: Unsupported ARCH, sorry. Want to support it? Contribute!' ; eend 1
1075 # support installation of local files into the chroot/ISO
1076 if [ -n "$CHROOT_INSTALL" ] ; then
1077 if ! [ -d "$CHROOT_INSTALL" ] ; then
1078 log "Configuration variable \$CHROOT_INSTALL is set but not a directory; ignoring"
1079 ewarn "Configuration variable \$CHROOT_INSTALL is set but not a directory; ignoring"
1081 log "Copying local files to chroot as requested via \$CHROOT_INSTALL"
1082 einfo "Copying local files to chroot as requested via \$CHROOT_INSTALL"
1083 rsync -avz --inplace "$CHROOT_INSTALL"/ "$CHROOT_OUTPUT/"
1085 einfo "Make sure to run squashfs stage, otherwise your local files won't be part of the ISO."
1086 FORCE_ISO_REBUILD=true
1090 if [ -f "$BUILD_OUTPUT"/live/${GRML_NAME}.squashfs -a -z "$UPDATE" -a -z "$BUILD_ONLY" -a -z "$BUILD_DIRTY" ] ; then
1091 log "Skipping stage 'squashfs' as $BUILD_OUTPUT/live exists already."
1092 ewarn "Skipping stage 'squashfs' as $BUILD_OUTPUT/live exists already." ; eend 0
1093 elif [ -n "$SKIP_MKSQUASHFS" ] ; then
1094 log "Skipping stage 'squashfs' as requested via option -q or -N"
1095 ewarn "Skipping stage 'squashfs' as requested via option -q or -N" ; eend 0
1097 mkdir -p "$BUILD_OUTPUT"/live/"${GRML_NAME}"/
1098 # make sure we don't leave (even an empty) base.tgz:
1099 [ -f "$CHROOT_OUTPUT/base.tgz" ] && rm -f "$CHROOT_OUTPUT/base.tgz"
1101 # if unconfigured default to squashfs-tools' mksquashfs binary
1102 if [ -z "$SQUASHFS_BINARY" ] ; then
1103 SQUASHFS_BINARY='mksquashfs'
1106 if which "$SQUASHFS_BINARY" >/dev/null 2>&1 ; then
1107 log "Using mksquashfs binary ${SQUASHFS_BINARY}"
1108 einfo "Using mksquashfs binary ${SQUASHFS_BINARY}" ; eend 0
1110 log "Error: mksquashfs binary ($SQUASHFS_BINARY) not found. Exiting."
1111 eerror "Error: mksquashfs binary ($SQUASHFS_BINARY) not found. Exiting." ; eend 1
1115 # use sane defaults if $SQUASHFS_OPTIONS isn't set
1116 if [ -z "$SQUASHFS_OPTIONS" ] ; then
1117 # use blocksize 256k as this gives best result with regards to time + compression
1118 SQUASHFS_OPTIONS="-b 256k"
1120 # set lzma/xz compression by default, unless -z option has been specified on command line
1121 if [ -z "$SQUASHFS_ZLIB" ] ; then
1122 SQUASHFS_OPTIONS="$SQUASHFS_OPTIONS -comp xz"
1124 SQUASHFS_OPTIONS="$SQUASHFS_OPTIONS -comp gzip"
1128 # support exclusion of files via exclude-file:
1129 if [ -n "$SQUASHFS_EXCLUDES_FILE" -a "$SQUASHFS_EXCLUDES_FILE" ] ; then
1130 SQUASHFS_OPTIONS="$SQUASHFS_OPTIONS -ef $SQUASHFS_EXCLUDES_FILE -wildcards"
1133 # get rid of unnecessary files when building grml-small for final release:
1134 if echo "$CLASSES" | grep -q GRML_SMALL ; then
1135 SQUASHFS_OPTIONS="$SQUASHFS_OPTIONS -e initrd.img* vmlinuz*"
1139 SQUASHFS_STDERR="$(mktemp -t grml-live.XXXXXX)"
1141 # informational stuff
1142 [ -n "$SQUASHFS_OPTIONS" ] && SQUASHFS_INFO_MSG="$SQUASHFS_OPTIONS"
1143 [ -n "$SQUASHFS_INFO_MSG" ] && SQUASHFS_INFO_MSG="using options: $SQUASHFS_INFO_MSG"
1144 einfo "Squashfs build information: running binary $SQUASHFS_BINARY $SQUASHFS_INFO_MSG"
1146 log "$SQUASHFS_BINARY $CHROOT_OUTPUT/ $BUILD_OUTPUT/live/${GRML_NAME}/${GRML_NAME}.squashfs -noappend $SQUASHFS_OPTIONS"
1148 if $SQUASHFS_BINARY $CHROOT_OUTPUT/ $BUILD_OUTPUT/live/"${GRML_NAME}"/"${GRML_NAME}".squashfs \
1149 -noappend $SQUASHFS_OPTIONS 2>"${SQUASHFS_STDERR}" ; then
1150 echo "${GRML_NAME}.squashfs" > $BUILD_OUTPUT/live/"${GRML_NAME}"/filesystem.module
1151 log "Finished execution of stage 'squashfs' [$(date)]"
1152 einfo "Finished execution of stage 'squashfs'" ; eend 0
1154 log "Error: there was a critical error executing stage 'squashfs' [$(date)]:"
1155 log "$(cat $SQUASHFS_STDERR)"
1156 eerror "Error: there was a critical error executing stage 'squashfs':"
1157 cat "${SQUASHFS_STDERR}"
1162 FORCE_ISO_REBUILD=true
1165 # create md5sum file:
1166 if [ -z "$BOOTSTRAP_ONLY" ] ; then
1167 ( cd $BUILD_OUTPUT/GRML/"${GRML_NAME}" &&
1168 find .. -type f -not -name md5sums -not -name isolinux.bin -exec md5sum {} \; > md5sums )
1172 # ISO_OUTPUT - mkisofs {{{
1173 [ -n "$ISO_OUTPUT" ] || ISO_OUTPUT="$OUTPUT/grml_isos"
1174 [ -n "$ISO_NAME" ] || ISO_NAME="${GRML_NAME}_${VERSION}.iso"
1176 if [ "$BOOT_METHOD" = "isolinux" ] ; then
1177 BOOT_ARGS="-no-emul-boot -boot-load-size 4 -boot-info-table -b boot/isolinux/isolinux.bin -c boot/isolinux/boot.cat"
1178 elif [ "$BOOT_METHOD" = "grub2" ] ; then
1179 BOOT_ARGS="-no-emul-boot -boot-load-size 4 -b boot/grub/toriboot.bin"
1182 # Just until http://bts.grml.org/grml/issue945 has been resolved.
1183 # HYBRID_METHOD defaults to manifold, so make sure the default works OOTB.
1184 if [[ $BOOT_METHOD != isolinux && ($HYBRID_METHOD = isohybrid || $HYBRID_METHOD = manifold) ]]; then
1185 log "Setting HYBRID_METHOD to grub2 as hybrid mode does not work with isohybrid yet."
1186 ewarn "Setting HYBRID_METHOD to grub2 as hybrid mode does not work with isohybrid yet."
1187 HYBRID_METHOD='grub2'
1191 if [ -f "${ISO_OUTPUT}/${ISO_NAME}" -a -z "$UPDATE" -a -z "$BUILD_ONLY" -a -z "$BUILD_DIRTY" -a "$FORCE_ISO_REBUILD" = "false" ] ; then
1192 log "Skipping stage 'iso build' as $ISO_OUTPUT/${ISO_NAME} exists already."
1193 ewarn "Skipping stage 'iso build' as $ISO_OUTPUT/${ISO_NAME} exists already." ; eend 0
1194 elif [ -n "$SKIP_MKISOFS" ] ; then
1195 log "Skipping stage 'iso build' as requested via option -n or -N"
1196 ewarn "Skipping stage 'iso build' as requested via option -n or -N" ; eend 0
1198 mkdir -p "$ISO_OUTPUT" || bailout 6 "Problem with creating $ISO_OUTPUT for stage 'iso build'"
1200 if $FORCE_ISO_REBUILD && ! [ -f "${ISO_OUTPUT}/${ISO_NAME}" ] ; then
1201 log "Forcing rebuild of ISO because files on ISO have been modified."
1202 einfo "Forcing rebuild of ISO because files on ISO have been modified."
1205 # support xorriso as well mkisofs and genisoimage
1206 if which xorriso >/dev/null 2>&1 ; then
1207 MKISOFS='xorriso -as mkisofs'
1208 elif which mkisofs >/dev/null 2>&1; then
1210 elif which genisoimage >/dev/null 2>&1; then
1211 MKISOFS='genisoimage'
1213 log "Error: neither xorriso nor mkisofs nor genisoimage available - can not create ISO."
1214 eerror "Error: neither xorriso nor mkisofs nor genisoimage available - can not create ISO." ; eend 1
1218 einfo "Using ${MKISOFS} to build ISO." ; eend 0
1219 case "${ARCH}-${MKISOFS}" in
1220 # using -eltorito-alt-boot is limited to xorriso for now
1224 if ! dpkg --compare-versions $(dpkg-query -W -f='${Version}\n' xorriso 2>/dev/null) gt-nl 1.1.6-1 ; then
1225 log "Disabling (U)EFI boot support because xorriso version is too old."
1226 ewarn "Disabling (U)EFI boot support because xorriso version is too old." ; eend 0
1228 if [ -r "${BUILD_OUTPUT}"/boot/efi.img ] ; then
1229 einfo "Enabling (U)EFI boot."
1230 log "Enabling (U)EFI boot."
1231 BOOT_ARGS="$BOOT_ARGS -boot-info-table -eltorito-alt-boot -e boot/efi.img -no-emul-boot"
1234 log "Disabling (U)EFI boot support because /boot/efi.img is missing."
1235 ewarn "Disabling (U)EFI boot support because /boot/efi.img is missing." ; eend 0
1244 if cd "$BUILD_OUTPUT" ; then
1245 if [ "$BOOT_METHOD" = "grub2" ]; then
1246 # make a 2048-byte bootsector for El Torito
1247 dd if=/dev/zero of=boot/grub/toriboot.bin bs=512 count=4 2>/dev/null
1248 # those are in 2048-byte sectors, so 1 16 matches 4 63 below
1249 echo 1 16 | mksh /usr/share/grml-live/scripts/bootgrub.mksh -B 11 | \
1250 dd of=boot/grub/toriboot.bin conv=notrunc 2>/dev/null
1252 log "$MKISOFS -V '${GRML_NAME} ${VERSION}' -publisher 'grml-live | grml.org' -l -r -J $BOOT_ARGS -o ${ISO_OUTPUT}/${ISO_NAME} ."
1253 $MKISOFS -V "${GRML_NAME} ${VERSION}" -publisher 'grml-live | grml.org' \
1254 -l -r -J $BOOT_ARGS -no-pad \
1255 -o "${ISO_OUTPUT}/${ISO_NAME}" . ; RC=$?
1256 # both of these need core.img there, so it’s easier to write it here
1257 if [ "$BOOT_METHOD" = "grub2" ] || [ "$HYBRID_METHOD" = "grub2" ]; then
1258 # must be <= 30720 bytes
1259 dd if=boot/grub/core.img of="${ISO_OUTPUT}/${ISO_NAME}" \
1260 conv=notrunc bs=512 seek=4 2>/dev/null
1263 # pad the output ISO to multiples of 256 KiB for partition table support
1264 siz=$($getfilesize "${ISO_OUTPUT}/${ISO_NAME}")
1265 cyls=$((siz / 512 / 32 / 16 + 1)) # C=$cyls H=16 S=32
1266 siz=$((cyls * 16 * 32 * 512)) # size after padding
1267 dd if=/dev/zero bs=1 count=1 seek=$((siz - 1)) \
1268 of="${ISO_OUTPUT}/${ISO_NAME}" 2>/dev/null
1270 # support disabling hybrid ISO image
1271 if [ "$HYBRID_METHOD" = "disable" ] ; then\
1272 log "Skipping creation of hybrid ISO file as requested via HYBRID_METHOD=disable"
1273 einfo "Skipping creation of hybrid ISO file as requested via HYBRID_METHOD=disable"
1275 elif [ "$HYBRID_METHOD" = "manifold" ] ; then
1276 # isoinfo is part of both mkisofs and genisoimage so we're good
1277 bootoff=$(isoinfo -l -i "${ISO_OUTPUT}/${ISO_NAME}" | \
1278 sed -n '/^.*\[ *\([0-9]*\)[] ].* ISOLINUX.BIN[;1]* *$/s//\1/p')
1279 if ! [ -r boot/grub/core.img ] ; then
1280 ewarn "boot/grub/core.img not found, not creating manifold boot ISO file"
1281 elif [ "${bootoff:-0}" -lt 1 ] ; then
1282 ewarn "isolinux.bin not found on the ISO file, disabling manifold boot"
1284 log "Creating hybrid ISO file with manifold method"
1285 einfo "Creating hybrid ISO file with manifold method"
1286 if [ "$HYBRID_METHOD" = "grub2" ] ; then
1287 # 512 bytes: MBR, partition table, load GRUB 2
1288 echo 4 63 | mksh /usr/share/grml-live/scripts/bootgrub.mksh -A -M 4:0x96 -g $cyls:16:32
1290 # read only one but 2048-byte sized (scale: << 2) sector
1291 echo $bootoff $bootoff | \
1292 mksh /usr/share/grml-live/scripts/bootilnx.mksh -A -M 4:0x96 -g $cyls:16:32 -S 2
1293 fi | dd of="${ISO_OUTPUT}/${ISO_NAME}" conv=notrunc 2>/dev/null
1296 # use isohybrid as default
1298 if ! which isohybrid >/dev/null 2>&1 ; then
1299 bailout 12 "isohybrid binary not found - please install syslinux/syslinux-common"
1301 log "Creating hybrid ISO file with isohybrid method"
1302 einfo "Creating hybrid ISO file with isohybrid method"
1303 # Notes for consideration:
1304 # "-entry 4 -type 1c"
1305 # * using 4 as the partition number is supposed to help with BIOSes
1306 # that only support USB-Zip boot
1307 # * using 1c (i.e. hidden FAT32 LBA), instead of the default 0x17
1308 # (hidden NTFS, IIRC), as the partition type is sometimes needed
1309 # to get the BIOS even look at the partition created by isohybrid
1310 if isohybrid --help | grep -q -- --uefi ; then
1311 einfo "Detected uefi support for isohybrid, enabling."
1312 ISOHYBRID_OPTIONS=--uefi
1315 log "isohybrid $ISOHYBRID_OPTIONS ${ISO_OUTPUT}/${ISO_NAME}"
1316 isohybrid $ISOHYBRID_OPTIONS "${ISO_OUTPUT}/${ISO_NAME}"
1321 # generate md5sum and sha1sum of ISO if we are using class 'RELEASE':
1322 case $CLASSES in *RELEASE*)
1325 if cd $ISO_OUTPUT ; then
1326 md5sum ${ISO_NAME} > ${ISO_NAME}.md5 && \
1327 touch -r ${ISO_NAME} ${ISO_NAME}.md5
1328 sha1sum ${ISO_NAME} > ${ISO_NAME}.sha1 && \
1329 touch -r ${ISO_NAME} ${ISO_NAME}.sha1
1338 if [ "$RC" = 0 ] ; then
1339 log "Finished execution of stage 'iso build' [$(date)]"
1340 einfo "Finished execution of stage 'iso build'" ; eend 0
1342 log "Error: there was a critical error ($RC) executing stage 'iso build' [$(date)]"
1343 eerror "Error: there was a critical error executing stage 'iso build'" ; eend 1
1349 # netboot package {{{
1350 create_netbootpackage() {
1351 local OUTPUT_FILE="${NETBOOT}/grml_netboot_package_${GRML_NAME}_${VERSION}.tar.bz2"
1353 if [ -f "${OUTPUT_FILE}" -a -z "$UPDATE" -a -z "$BUILD_ONLY" -a -z "$BUILD_DIRTY" ] ; then
1354 log "Skipping stage 'netboot' as $OUTPUT_FILE exists already."
1355 ewarn "Skipping stage 'netboot' as $OUTPUT_FILE exists already." ; eend 0
1357 elif [ -n "$SKIP_NETBOOT" ] ; then
1358 log "Skipping stage 'netboot' as requested via option -Q"
1359 ewarn "Skipping stage 'netboot' as requested via option -Q" ; eend 0
1365 if ! [ -r "${CHROOT}/usr/lib/syslinux/pxelinux.0" ] ; then
1366 ewarn "File /usr/lib/syslinux/pxelinux.0 not found in build chroot." ; eend 0
1368 einfo "Install syslinux[-common] package in chroot to get a netboot package."
1373 local OUTPUTDIR="${NETBOOT}/build_tmp"
1374 local WORKING_DIR="${OUTPUTDIR}/grml_netboot_package_${GRML_NAME}_${VERSION}/tftpboot/"
1376 mkdir -p "$WORKING_DIR"
1378 cp "${CHROOT_OUTPUT}"/boot/vmlinuz-* "$WORKING_DIR"/vmlinuz
1379 cp "${CHROOT_OUTPUT}"/boot/initrd.img-* "$WORKING_DIR"/initrd.img
1380 cp "${CHROOT_OUTPUT}"/usr/lib/syslinux/pxelinux.0 "${WORKING_DIR}/pxelinux.0"
1382 mkdir -p "${WORKING_DIR}/pxelinux.cfg"
1383 if [ -r "${BUILD_OUTPUT}/boot/isolinux/netboot.cfg" ] ; then
1384 cp "${BUILD_OUTPUT}/boot/isolinux/netboot.cfg" "${WORKING_DIR}/pxelinux.cfg/default"
1386 ewarn "File ${BUILD_OUTPUT}/boot/isolinux/netboot.cfg not found." ; eend 0
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