add support for genisoimage
[grml-live.git] / grml-live
1 #!/bin/bash
2 # Filename:      grml-live
3 # Purpose:       build process script for generating a (grml based) Linux Live-ISO
4 # Authors:       grml-team (grml.org),
5 #                (c) Michael Prokop <mika@grml.org>,
6 #                (c) Thorsten Glaser <tg@mirbsd.org>
7 # Bug-Reports:   see http://grml.org/bugs/
8 # License:       This file is licensed under the GPL v2 or any later version.
9 ################################################################################
10
11 # read configuration files, set some misc variables {{{
12
13 export LANG=C
14 export LC_ALL=C
15
16 # define function getfilesize before "set -e"
17 if stat --help >/dev/null 2>&1; then
18         getfilesize='stat -c %s'        # GNU stat
19 else
20         getfilesize='stat -f %z'        # BSD stat
21 fi
22
23 # exit on any error:
24 set -e
25
26 GRML_LIVE_VERSION='0.9.20'
27 PN="$(basename $0)"
28 CMDLINE="$0 $@"
29 ISO_DATE="$(date +%Y-%m-%d)"
30 SOURCES_LIST_FILE='/etc/grml/fai/apt/sources.list'
31
32 # usage information {{{
33 usage()
34 {
35   echo "
36 $PN - build process script for generating a (grml based) Linux Live-ISO
37
38 Usage: $PN [options, see as follows]
39
40    -a <architecture>       architecture; available values: i386 and amd64
41    -b                      build the ISO without updating the chroot via FAI
42    -B                      build the ISO without touching the chroot (skips cleanup)
43    -c <classe[s]>          classes to be used for building the ISO via FAI
44    -C <configfile>         configuration file for grml-live
45    -F                      force execution without prompting
46    -g <grml_name>]         set the grml flavour name
47    -h                      display short usage information and exit
48    -i <iso_name>           name of ISO
49    -o <output_directory>   main output directory of the build process
50    -q                      skip mksquashfs
51    -r <release_name<       release name
52    -s <suite>              Debian suite; values: etch, lenny, squeeze, sid
53    -t <template_directory> place of the templates
54    -u                      update existing chroot instead of rebuilding it from scratch
55    -v <version_number>     specify version number of the release
56    -V                      increase verbosity in the build process
57    -z                      use ZLIB instead of LZMA compression (depends on
58                            squashfs-tools version)
59
60 Usage examples:
61
62     $PN
63     $PN -c GRMLBASE,GRML_MEDIUM,I386 -o /dev/shm/grml
64     $PN -c GRMLBASE,GRML_SMALL,REMOVE_DOCS,I386 -g grml-small -v 1.0
65     $PN -c GRMLBASE,GRML_FULL,I386 -i grml_0.0-1.iso -v 0.0-1
66     $PN -c GRMLBASE,GRML_FULL,I386 -s sid -V -r 'grml-live rocks'
67
68 More details: man grml-live + /usr/share/doc/grml-live/grml-live.html
69               http://grml.org/grml-live/
70
71 Please send your bug reports and feedback to the grml-team: http://grml.org/bugs/
72 "
73 }
74
75 # make sure it's possible to get usage information without being
76 # root or actually executing the script
77 if [ "$1" = '-h' -o "$1" = '--help' ] ; then
78    usage
79    [ "$(id -u 2>/dev/null)" != 0 ] && echo "Please notice that this script requires root permissions."
80    exit 0
81 fi
82 # }}}
83
84 # we need root permissions for the build-process:
85 if [ "$(id -u 2>/dev/null)" != 0 ] ; then
86    echo "Error: please run this script with uid 0 (root)." >&2
87    exit 1
88 fi
89
90 if [ -r /var/run/fai/FAI_INSTALLATION_IN_PROGRESS ] ; then
91    echo "/usr/sbin/fai already running or was aborted before.">&2
92    echo "You may remove /var/run/fai/FAI_INSTALLATION_IN_PROGRESS and try again.">&2
93    exit 1
94 fi
95
96 # see #449236
97 if [ -r /var/run/fai/fai_softupdate_is_running ] ; then
98    echo "/usr/sbin/fai softupdate already running or was aborted before.">&2
99    echo "You may remove /var/run/fai/fai_softupdate_is_running and try again.">&2
100    exit 1
101 fi
102
103 # make sure they are not set by default
104 VERBOSE=''
105 FORCE=''
106 UPDATE=''
107 BUILD_ONLY=''
108 BUILD_DIRTY=''
109 HOSTNAME=''
110
111 if [ -r /etc/grml/lsb-functions ] ; then
112    . /etc/grml/lsb-functions
113 else
114    einfo()  { echo "  [*] $*" ;}
115    eerror() { echo "  [!] $*">&2 ;}
116    ewarn()  { echo "  [x] $*" ;}
117    eend()   { return 0 ;}
118 fi
119
120 # source main configuration file:
121 LIVE_CONF=/etc/grml/grml-live.conf
122 . $LIVE_CONF
123
124 # }}}
125
126 # clean exit {{{
127 bailout() {
128   rm -f /var/run/fai/fai_softupdate_is_running \
129         /var/run/fai/FAI_INSTALLATION_IN_PROGRESS
130   [ -n "$MIRROR_DIRECTORY" ] && umount "${CHROOT_OUTPUT}/${MIRROR_DIRECTORY}"
131   [ -n "$1" ] && EXIT="$1" || EXIT="1"
132   [ -n "$2" ] && eerror "$2">&2
133   log "------------------------------------------------------------------------------"
134   exit "$EXIT"
135 }
136 trap bailout 1 2 3 3 6 9 14 15
137 # }}}
138
139 # check for important variables {{{
140 [ -n "$GRML_FAI_CONFIG" ] || GRML_FAI_CONFIG=/etc/grml/fai
141 [ -n "$HOSTNAME" ] || HOSTNAME=grml
142 [ -n "$USERNAME" ] || USERNAME=grml
143 [ -n "$CLASSES" ]  || CLASSES="GRML,I386"
144 [ -n "$BOOT_METHOD" ] || BOOT_METHOD='isolinux'
145 [ -n "$OUTPUT" ] || bailout 1 "${PN}: \$OUTPUT not specified. Please adjust $LIVE_CONF. Exiting."
146
147 [ -n "$VERSION" ]  || VERSION="0.0.1"
148 [ -n "$RELEASENAME" ] || RELEASENAME="grml-live rocks"
149 [ -n "$GRML_NAME" ] || GRML_NAME='grml'
150
151 # logfile:
152 if [ -z "$LOGFILE" ] ; then
153    LOGFILE=/var/log/grml-live.log
154 fi
155 touch $LOGFILE
156 chown root:adm $LOGFILE
157 chmod 664 $LOGFILE
158
159 NFSROOT_CONF=/etc/grml/fai/make-fai-nfsroot.conf
160
161 # }}}
162
163 # some important functions {{{
164
165 # log output:
166 # usage: log "string to log"
167 log() { echo "$*" >> $LOGFILE ; }
168
169 # cut string at character number int = $1
170 # usage: cut_string 5 "1234567890" will output "12345"
171 cut_string() {
172   [ -n "$2" ] || return 1
173   echo "$2" | head -c "$1"; echo -ne "\n"
174 }
175
176 # prepend int = $1 spaces before string = $2
177 # usage: extend_string_begin 5 "123" will output "  123"
178 extend_string_begin() {
179   [ -n "$2" ] || return 1
180   local COUNT="$(echo $2 | wc -c)"
181   local FILL="$(expr $COUNT - $1)"
182   while [ "$FILL" -gt 1 ] ; do
183     echo -n " "
184     local FILL=$(expr $FILL - 1)
185   done
186   while [ "$FILL" -lt 1 ] ; do
187     echo -n " "
188     local FILL=$(expr $FILL + 1)
189   done
190   echo "$2" | head -c "$1"; echo -ne "\n"
191 }
192
193 # append int = $1 spaces to string = $2
194 # usage: extend_string_begin 5 "123" will output "123  "
195 extend_string_end() {
196   [ -n "$2" ] || return 1
197   echo -n "$2" | head -c "$1"
198   local COUNT="$(echo $2 | wc -c)"
199   local FILL="$(expr $COUNT - $1)"
200   while [ "$FILL" -gt 1 ] ; do
201     echo -n " "
202     local FILL=$(expr $FILL - 1)
203   done
204   while [ "$FILL" -lt 1 ] ; do
205     echo -n " "
206     local FILL=$(expr $FILL + 1)
207   done
208   echo -ne "\n"
209 }
210 # }}}
211
212 # read local (non-packaged) configuration {{{
213 LOCAL_CONFIG=/etc/grml/grml-live.local
214 if [ -r "$LOCAL_CONFIG" ] ; then
215    log "Sourcing $LOCAL_CONFIG"
216    . $LOCAL_CONFIG
217 else
218    log "No $LOCAL_CONFIG found, not sourcing it"
219    LOCAL_CONFIG=''
220 fi
221 # }}}
222
223 # command line parsing {{{
224 while getopts "a:C:c:g:i:o:r:s:t:v:bBFuqVz" opt; do
225   case "$opt" in
226     a) ARCH="$OPTARG" ;;
227     b) BUILD_ONLY=1 ;;
228     B) BUILD_DIRTY=1 ;;
229     c) CLASSES="$OPTARG" ;;
230     C) CONFIG="$OPTARG" ;;
231     g) GRML_NAME="$OPTARG" ;;
232     i) ISO_NAME="$OPTARG" ;;
233     o) OUTPUT="$OPTARG"
234        CHROOT_OUTPUT="$OUTPUT/grml_chroot"
235        BUILD_OUTPUT="$OUTPUT/grml_cd"
236        ISO_OUTPUT="$OUTPUT/grml_isos"
237        ;;
238     q) SKIP_MKSQUASHFS=1 ;;
239     r) RELEASENAME="$OPTARG" ;;
240     s) SUITE="$OPTARG" ;;
241     t) TEMPLATE_DIRECTORY="$OPTARG";;
242     v) VERSION="$OPTARG" ;;
243     F) FORCE=1 ;;
244     u) UPDATE=1 ;;
245     V) VERBOSE="-v" ;;
246     z) SQUASHFS_ZLIB="-nolzma" ;;
247     ?) echo "invalid option -$OPTARG" >&2; bailout 1 ;;
248   esac
249 done
250 shift $(($OPTIND - 1))  # set ARGV to the first not parsed commandline parameter
251 # }}}
252
253 # some misc checks before executing FAI {{{
254 [ -n "$CLASSES" ] || bailout 1 "Error: \$CLASSES unset, please set it in $LIVE_CONF or
255 specify it on the command line using the -c option."
256 [ -n "$OUTPUT" ] || bailout 1 "Error: \$OUTPUT unset, please set it in $LIVE_CONF or
257 specify it on the command line using the -o option."
258
259 # trim characters that are known to cause problems inside $GRML_NAME;
260 # for example isolinux does not like '-' inside the directory name
261 [ -n "$GRML_NAME" ] && export SHORT_GRML_NAME="$(echo $GRML_NAME | tr -d ',./;\- ')"
262
263 # export variables to have them available in fai scripts:
264 [ -n "$GRML_NAME" ]   && export GRML_NAME="$GRML_NAME"
265 [ -n "$RELEASENAME" ] && export RELEASENAME="$RELEASENAME"
266 # }}}
267
268 # clean/zero grml-live logfile {{{
269 if [ -n "$ZERO_LOGFILE" ] ; then
270    echo -n > $LOGFILE
271 fi
272 # }}}
273
274 # clean/zero/remove old FAI directory {{{
275 if [ -n "$ZERO_FAI_LOGFILE" ] ; then
276    if [ -d /var/log/fai/"$HOSTNAME" ] ; then
277       rm -rf /var/log/fai/"$HOSTNAME"/"$(readlink /var/log/fai/"$HOSTNAME"/last)"
278       rm -rf /var/log/fai/"$HOSTNAME"/"$(readlink /var/log/fai/"$HOSTNAME"/last-dirinstall)"
279       rm -rf /var/log/fai/"$HOSTNAME"/"$(readlink /var/log/fai/"$HOSTNAME"/last-softupdate)"
280       rm -f /var/log/fai/"$HOSTNAME"/last \
281             /var/log/fai/"$HOSTNAME"/last-dirinstall \
282             /var/log/fai/"$HOSTNAME"/last-softupdate
283    fi
284 fi
285 # }}}
286
287 # ask user whether the setup is ok {{{
288 if [ -z "$FORCE" ] ; then
289    echo
290    echo "${PN} [${GRML_LIVE_VERSION}]: check your configuration (or use -F to force execution):"
291    echo
292    echo "  FAI classes:       $CLASSES"
293    [ -r "$LOCAL_CONFIG" ]       && echo "  local config:      /etc/grml/grml-live.local"
294    [ -n "$CONFIG" ]             && echo "  configuration:     $CONFIG"
295    echo "  main directory:    $OUTPUT"
296    [ -n "$CHROOT_OUTPUT" ]      && echo "  chroot target:     $CHROOT_OUTPUT"
297    [ -n "$BUILD_OUTPUT" ]       && echo "  build target:      $BUILD_OUTPUT"
298    [ -n "$ISO_OUTPUT" ]         && echo "  ISO target:        $ISO_OUTPUT"
299    [ -n "$GRML_NAME" ]          && echo "  grml name:         $GRML_NAME"
300    [ -n "$RELEASENAME" ]        && echo "  release name:      $RELEASENAME"
301    [ -n "$VERSION" ]            && echo "  grml version:      $VERSION"
302    [ -n "$SUITE" ]              && echo "  Debian suite:      $SUITE"
303    [ -n "$ARCH" ]               && echo "  Architecture:      $ARCH"
304    [ -n "$BOOT_METHOD" ]        && echo "  Boot method:       $BOOT_METHOD"
305    [ -n "$TEMPLATE_DIRECTORY" ] && echo "  Template files:    $TEMPLATE_DIRECTORY"
306    [ -n "$FAI_ARGS" ]           && echo "  additional arguments for FAI: $FAI_ARGS"
307    [ -n "$LOGFILE" ]            && echo "  Logging to file:   $LOGFILE"
308    [ -n "$SQUASHFS_ZLIB" ]      && echo "  Using ZLIB (instead of LZMA) compression."
309    [ -n "$SQUASHFS_OPTIONS" ]   && echo "  Using SQUASHFS_OPTIONS ${SQUASHFS_OPTIONS}"
310    [ -n "$VERBOSE" ]            && echo "  Using VERBOSE mode."
311    [ -n "$UPDATE" ]             && echo "  Executing UPDATE instead of fresh installation."
312    [ -n "$SKIP_MKSQUASHFS" ]    && echo "  Skipping creation of SQUASHFS file."
313    [ -n "$BUILD_ONLY" ]         && echo "  Executing BUILD_ONLY instead of fresh installation or UPDATE."
314    [ -n "$BUILD_DIRTY" ]        && echo "  Executing BUILD_DIRTY to leave chroot untouched."
315    echo
316    echo -n "Is this ok for you? [y/N] "
317    read a
318    if ! [ "$a" = 'y' -o "$a" = 'Y' ] ; then
319       bailout 1 "Exiting as requested."
320    fi
321    echo
322 fi
323
324 if [ -n "$CONFIG" ] ; then
325    if ! [ -f "$CONFIG" ] ; then
326       log "Sorry, $CONFIG could not be read. Exiting. [$(date)]"
327       eerror "Sorry, $CONFIG could not be read. Exiting."
328       bailout 1
329    else
330       log "Sourcing $CONFIG"
331       . $CONFIG
332    fi
333 fi
334
335 start_seconds=$(cut -d . -f 1 /proc/uptime)
336 log "------------------------------------------------------------------------------"
337 log "Starting grml-live [${GRML_LIVE_VERSION}] run on $(date)"
338 log "Executed grml-live command line:"
339 log "$CMDLINE"
340
341 einfo "Logging actions to logfile $LOGFILE"
342 # }}}
343
344 # on-the-fly configuration {{{
345 if [ -n "$MIRROR_DIRECTORY" ] ; then
346    if ! [ -d "$MIRROR_DIRECTORY/debian" ] ; then
347       log "Sorry, $MIRROR_DIRECTORY/debian does not seem to exist. Exiting. [$(date)]"
348       eerror "Sorry, $MIRROR_DIRECTORY/debian does not seem to exist. Exiting."
349       bailout 1
350    fi
351    cat > "$SOURCES_LIST_FILE" << EOF
352 # NOTE: This file is *NOT* meant for manual customisation! This file is
353 # modified by grml-live and any changes might be overriden.
354 # You might consider using GRML_LIVE_SOURCES in /etc/grml/grml-live.conf*
355 # and using /etc/grml/fai/files/etc/apt instead!'
356 EOF
357    echo "$MIRROR_SOURCES" >> "$SOURCES_LIST_FILE"
358    if [ -n "$GRML_LIVE_SOURCES" ] ; then
359       echo "$GRML_LIVE_SOURCES" >> "$SOURCES_LIST_FILE"
360    fi
361 elif [ -n "$GRML_LIVE_SOURCES" ] ; then
362    cat > "$SOURCES_LIST_FILE" << EOF
363 # NOTE: This file is *NOT* meant for manual customisation! This file is
364 # modified by grml-live and any changes might be overriden.
365 # You might consider using GRML_LIVE_SOURCES in /etc/grml/grml-live.conf*
366 # and using /etc/grml/fai/files/etc/apt instead!'
367 EOF
368    echo "$GRML_LIVE_SOURCES" >> "$SOURCES_LIST_FILE"
369 fi
370
371 if [ -n "$FAI_DEBOOTSTRAP" ] ; then
372    sed "s#^FAI_DEBOOTSTRAP=.*#FAI_DEBOOTSTRAP=\"$FAI_DEBOOTSTRAP\"#" "$NFSROOT_CONF" | sponge "$NFSROOT_CONF"
373 fi
374
375 # does this suck? YES!
376 if [ -n "$SUITE" ] ; then
377
378    # /usr/share/debootstrap/scripts/unstable does not exist, instead use 'sid':
379    case $SUITE in
380       unstable) SUITE='sid' ;;
381       # make sure that we *NEVER* write any broken suite name to sources.list,
382       # otherwise we won't be able to adjust it one next (correct) execution
383       stable)   ;;
384       testing)  ;;
385       etch)     ;;
386       lenny)    ;;
387       squeeze)  ;;
388       sid)      ;;
389       *) echo "Sorry, $SUITE is not a valid Debian suite, exiting.">&2; bailout 1 ;;
390    esac
391
392    DIST=" etch\| stable\| lenny\| squeeze\| testing\| sid\| unstable"
393    sed "s/\(^deb .\+\)\([ \t]*\)\($DIST\)\([ \t]*\)\(main \)/\1 \2$SUITE\4\5/" "$SOURCES_LIST_FILE" | sponge "$SOURCES_LIST_FILE"
394    for file in "$LIVE_CONF" "$CONFIG" "$LOCAL_CONFIG" ; do
395        if [ -n "$file" ] ; then
396           sed "s/^SUITE=.*/SUITE=\"$SUITE\"/" $file | sponge $file
397           sed "s/\(^deb .\+\)\([ \t]*\)\($DIST\)\([ \t]*\)\(main \)/\1 \2$SUITE\4\5/" "$file" | sponge "$file"
398        fi
399    done
400
401    # notice: activate grml-live pool only if we are building against unstable:
402    if grep -qe unstable -qe sid "$SOURCES_LIST_FILE" ; then
403       grep -q 'grml-live.*main' "$SOURCES_LIST_FILE" || \
404       grep grml-stable "$SOURCES_LIST_FILE" | \
405            sed 's/grml-stable/grml-live/' >> "$SOURCES_LIST_FILE"
406    else
407       grep -q 'grml-live.*main' "$SOURCES_LIST_FILE" && \
408       sed 's/.*grml-live.*/# removed grml-live repository/' "$SOURCES_LIST_FILE" | sponge "$SOURCES_LIST_FILE"
409    fi
410
411    for file in "$LIVE_CONF" "$CONFIG" "$LOCAL_CONFIG" "$NFSROOT_CONF" ; do
412        if [ -n "$file" ] ; then
413           sed "s|^FAI_DEBOOTSTRAP=\"[a-z]* |FAI_DEBOOTSTRAP=\"$SUITE |" "$file" | sponge "$file"
414        fi
415    done
416 fi
417
418 # set $ARCH
419 [ -n "$ARCH" ] || ARCH="$(dpkg --print-architecture)"
420 if grep -q -- 'FAI_DEBOOTSTRAP_OPTS.*--arch' "$NFSROOT_CONF" ; then
421    sed "s/--arch [a-z0-9]* /--arch $ARCH /" "$NFSROOT_CONF" | sponge "$NFSROOT_CONF"
422 else
423    sed "s|^FAI_DEBOOTSTRAP_OPTS=\"\(.*\)|FAI_DEBOOTSTRAP_OPTS=\"--arch $ARCH \1|" "$NFSROOT_CONF" | sponge "$NFSROOT_CONF"
424 fi
425 # }}}
426
427 # CHROOT_OUTPUT - execute FAI {{{
428 if [ -n "$BUILD_DIRTY" ]; then
429   einfo "Skipping FAI" ; eend 0
430 else
431    [ -n "$CHROOT_OUTPUT" ] || CHROOT_OUTPUT="$OUTPUT/grml_chroot"
432
433    if [ -n "$UPDATE" -o -n "$BUILD_ONLY" ] ; then
434       FAI_ACTION=softupdate
435    else
436       FAI_ACTION=dirinstall
437    fi
438
439    if [ -n "$UPDATE" -o -n "$BUILD_ONLY" ] ; then
440       if ! [ -r "$CHROOT_OUTPUT/etc/grml_version" ] ; then
441          log "Error: does not look like you have a working chroot. Updating/building not possible."
442          eerror "Error: does not look like you have a working chroot. Updating/building not possible. (Drop -u/-b option?)"
443          eend 1
444          bailout 20
445       fi
446    fi
447
448    if [ -d "$CHROOT_OUTPUT/bin" -a -z "$UPDATE" -a -z "$BUILD_ONLY" ] ; then
449       log "$CHROOT_OUTPUT exists already, skipping stage 'fai dirinstall'"
450       ewarn "$CHROOT_OUTPUT exists already, skipping stage 'fai dirinstall'" ; eend 0
451    else
452       mkdir -p "$CHROOT_OUTPUT" || bailout 5 "Problem with creating $CHROOT_OUTPUT for FAI"
453
454       if [ -n "${MIRROR_DIRECTORY}" ] ; then
455          mkdir -p "${CHROOT_OUTPUT}/${MIRROR_DIRECTORY}"
456          mount --bind "${MIRROR_DIRECTORY}" "${CHROOT_OUTPUT}/${MIRROR_DIRECTORY}"
457       fi
458
459       log "Executed FAI command line:"
460       log "BUILD_ONLY=$BUILD_ONLY fai $VERBOSE -C $GRML_FAI_CONFIG -c$CLASSES -u $HOSTNAME $FAI_ACTION $CHROOT_OUTPUT $FAI_ARGS"
461       BUILD_ONLY="$BUILD_ONLY" fai $VERBOSE -C "$GRML_FAI_CONFIG" -c"$CLASSES" -u \
462       "$HOSTNAME" $FAI_ACTION "$CHROOT_OUTPUT" $FAI_ARGS | tee -a $LOGFILE
463       RC="$PIPESTATUS" # notice: bash-only
464
465       if [ "$RC" != 0 ] ; then
466          log "Error while executing fai [exit code ${RC}]. Exiting."
467          eerror "Error while executing fai [exit code ${RC}]. Exiting." ; eend 1
468          bailout 1
469       else
470          log "Setting /etc/grml_version to $GRML_NAME $VERSION Release Codename $RELEASENAME [$ISO_DATE]"
471          echo "$GRML_NAME $VERSION Release Codename $RELEASENAME [$ISO_DATE]" > $CHROOT_OUTPUT/etc/grml_version
472          chmod 644 $CHROOT_OUTPUT/etc/grml_version
473          einfo "Rebuilding initramfs"
474          # make sure new /etc/grml_version reaches the initramfs:
475          chroot $CHROOT_OUTPUT update-initramfs -u -t
476          eend $?
477       fi
478
479       # Remove all FAI logs from chroot if class RELEASE is used:
480       if [ -f "$CHROOT_OUTPUT"/etc/grml_fai_release ] ; then
481          rm -rf "$CHROOT_OUTPUT"/var/log/fai/*
482       fi
483
484       # make sure we don't leave any mounts - FAI doesn't remove them always
485       umount $CHROOT_OUTPUT/proc 2>/dev/null || /bin/true
486       umount $CHROOT_OUTPUT/sys  2>/dev/null || /bin/true
487       umount $CHROOT_OUTPUT/dev/pts 2>/dev/null || /bin/true
488       umount $CHROOT_OUTPUT/dev 2>/dev/null || /bin/true
489
490       [ -n "$MIRROR_DIRECTORY" ] && umount "${CHROOT_OUTPUT}/${MIRROR_DIRECTORY}"
491
492       # notice: 'fai dirinstall' does not seem to exit appropriate, so:
493       ERROR=''
494       CHECKLOG=/var/log/fai/$HOSTNAME/last
495       if [ -r "$CHECKLOG/software.log" ] ; then
496          # 1 errors during executing of commands
497          grep 'dpkg: error processing' $CHECKLOG/software.log >> $LOGFILE && ERROR=1
498          grep 'E: Method http has died unexpectedly!' $CHECKLOG/software.log >> $LOGFILE && ERROR=2
499          grep 'ERROR: chroot' $CHECKLOG/software.log >> $LOGFILE && ERROR=3
500          grep 'E: Failed to fetch' $CHECKLOG/software.log >> $LOGFILE && ERROR=4
501          grep 'Unable to write mmap - msync (28 No space left on device)' $CHECKLOG/software.log >> $LOGFILE && ERROR=5
502       fi
503
504       if [ -r "$CHECKLOG/shell.log" ] ; then
505          grep 'FAILED with exit code' $CHECKLOG/shell.log >> $LOGFILE && ERROR=2
506       fi
507
508       if [ -n "$ERROR" ] ; then
509          log "There was an error [${ERROR}] during execution of stage 'fai dirinstall' [$(date)]"
510          eerror "There was an error during execution of stage 'fai dirinstall'"
511          echo "   Check out ${CHECKLOG}/ for details. [exit ${ERROR}]"
512          eend 1
513          bailout 1
514       else
515          log "Finished execution of stage 'fai dirinstall' [$(date)]"
516          einfo "Finished execution of stage 'fai dirinstall'"
517       fi
518
519    fi
520 fi # BUILD_DIRTY?
521 # }}}
522
523 # BUILD_OUTPUT - execute arch specific stuff and squashfs {{{
524 [ -n "$BUILD_OUTPUT" ] || BUILD_OUTPUT="$OUTPUT/grml_cd"
525 mkdir -p "$BUILD_OUTPUT" || bailout 6 "Problem with creating $BUILD_OUTPUT for stage ARCH"
526
527 # i386:
528 if [ "$ARCH" = i386 ] || [ "$ARCH" = amd64 ] ; then
529    if [ -d "$BUILD_OUTPUT"/boot -a -z "$UPDATE" -a -z "$BUILD_ONLY" ] ; then
530       log "$BUILD_OUTPUT/boot exists already, skipping stage 'boot'"
531       ewarn "$BUILD_OUTPUT/boot exists already, skipping stage 'boot'" ; eend 0
532    else
533       # booting stuff:
534       [ -d "$BUILD_OUTPUT"/boot/isolinux ] || mkdir -p "$BUILD_OUTPUT"/boot/isolinux
535       [ -d "$BUILD_OUTPUT"/boot/"${SHORT_GRML_NAME}" ] || mkdir -p "$BUILD_OUTPUT"/boot/"${SHORT_GRML_NAME}"
536
537       if [ -z "$NO_ADDONS" ] ; then
538          [ -d "$BUILD_OUTPUT"/boot/addons   ] || mkdir -p "$BUILD_OUTPUT"/boot/addons
539          cp /boot/memtest86+.bin "$BUILD_OUTPUT"/boot/addons/memtest
540       fi
541
542       # if we don't have an initrd we a) can't boot and b) there was an error
543       # during build, so check for the file:
544       INITRD="$(ls $CHROOT_OUTPUT/boot/initrd* 2>/dev/null| grep -v '.bak$' | sort -r | head -1)"
545       if [ -n "$INITRD" ] ; then
546          cp $INITRD "$BUILD_OUTPUT"/boot/"${SHORT_GRML_NAME}"/initrd.gz
547          find $CHROOT_OUTPUT/boot/ -name initrd\*.bak -exec rm {} \;
548       else
549          log "No initrd found inside $CHROOT_OUTPUT/boot/ - Exiting"
550          eerror "No initrd found inside $CHROOT_OUTPUT/boot/ - Exiting" ; eend 1
551          bailout 10
552       fi
553
554       KERNEL_IMAGE="$(ls $CHROOT_OUTPUT/boot/vmlinuz* 2>/dev/null | sort -r | head -1)"
555       if [ -n "$KERNEL_IMAGE" ] ; then
556          cp "$KERNEL_IMAGE" "$BUILD_OUTPUT"/boot/"${SHORT_GRML_NAME}"/linux26
557       else
558          log "No kernel found inside $CHROOT_OUTPUT/boot/ - Exiting"
559          eerror "No kernel found inside $CHROOT_OUTPUT/boot/ - Exiting" ; eend 1
560          bailout 11
561       fi
562
563       [ -n "$TEMPLATE_DIRECTORY" ] || TEMPLATE_DIRECTORY='/usr/share/grml-live/templates'
564       if ! [ -d "${TEMPLATE_DIRECTORY}"/boot ] ; then
565          log "${TEMPLATE_DIRECTORY}/boot does not exist. Exiting."
566          eerror "${TEMPLATE_DIRECTORY}/boot does not exist. Exiting." ; eend 1
567          bailout 8
568       fi
569
570       cp ${TEMPLATE_DIRECTORY}/boot/isolinux/*  "$BUILD_OUTPUT"/boot/isolinux/
571
572       if [ -z "$NO_ADDONS" ] ; then
573          if ! [ -d /usr/share/grml-live/templates/boot/addons/bsd4grml ] ; then
574            ewarn "Boot addons not found, skipping therefore. (Consider installing package grml-live-addons)" ; eend 0
575          else
576            # copy only files so we can handle bsd4grml on its own
577            for file in ${TEMPLATE_DIRECTORY}/boot/addons/* ; do
578                test -f $file && cp $file "$BUILD_OUTPUT"/boot/addons/
579            done
580
581            if [ -z "$NO_ADDONS_BSD4GRML" ] ; then
582               cp -a ${TEMPLATE_DIRECTORY}/boot/addons/bsd4grml "$BUILD_OUTPUT"/boot/addons/
583            fi
584          fi
585       fi
586
587       if ! [ -d "${BUILD_OUTPUT}/boot/grub" ] ; then
588          cp -a ${TEMPLATE_DIRECTORY}/boot/grub  "$BUILD_OUTPUT"/boot/
589       fi
590       # make sure we have recent template files available, otherwise updating
591       # the strings like $GRML_NAME and $VERSION might be out of date
592       cp ${TEMPLATE_DIRECTORY}/boot/grub/* "$BUILD_OUTPUT"/boot/grub/
593
594       if ! [ -d "${TEMPLATE_DIRECTORY}"/GRML ] ; then
595          log "${TEMPLATE_DIRECTORY}/GRML does not exist. Exiting."
596          eerror "${TEMPLATE_DIRECTORY}/GRML does not exist. Exiting." ; eend 1
597          bailout 9
598       fi
599
600       [ -d "$BUILD_OUTPUT"/GRML ] || mkdir "$BUILD_OUTPUT"/GRML
601       cp -a ${TEMPLATE_DIRECTORY}/GRML/* "$BUILD_OUTPUT"/GRML/
602
603       # adjust boot splash information:
604       RELEASE_INFO="$GRML_NAME $VERSION - Release Codename $RELEASENAME"
605       RELEASE_INFO="$(cut_string 68 "$RELEASE_INFO")"
606       RELEASE_INFO="$(extend_string_end 68 "$RELEASE_INFO")"
607
608       sed -i "s/%RELEASE_INFO%/$GRML_NAME $VERSION - $RELEASENAME/" "$BUILD_OUTPUT"/GRML/grml-version
609       sed -i "s/%DATE%/$ISO_DATE/"             "$BUILD_OUTPUT"/GRML/grml-version
610
611       sed -i "s/%RELEASE_INFO%/$RELEASE_INFO/" "$BUILD_OUTPUT"/boot/isolinux/boot.msg
612       sed -i "s/%DATE%/$ISO_DATE/"             "$BUILD_OUTPUT"/boot/isolinux/boot.msg
613
614       sed -i "s/%GRML_NAME%/$SHORT_GRML_NAME/" "$BUILD_OUTPUT"/boot/isolinux/isolinux.cfg
615       sed -i "s/%GRML_NAME%/$SHORT_GRML_NAME/" "$BUILD_OUTPUT"/boot/isolinux/syslinux.cfg
616
617       sed -i "s/%RELEASE_INFO%/$RELEASE_INFO/" "$BUILD_OUTPUT"/boot/isolinux/boot-beep.msg
618       sed -i "s/%DATE%/$ISO_DATE/"             "$BUILD_OUTPUT"/boot/isolinux/boot-beep.msg
619
620       sed -i "s/%VERSION%/$VERSION/"           "$BUILD_OUTPUT"/boot/grub/menu.lst
621       sed -i "s/%GRML_NAME%/$SHORT_GRML_NAME/" "$BUILD_OUTPUT"/boot/grub/menu.lst
622
623       sed -i "s/%VERSION%/$VERSION/"           "$BUILD_OUTPUT"/boot/grub/grub.cfg
624       sed -i "s/%GRML_NAME%/$SHORT_GRML_NAME/" "$BUILD_OUTPUT"/boot/grub/grub.cfg
625
626       # make sure the squashfs filename is set accordingly:
627       GRML_NAME_SQUASHFS="$GRML_NAME.squashfs"
628       sed -i "s/%GRML_NAME_SQUASHFS%/$GRML_NAME_SQUASHFS/" "$BUILD_OUTPUT"/boot/isolinux/isolinux.cfg
629       sed -i "s/%GRML_NAME_SQUASHFS%/$GRML_NAME_SQUASHFS/" "$BUILD_OUTPUT"/boot/isolinux/syslinux.cfg
630       sed -i "s/%GRML_NAME_SQUASHFS%/$GRML_NAME_SQUASHFS/" "$BUILD_OUTPUT"/boot/grub/menu.lst
631
632       GRML_NAME_SQUASHFS="$(cut_string 20 "$GRML_NAME_SQUASHFS")"
633       GRML_NAME_SQUASHFS="$(extend_string_end 20 "$GRML_NAME_SQUASHFS")"
634       sed -i "s/%GRML_NAME_SQUASHFS%/$GRML_NAME_SQUASHFS/" "$BUILD_OUTPUT"/boot/isolinux/f4
635       sed -i "s/%GRML_NAME_SQUASHFS%/$GRML_NAME_SQUASHFS/" "$BUILD_OUTPUT"/boot/isolinux/f5
636
637       # jump back to grub from bsd4grml:
638       if [ -e "$BUILD_OUTPUT"/boot/addons/bsd4grml/boot.6 ]; then
639          if [ -e "$BUILD_OUTPUT"/boot/grub/core.img ]; then
640             GRUB_VERSION=2
641          else
642             GRUB_VERSION=1
643          fi
644          if [ -e "$BUILD_OUTPUT"/boot/grub/stage2 ]; then
645             GRUB_LEGACY=stage2
646          else
647             GRUB_LEGACY=stage2_eltorito
648          fi
649
650          # why not ed(1)?
651          for file in "$BUILD_OUTPUT"/boot/addons/bsd4grml/boot.6 \
652                      "$BUILD_OUTPUT"/boot/addons/bsd4grml/boot.cfg; do
653              sed -i -e "s!%GRUB_VERSION%!$GRUB_VERSION!g" \
654                     -e "s!%GRUB_LEGACY%!$GRUB_LEGACY!g" "$file"
655          done
656       fi
657
658       # autostart for Windows:
659       if [ -d "${TEMPLATE_DIRECTORY}/windows/autostart/" ] ; then
660          cp ${TEMPLATE_DIRECTORY}/windows/autostart/* "$BUILD_OUTPUT"/
661       fi
662
663       # windows-binaries:
664       if [ -n "$WINDOWS_BINARIES" ] ; then
665          if [ -f "$BUILD_OUTPUT"/windows/putty.exe ] ; then
666             log "$BUILD_OUTPUT/windows exists already, skipping stage 'WINDOWS_BINARIES'"
667             ewarn "$BUILD_OUTPUT/windows exists already, skipping stage 'WINDOWS_BINARIES'" ; eend 0
668          else
669             if ! [ -d "$BUILD_OUTPUT"/windows ] ; then
670                mkdir "$BUILD_OUTPUT"/windows
671                ( cd "$BUILD_OUTPUT"/windows
672                  for file in pageant plink pscp psftp putty puttygen ; do
673                     wget -O ${file}.exe ${WINDOWS_BINARIES}/${file}.exe
674                     md5sum ${file}.exe > ${file}.exe.md5
675                  done )
676             fi
677          fi
678       log "Finished execution of stage 'WINDOWS_BINARIES' [$(date)]"
679       einfo "Finished execution of stage 'WINDOWS_BINARIES'" ; eend 0
680       fi
681    einfo "Finished execution of stage 'boot'" ; eend 0
682    fi
683 # ppc:
684 elif [ "$ARCH" = powerpc ] ; then
685     ewarn 'Warning: formorer, it is your turn. :)'>&2
686 # unsuported:
687 else
688    eerror 'Error: Unsupported ARCH, sorry. Want to support it? Contribute!' ; eend 1
689 fi
690
691 if [ -f "$BUILD_OUTPUT"/live/${GRML_NAME}.squashfs -a -z "$UPDATE" -a -z "$BUILD_ONLY" -a -z "$BUILD_DIRTY" ] ; then
692    log "$BUILD_OUTPUT/live exists already, skipping stage 'squashfs'"
693    ewarn "$BUILD_OUTPUT/live exists already, skipping stage 'squashfs'" ; eend 0
694 elif [ -n "$SKIP_MKSQUASHFS" ] ; then
695    log "Skipping stage 'squashfs' as requested via option -q"
696    ewarn "Skipping stage 'squashfs' as requested via option -q" ; eend 0
697 else
698    [ -d "$BUILD_OUTPUT"/live ] || mkdir "$BUILD_OUTPUT"/live
699    # make sure we don't leave (even an empty) base.tgz:
700    [ -f "$CHROOT_OUTPUT/base.tgz" ] && rm -f "$CHROOT_OUTPUT/base.tgz"
701
702    # make sure mksquashfs can handle the according option:
703    if [ -n "$SQUASHFS_ZLIB" ] ; then
704       mksquashfs --help 2>&1 | grep -q -- "$SQUASHFS_ZLIB" || SQUASHFS_ZLIB=''
705    fi
706
707    if echo "$SQUASHFS_OPTIONS" | grep -q -- "-nolzma" ; then
708       if ! mksquashfs --help 2>&1 | grep -q -- '-nolzma' ; then
709          ewarn "mksquashfs does NOT support the nolzma option, just using default zlib mode."
710          SQUASHFS_OPTIONS="$(echo $SQUASHFS_OPTIONS | sed 's/-nolzma//g')"
711          eend 0
712       fi
713    fi
714
715    if echo "$SQUASHFS_OPTIONS" | grep -q -- "-lzma" ; then
716       if ! mksquashfs --help 2>&1 | grep -q -- '-lzma' ; then
717          ewarn "mksquashfs does NOT support the lzma option, falling back to zlib mode."
718          SQUASHFS_OPTIONS="$(echo $SQUASHFS_OPTIONS | sed 's/-lzma//g')"
719          eend 0
720       fi
721    fi
722
723    # support exclusion of files via exclude-file:
724    if [ -n "$SQUASHFS_EXCLUDES_FILE" -a "$SQUASHFS_EXCLUDES_FILE" ] ; then
725       SQUASHFS_OPTIONS="$SQUASHFS_OPTIONS -ef $SQUASHFS_EXCLUDES_FILE"
726    fi
727
728    # get rid of unnecessary files when building grml-small for final release:
729    if echo "$CLASSES" | grep -q GRML_SMALL ; then
730       SQUASHFS_OPTIONS="$SQUASHFS_OUTPUT -e initrd.img* vmlinuz*"
731    fi
732
733    SQUASHFS_OUTPUT="$(mktemp -t grml-live.XXXXXX)"
734    log "mksquashfs $CHROOT_OUTPUT/* $BUILD_OUTPUT/live/${GRML_NAME}.squashfs -noappend $SQUASHFS_OPTIONS $SQUASHFS_ZLIB"
735    if mksquashfs $CHROOT_OUTPUT/* $BUILD_OUTPUT/live/"${GRML_NAME}".squashfs \
736       -noappend $SQUASHFS_OPTIONS $SQUASHFS_ZLIB 2>"${SQUASHFS_OUTPUT}" ; then
737       echo "${GRML_NAME}.squashfs" > $BUILD_OUTPUT/live/filesystem.module
738       log "Finished execution of stage 'squashfs' [$(date)]"
739       einfo "Finished execution of stage 'squashfs'" ; eend 0
740       rm -f "${SQUASHFS_OUTPUT}"
741    else
742       log "There was an error executing stage 'squashfs' [$(date)]:"
743       log "$(cat $SQUASHFS_OUTPUT)"
744       eerror "There was an error executing stage 'squashfs':" ; eend 1
745       cat "${SQUASHFS_OUTPUT}"
746       rm -f "${SQUASHFS_OUTPUT}"
747       bailout
748    fi
749 fi
750
751 # create md5sum file:
752 ( cd $BUILD_OUTPUT/GRML &&
753 find .. -type f -not -name md5sums -not -name isolinux.bin -exec md5sum {} \; > md5sums )
754 # }}}
755
756 # ISO_OUTPUT - mkisofs {{{
757 [ -n "$ISO_OUTPUT" ] || ISO_OUTPUT="$OUTPUT/grml_isos"
758 [ -n "$ISO_NAME" ] || ISO_NAME="${GRML_NAME}_${VERSION}.iso"
759
760 if [ "$BOOT_METHOD" = "isolinux" ] ; then
761    BOOT_FILE="boot/isolinux/isolinux.bin -c boot/isolinux/boot.cat"
762 elif [ "$BOOT_METHOD" = "grub" ] ; then
763    BOOT_FILE="boot/grub/stage2_eltorito"
764 fi
765
766 if [ -f "${ISO_OUTPUT}/${ISO_NAME}" -a -z "$UPDATE" -a -z "$BUILD_ONLY" -a -z "$BUILD_DIRTY" ] ; then
767
768    # support mkisofs as well as genisoimage
769    if which mkisofs >/dev/null 2>&1; then
770       MKISOFS=mkisofs
771    elif which genisoimage >/dev/null 2>&1; then
772       MKISOFS=genisoimage
773    else
774       log "Sorry, neither mkisofs nor genisoimage available - can not create ISO."
775       eerror "Sorry, neither mkisofs nor genisoimage available - can not create ISO." ; eend 1
776       bailout
777    fi
778
779    log "$ISO_OUTPUT exists already, skipping stage 'iso build'"
780    ewarn "$ISO_OUTPUT exists already, skipping stage 'iso build'" ; eend 0
781 else
782    mkdir -p "$ISO_OUTPUT" || bailout 6 "Problem with creating $ISO_OUTPUT for stage 'iso build'"
783
784    CURRENT_DIR=$(pwd)
785    if cd "$BUILD_OUTPUT" ; then
786       log "$MKISOFS -V '${GRML_NAME} ${VERSION}' -publisher 'grml-live | grml.org' -l -r -J -no-emul-boot -boot-load-size 4 -boot-info-table -b $BOOT_FILE -o ${ISO_OUTPUT}/${ISO_NAME} ."
787       $MKISOFS -V "${GRML_NAME} ${VERSION}" -publisher 'grml-live | grml.org' \
788               -l -r -J -no-emul-boot -boot-load-size 4 -boot-info-table    \
789               -b $BOOT_FILE -no-pad \
790               -o "${ISO_OUTPUT}/${ISO_NAME}" . ; RC=$?
791
792       # pad the output ISO to multiples of 256 KiB for partition table support
793       siz=$($getfilesize "${ISO_OUTPUT}/${ISO_NAME}")
794       cyls=$((siz / 512 / 32 / 16 + 1))         # C=$cyls H=16 S=32
795       siz=$((cyls * 16 * 32 * 512))             # size after padding
796       dd if=/dev/zero bs=1 count=1 seek=$((siz - 1)) \
797          of="${ISO_OUTPUT}/${ISO_NAME}" 2>/dev/null
798
799       # support disabling hybrid ISO image
800       if [ "$HYBRID_METHOD" = "disable" ] ; then\
801          log "Skipping creation of hybrid ISO file as requested via HYBRID_METHOD=disable"
802          einfo "Skipping creation of hybrid ISO file as requested via HYBRID_METHOD=disable"
803          eend 0
804       # use isohybrid only on request
805       elif [ "$HYBRID_METHOD" = "isohybrid" ] ; then
806          if ! which isohybrid >/dev/null 2>&1 ; then
807            bailout 12 "isohybrid binary not found - please install syslinux/syslinux-common"
808          else
809            log "Creating hybrid ISO file with isohybrid method"
810            einfo "Creating hybrid ISO file with isohybrid method"
811            isohybrid "${ISO_OUTPUT}/${ISO_NAME}"
812            eend $?
813          fi
814       # by default use our manifold boot method:
815       else
816          if ! [ -r boot/grub/core.img ] ; then
817            ewarn "boot/grub/core.img not found, not creating manifold boot ISO file"
818          else
819            log "Creating hybrid ISO file with manifold method"
820            einfo "Creating hybrid ISO file with manifold method"
821            echo 1 63 | \
822                mksh /usr/share/grml-live/scripts/bootgrub.mksh -A -M 1 -p 0x83 -g $cyls:16:32 | \
823                cat - boot/grub/core.img | \
824                dd conv=notrunc of="${ISO_OUTPUT}/${ISO_NAME}" conv=notrunc 2>/dev/null
825            eend $?
826          fi
827       fi
828
829       # generate md5sum and sha1sum of ISO if we are using class 'RELEASE':
830       case $CLASSES in *RELEASE*)
831          [ "$RC" = 0 ] && \
832          (
833            if cd $ISO_OUTPUT ; then
834              md5sum ${ISO_NAME} > ${ISO_NAME}.md5 && \
835              touch -r ${ISO_NAME} ${ISO_NAME}.md5
836              sha1sum ${ISO_NAME} > ${ISO_NAME}.sha1 && \
837              touch -r ${ISO_NAME} ${ISO_NAME}.sha1
838            fi
839          )
840          ;;
841       esac
842
843       cd $CURRENT_DIR
844    fi
845
846    if [ "$RC" = 0 ] ; then
847       log "Finished execution of stage 'iso build' [$(date)]"
848       einfo "Finished execution of stage 'iso build'" ; eend 0
849    else
850       log "There was an error ($RC) executing stage 'iso build' [$(date)]"
851       eerror "There was an error executing stage 'iso build'" ; eend 1
852       bailout $RC
853    fi
854 fi
855 # }}}
856
857 # finalize {{{
858 [ -n "$start_seconds" ] && SECONDS="$[$(cut -d . -f 1 /proc/uptime)-$start_seconds]" || SECONDS="unknown"
859 einfo "Sucessfully finished execution of $PN [running ${SECONDS} seconds]" ; eend 0
860 log "Sucessfully finished execution of $PN [running ${SECONDS} seconds]"
861 bailout 0
862 # }}}
863
864 ## END OF FILE #################################################################
865 # vim:foldmethod=marker ts=2 ft=sh ai expandtab tw=80 sw=3