Provide information about grml-live version and Debian/suite through /etc/grml_live_v...
[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     q) SKIP_MKSQUASHFS=1 ;;
235     r) RELEASENAME="$OPTARG" ;;
236     s) SUITE="$OPTARG" ;;
237     t) TEMPLATE_DIRECTORY="$OPTARG";;
238     v) VERSION="$OPTARG" ;;
239     F) FORCE=1 ;;
240     u) UPDATE=1 ;;
241     V) VERBOSE="-v" ;;
242     z) SQUASHFS_ZLIB="-nolzma" ;;
243     ?) echo "invalid option -$OPTARG" >&2; bailout 1 ;;
244   esac
245 done
246 shift $(($OPTIND - 1))  # set ARGV to the first not parsed commandline parameter
247 # }}}
248
249 # some misc checks before executing FAI {{{
250 [ -n "$CLASSES" ] || bailout 1 "Error: \$CLASSES unset, please set it in $LIVE_CONF or
251 specify it on the command line using the -c option."
252 [ -n "$OUTPUT" ] || bailout 1 "Error: \$OUTPUT unset, please set it in $LIVE_CONF or
253 specify it on the command line using the -o option."
254
255 # set subdirectories according to $OUTPUT:
256 CHROOT_OUTPUT="$OUTPUT/grml_chroot"
257 BUILD_OUTPUT="$OUTPUT/grml_cd"
258 ISO_OUTPUT="$OUTPUT/grml_isos"
259
260 # trim characters that are known to cause problems inside $GRML_NAME;
261 # for example isolinux does not like '-' inside the directory name
262 [ -n "$GRML_NAME" ] && export SHORT_GRML_NAME="$(echo $GRML_NAME | tr -d ',./;\- ')"
263
264 # export variables to have them available in fai scripts:
265 [ -n "$GRML_NAME" ]   && export GRML_NAME="$GRML_NAME"
266 [ -n "$RELEASENAME" ] && export RELEASENAME="$RELEASENAME"
267 # }}}
268
269 # clean/zero grml-live logfile {{{
270 if [ -n "$ZERO_LOGFILE" ] ; then
271    echo -n > $LOGFILE
272 fi
273 # }}}
274
275 # clean/zero/remove old FAI directory {{{
276 if [ -n "$ZERO_FAI_LOGFILE" ] ; then
277    if [ -d /var/log/fai/"$HOSTNAME" ] ; then
278       rm -rf /var/log/fai/"$HOSTNAME"/"$(readlink /var/log/fai/"$HOSTNAME"/last)"
279       rm -rf /var/log/fai/"$HOSTNAME"/"$(readlink /var/log/fai/"$HOSTNAME"/last-dirinstall)"
280       rm -rf /var/log/fai/"$HOSTNAME"/"$(readlink /var/log/fai/"$HOSTNAME"/last-softupdate)"
281       rm -f /var/log/fai/"$HOSTNAME"/last \
282             /var/log/fai/"$HOSTNAME"/last-dirinstall \
283             /var/log/fai/"$HOSTNAME"/last-softupdate
284    fi
285 fi
286 # }}}
287
288 # ask user whether the setup is ok {{{
289 if [ -z "$FORCE" ] ; then
290    echo
291    echo "${PN} [${GRML_LIVE_VERSION}]: check your configuration (or use -F to force execution):"
292    echo
293    echo "  FAI classes:       $CLASSES"
294    [ -r "$LOCAL_CONFIG" ]       && echo "  local config:      /etc/grml/grml-live.local"
295    [ -n "$CONFIG" ]             && echo "  configuration:     $CONFIG"
296    echo "  main directory:    $OUTPUT"
297    [ -n "$CHROOT_OUTPUT" ]      && echo "  chroot target:     $CHROOT_OUTPUT"
298    [ -n "$BUILD_OUTPUT" ]       && echo "  build target:      $BUILD_OUTPUT"
299    [ -n "$ISO_OUTPUT" ]         && echo "  ISO target:        $ISO_OUTPUT"
300    [ -n "$GRML_NAME" ]          && echo "  grml name:         $GRML_NAME"
301    [ -n "$RELEASENAME" ]        && echo "  release name:      $RELEASENAME"
302    [ -n "$VERSION" ]            && echo "  grml version:      $VERSION"
303    [ -n "$SUITE" ]              && echo "  Debian suite:      $SUITE"
304    [ -n "$ARCH" ]               && echo "  Architecture:      $ARCH"
305    [ -n "$BOOT_METHOD" ]        && echo "  Boot method:       $BOOT_METHOD"
306    [ -n "$TEMPLATE_DIRECTORY" ] && echo "  Template files:    $TEMPLATE_DIRECTORY"
307    [ -n "$FAI_ARGS" ]           && echo "  additional arguments for FAI: $FAI_ARGS"
308    [ -n "$LOGFILE" ]            && echo "  Logging to file:   $LOGFILE"
309    [ -n "$SQUASHFS_ZLIB" ]      && echo "  Using ZLIB (instead of LZMA) compression."
310    [ -n "$SQUASHFS_OPTIONS" ]   && echo "  Using SQUASHFS_OPTIONS ${SQUASHFS_OPTIONS}"
311    [ -n "$VERBOSE" ]            && echo "  Using VERBOSE mode."
312    [ -n "$UPDATE" ]             && echo "  Executing UPDATE instead of fresh installation."
313    [ -n "$SKIP_MKSQUASHFS" ]    && echo "  Skipping creation of SQUASHFS file."
314    [ -n "$BUILD_ONLY" ]         && echo "  Executing BUILD_ONLY instead of fresh installation or UPDATE."
315    [ -n "$BUILD_DIRTY" ]        && echo "  Executing BUILD_DIRTY to leave chroot untouched."
316    echo
317    echo -n "Is this ok for you? [y/N] "
318    read a
319    if ! [ "$a" = 'y' -o "$a" = 'Y' ] ; then
320       bailout 1 "Exiting as requested."
321    fi
322    echo
323 fi
324
325 if [ -n "$CONFIG" ] ; then
326    if ! [ -f "$CONFIG" ] ; then
327       log "Sorry, $CONFIG could not be read. Exiting. [$(date)]"
328       eerror "Sorry, $CONFIG could not be read. Exiting."
329       bailout 1
330    else
331       log "Sourcing $CONFIG"
332       . $CONFIG
333    fi
334 fi
335
336 start_seconds=$(cut -d . -f 1 /proc/uptime)
337 log "------------------------------------------------------------------------------"
338 log "Starting grml-live [${GRML_LIVE_VERSION}] run on $(date)"
339 log "Executed grml-live command line:"
340 log "$CMDLINE"
341
342 einfo "Logging actions to logfile $LOGFILE"
343 # }}}
344
345 # on-the-fly configuration {{{
346 if [ -n "$MIRROR_DIRECTORY" ] ; then
347    if ! [ -d "$MIRROR_DIRECTORY/debian" ] ; then
348       log "Sorry, $MIRROR_DIRECTORY/debian does not seem to exist. Exiting. [$(date)]"
349       eerror "Sorry, $MIRROR_DIRECTORY/debian does not seem to exist. Exiting."
350       bailout 1
351    fi
352    cat > "$SOURCES_LIST_FILE" << EOF
353 # NOTE: This file is *NOT* meant for manual customisation! This file is
354 # modified by grml-live and any changes might be overriden.
355 # You might consider using GRML_LIVE_SOURCES in /etc/grml/grml-live.conf*
356 # and using /etc/grml/fai/files/etc/apt instead!'
357 EOF
358    echo "$MIRROR_SOURCES" >> "$SOURCES_LIST_FILE"
359    if [ -n "$GRML_LIVE_SOURCES" ] ; then
360       echo "$GRML_LIVE_SOURCES" >> "$SOURCES_LIST_FILE"
361    fi
362 elif [ -n "$GRML_LIVE_SOURCES" ] ; then
363    cat > "$SOURCES_LIST_FILE" << EOF
364 # NOTE: This file is *NOT* meant for manual customisation! This file is
365 # modified by grml-live and any changes might be overriden.
366 # You might consider using GRML_LIVE_SOURCES in /etc/grml/grml-live.conf*
367 # and using /etc/grml/fai/files/etc/apt instead!'
368 EOF
369    echo "$GRML_LIVE_SOURCES" >> "$SOURCES_LIST_FILE"
370 fi
371
372 if [ -n "$FAI_DEBOOTSTRAP" ] ; then
373    sed "s#^FAI_DEBOOTSTRAP=.*#FAI_DEBOOTSTRAP=\"$FAI_DEBOOTSTRAP\"#" "$NFSROOT_CONF" | sponge "$NFSROOT_CONF"
374 fi
375
376 # does this suck? YES!
377 if [ -n "$SUITE" ] ; then
378
379    # /usr/share/debootstrap/scripts/unstable does not exist, instead use 'sid':
380    case $SUITE in
381       unstable) SUITE='sid' ;;
382       # make sure that we *NEVER* write any broken suite name to sources.list,
383       # otherwise we won't be able to adjust it one next (correct) execution
384       stable)   ;;
385       testing)  ;;
386       etch)     ;;
387       lenny)    ;;
388       squeeze)  ;;
389       sid)      ;;
390       *) echo "Sorry, $SUITE is not a valid Debian suite, exiting.">&2; bailout 1 ;;
391    esac
392
393    DIST=" etch\| stable\| lenny\| squeeze\| testing\| sid\| unstable"
394    sed "s/\(^deb .\+\)\([ \t]*\)\($DIST\)\([ \t]*\)\(main \)/\1 \2$SUITE\4\5/" "$SOURCES_LIST_FILE" | sponge "$SOURCES_LIST_FILE"
395    for file in "$LIVE_CONF" "$CONFIG" "$LOCAL_CONFIG" ; do
396        if [ -n "$file" ] ; then
397           sed "s/^SUITE=.*/SUITE=\"$SUITE\"/" $file | sponge $file
398           sed "s/\(^deb .\+\)\([ \t]*\)\($DIST\)\([ \t]*\)\(main \)/\1 \2$SUITE\4\5/" "$file" | sponge "$file"
399        fi
400    done
401
402    # notice: activate grml-live pool only if we are building against unstable:
403    if grep -qe unstable -qe sid "$SOURCES_LIST_FILE" ; then
404       grep -q 'grml-live.*main' "$SOURCES_LIST_FILE" || \
405       grep grml-stable "$SOURCES_LIST_FILE" | \
406            sed 's/grml-stable/grml-live/' >> "$SOURCES_LIST_FILE"
407    else
408       grep -q 'grml-live.*main' "$SOURCES_LIST_FILE" && \
409       sed 's/.*grml-live.*/# removed grml-live repository/' "$SOURCES_LIST_FILE" | sponge "$SOURCES_LIST_FILE"
410    fi
411
412    for file in "$LIVE_CONF" "$CONFIG" "$LOCAL_CONFIG" "$NFSROOT_CONF" ; do
413        if [ -n "$file" ] ; then
414           sed "s|^FAI_DEBOOTSTRAP=\"[a-z]* |FAI_DEBOOTSTRAP=\"$SUITE |" "$file" | sponge "$file"
415        fi
416    done
417 fi
418
419 # validate whether the specified architecture class matches the
420 # architecture (option), otherwise installation of kernel will fail
421 if echo $CLASSES | grep -qi i386 ; then
422    if ! [[ "$ARCH" == "i386" ]] ; then
423       eerror "You specified the I386 class but are trying to build something else (AMD64?)."
424       eerror "-> Either invoke grml-live with '-i i386' or adjust the architecture class. Exiting."
425       bailout
426    fi
427 elif echo $CLASSES | grep -qi amd64 ; then
428    if ! [[ "$ARCH" == "amd64" ]] ; then
429       eerror "You specified the AMD64 class but are trying to build something else (I386?)."
430       eerror "-> Either invoke grml-live with '-i amd64' or adjust the architecture class. Exiting."
431       bailout
432    fi
433 fi
434
435 # set $ARCH
436 [ -n "$ARCH" ] || ARCH="$(dpkg --print-architecture)"
437 if grep -q -- 'FAI_DEBOOTSTRAP_OPTS.*--arch' "$NFSROOT_CONF" ; then
438    sed "s/--arch [a-z0-9]* /--arch $ARCH /" "$NFSROOT_CONF" | sponge "$NFSROOT_CONF"
439 else
440    sed "s|^FAI_DEBOOTSTRAP_OPTS=\"\(.*\)|FAI_DEBOOTSTRAP_OPTS=\"--arch $ARCH \1|" "$NFSROOT_CONF" | sponge "$NFSROOT_CONF"
441 fi
442 # }}}
443
444 # CHROOT_OUTPUT - execute FAI {{{
445 if [ -n "$BUILD_DIRTY" ]; then
446   einfo "Skipping FAI" ; eend 0
447 else
448    [ -n "$CHROOT_OUTPUT" ] || CHROOT_OUTPUT="$OUTPUT/grml_chroot"
449
450    # provide inform fai about the ISO we build
451    echo '# This file has been generated by grml-live.' > "$CHROOT_OUTPUT/etc/grml_live_version"
452    [ -n "$GRML_LIVE_VERSION" ] && echo "GRML_LIVE_VERSION=$GRML_LIVE_VERSION" >> "$CHROOT_OUTPUT/etc/grml_live_version"
453    [ -n "$SUITE" ] && echo "SUITE=$SUITE" >> "$CHROOT_OUTPUT/etc/grml_live_version"
454
455    if [ -n "$UPDATE" -o -n "$BUILD_ONLY" ] ; then
456       FAI_ACTION=softupdate
457    else
458       FAI_ACTION=dirinstall
459    fi
460
461    if [ -n "$UPDATE" -o -n "$BUILD_ONLY" ] ; then
462       if ! [ -r "$CHROOT_OUTPUT/etc/grml_version" ] ; then
463          log "Error: does not look like you have a working chroot. Updating/building not possible."
464          eerror "Error: does not look like you have a working chroot. Updating/building not possible. (Drop -u/-b option?)"
465          eend 1
466          bailout 20
467       fi
468    fi
469
470    if [ -d "$CHROOT_OUTPUT/bin" -a -z "$UPDATE" -a -z "$BUILD_ONLY" ] ; then
471       log "$CHROOT_OUTPUT exists already, skipping stage 'fai dirinstall'"
472       ewarn "$CHROOT_OUTPUT exists already, skipping stage 'fai dirinstall'" ; eend 0
473    else
474       mkdir -p "$CHROOT_OUTPUT" || bailout 5 "Problem with creating $CHROOT_OUTPUT for FAI"
475
476       if [ -n "${MIRROR_DIRECTORY}" ] ; then
477          mkdir -p "${CHROOT_OUTPUT}/${MIRROR_DIRECTORY}"
478          mount --bind "${MIRROR_DIRECTORY}" "${CHROOT_OUTPUT}/${MIRROR_DIRECTORY}"
479       fi
480
481       log "Executed FAI command line:"
482       log "BUILD_ONLY=$BUILD_ONLY fai $VERBOSE -C $GRML_FAI_CONFIG -c$CLASSES -u $HOSTNAME $FAI_ACTION $CHROOT_OUTPUT $FAI_ARGS"
483       BUILD_ONLY="$BUILD_ONLY" fai $VERBOSE -C "$GRML_FAI_CONFIG" -c"$CLASSES" -u \
484       "$HOSTNAME" $FAI_ACTION "$CHROOT_OUTPUT" $FAI_ARGS | tee -a $LOGFILE
485       RC="$PIPESTATUS" # notice: bash-only
486
487       if [ "$RC" != 0 ] ; then
488          log "Error while executing fai [exit code ${RC}]. Exiting."
489          eerror "Error while executing fai [exit code ${RC}]. Exiting." ; eend 1
490          bailout 1
491       else
492          log "Setting /etc/grml_version to $GRML_NAME $VERSION Release Codename $RELEASENAME [$ISO_DATE]"
493          echo "$GRML_NAME $VERSION Release Codename $RELEASENAME [$ISO_DATE]" > $CHROOT_OUTPUT/etc/grml_version
494          chmod 644 $CHROOT_OUTPUT/etc/grml_version
495          einfo "Rebuilding initramfs"
496          # make sure new /etc/grml_version reaches the initramfs:
497          chroot $CHROOT_OUTPUT update-initramfs -u -t
498          eend $?
499       fi
500
501       # Remove all FAI logs from chroot if class RELEASE is used:
502       if [ -f "$CHROOT_OUTPUT"/etc/grml_fai_release ] ; then
503          rm -rf "$CHROOT_OUTPUT"/var/log/fai/*
504       fi
505
506       # make sure we don't leave any mounts - FAI doesn't remove them always
507       umount $CHROOT_OUTPUT/proc 2>/dev/null || /bin/true
508       umount $CHROOT_OUTPUT/sys  2>/dev/null || /bin/true
509       umount $CHROOT_OUTPUT/dev/pts 2>/dev/null || /bin/true
510       umount $CHROOT_OUTPUT/dev 2>/dev/null || /bin/true
511
512       [ -n "$MIRROR_DIRECTORY" ] && umount "${CHROOT_OUTPUT}/${MIRROR_DIRECTORY}"
513
514       # notice: 'fai dirinstall' does not seem to exit appropriate, so:
515       ERROR=''
516       CHECKLOG=/var/log/fai/$HOSTNAME/last
517       if [ -r "$CHECKLOG/software.log" ] ; then
518          # 1 errors during executing of commands
519          grep 'dpkg: error processing' $CHECKLOG/software.log >> $LOGFILE && ERROR=1
520          grep 'E: Method http has died unexpectedly!' $CHECKLOG/software.log >> $LOGFILE && ERROR=2
521          grep 'ERROR: chroot' $CHECKLOG/software.log >> $LOGFILE && ERROR=3
522          grep 'E: Failed to fetch' $CHECKLOG/software.log >> $LOGFILE && ERROR=4
523          grep 'Unable to write mmap - msync (28 No space left on device)' $CHECKLOG/software.log >> $LOGFILE && ERROR=5
524       fi
525
526       if [ -r "$CHECKLOG/shell.log" ] ; then
527          grep 'FAILED with exit code' $CHECKLOG/shell.log >> $LOGFILE && ERROR=2
528       fi
529
530       if [ -n "$ERROR" ] ; then
531          log "There was an error [${ERROR}] during execution of stage 'fai dirinstall' [$(date)]"
532          eerror "There was an error during execution of stage 'fai dirinstall'"
533          echo "   Check out ${CHECKLOG}/ for details. [exit ${ERROR}]"
534          eend 1
535          bailout 1
536       else
537          log "Finished execution of stage 'fai dirinstall' [$(date)]"
538          einfo "Finished execution of stage 'fai dirinstall'"
539       fi
540
541    fi
542 fi # BUILD_DIRTY?
543 # }}}
544
545 # BUILD_OUTPUT - execute arch specific stuff and squashfs {{{
546 [ -n "$BUILD_OUTPUT" ] || BUILD_OUTPUT="$OUTPUT/grml_cd"
547 mkdir -p "$BUILD_OUTPUT" || bailout 6 "Problem with creating $BUILD_OUTPUT for stage ARCH"
548
549 # i386:
550 if [ "$ARCH" = i386 ] || [ "$ARCH" = amd64 ] ; then
551    if [ -d "$BUILD_OUTPUT"/boot -a -z "$UPDATE" -a -z "$BUILD_ONLY" ] ; then
552       log "$BUILD_OUTPUT/boot exists already, skipping stage 'boot'"
553       ewarn "$BUILD_OUTPUT/boot exists already, skipping stage 'boot'" ; eend 0
554    else
555       # booting stuff:
556       [ -d "$BUILD_OUTPUT"/boot/isolinux ] || mkdir -p "$BUILD_OUTPUT"/boot/isolinux
557       [ -d "$BUILD_OUTPUT"/boot/"${SHORT_GRML_NAME}" ] || mkdir -p "$BUILD_OUTPUT"/boot/"${SHORT_GRML_NAME}"
558
559       if [ -z "$NO_ADDONS" ] ; then
560          [ -d "$BUILD_OUTPUT"/boot/addons   ] || mkdir -p "$BUILD_OUTPUT"/boot/addons
561          cp /boot/memtest86+.bin "$BUILD_OUTPUT"/boot/addons/memtest
562       fi
563
564       # if we don't have an initrd we a) can't boot and b) there was an error
565       # during build, so check for the file:
566       INITRD="$(ls $CHROOT_OUTPUT/boot/initrd* 2>/dev/null| grep -v '.bak$' | sort -r | head -1)"
567       if [ -n "$INITRD" ] ; then
568          cp $INITRD "$BUILD_OUTPUT"/boot/"${SHORT_GRML_NAME}"/initrd.gz
569          find $CHROOT_OUTPUT/boot/ -name initrd\*.bak -exec rm {} \;
570       else
571          log "No initrd found inside $CHROOT_OUTPUT/boot/ - Exiting"
572          eerror "No initrd found inside $CHROOT_OUTPUT/boot/ - Exiting" ; eend 1
573          bailout 10
574       fi
575
576       KERNEL_IMAGE="$(ls $CHROOT_OUTPUT/boot/vmlinuz* 2>/dev/null | sort -r | head -1)"
577       if [ -n "$KERNEL_IMAGE" ] ; then
578          cp "$KERNEL_IMAGE" "$BUILD_OUTPUT"/boot/"${SHORT_GRML_NAME}"/linux26
579       else
580          log "No kernel found inside $CHROOT_OUTPUT/boot/ - Exiting"
581          eerror "No kernel found inside $CHROOT_OUTPUT/boot/ - Exiting" ; eend 1
582          bailout 11
583       fi
584
585       [ -n "$TEMPLATE_DIRECTORY" ] || TEMPLATE_DIRECTORY='/usr/share/grml-live/templates'
586       if ! [ -d "${TEMPLATE_DIRECTORY}"/boot ] ; then
587          log "${TEMPLATE_DIRECTORY}/boot does not exist. Exiting."
588          eerror "${TEMPLATE_DIRECTORY}/boot does not exist. Exiting." ; eend 1
589          bailout 8
590       fi
591
592       cp ${TEMPLATE_DIRECTORY}/boot/isolinux/*  "$BUILD_OUTPUT"/boot/isolinux/
593
594       if [ -z "$NO_ADDONS" ] ; then
595          if ! [ -d /usr/share/grml-live/templates/boot/addons/bsd4grml ] ; then
596            ewarn "Boot addons not found, skipping therefore. (Consider installing package grml-live-addons)" ; eend 0
597          else
598            # copy only files so we can handle bsd4grml on its own
599            for file in ${TEMPLATE_DIRECTORY}/boot/addons/* ; do
600                test -f $file && cp $file "$BUILD_OUTPUT"/boot/addons/
601            done
602
603            if [ -z "$NO_ADDONS_BSD4GRML" ] ; then
604               cp -a ${TEMPLATE_DIRECTORY}/boot/addons/bsd4grml "$BUILD_OUTPUT"/boot/addons/
605            fi
606          fi
607       fi
608
609       if ! [ -d "${BUILD_OUTPUT}/boot/grub" ] ; then
610          cp -a ${TEMPLATE_DIRECTORY}/boot/grub  "$BUILD_OUTPUT"/boot/
611       fi
612       # make sure we have recent template files available, otherwise updating
613       # the strings like $GRML_NAME and $VERSION might be out of date
614       cp ${TEMPLATE_DIRECTORY}/boot/grub/* "$BUILD_OUTPUT"/boot/grub/
615
616       if ! [ -d "${TEMPLATE_DIRECTORY}"/GRML ] ; then
617          log "${TEMPLATE_DIRECTORY}/GRML does not exist. Exiting."
618          eerror "${TEMPLATE_DIRECTORY}/GRML does not exist. Exiting." ; eend 1
619          bailout 9
620       fi
621
622       [ -d "$BUILD_OUTPUT"/GRML ] || mkdir "$BUILD_OUTPUT"/GRML
623       cp -a ${TEMPLATE_DIRECTORY}/GRML/* "$BUILD_OUTPUT"/GRML/
624
625       # adjust boot splash information:
626       RELEASE_INFO="$GRML_NAME $VERSION - Release Codename $RELEASENAME"
627       RELEASE_INFO="$(cut_string 68 "$RELEASE_INFO")"
628       RELEASE_INFO="$(extend_string_end 68 "$RELEASE_INFO")"
629
630       sed -i "s/%RELEASE_INFO%/$GRML_NAME $VERSION - $RELEASENAME/" "$BUILD_OUTPUT"/GRML/grml-version
631       sed -i "s/%DATE%/$ISO_DATE/"             "$BUILD_OUTPUT"/GRML/grml-version
632
633       sed -i "s/%RELEASE_INFO%/$RELEASE_INFO/" "$BUILD_OUTPUT"/boot/isolinux/boot.msg
634       sed -i "s/%DATE%/$ISO_DATE/"             "$BUILD_OUTPUT"/boot/isolinux/boot.msg
635
636       sed -i "s/%GRML_NAME%/$SHORT_GRML_NAME/" "$BUILD_OUTPUT"/boot/isolinux/isolinux.cfg
637       sed -i "s/%GRML_NAME%/$SHORT_GRML_NAME/" "$BUILD_OUTPUT"/boot/isolinux/syslinux.cfg
638
639       sed -i "s/%RELEASE_INFO%/$RELEASE_INFO/" "$BUILD_OUTPUT"/boot/isolinux/boot-beep.msg
640       sed -i "s/%DATE%/$ISO_DATE/"             "$BUILD_OUTPUT"/boot/isolinux/boot-beep.msg
641
642       sed -i "s/%VERSION%/$VERSION/"           "$BUILD_OUTPUT"/boot/grub/menu.lst
643       sed -i "s/%GRML_NAME%/$SHORT_GRML_NAME/" "$BUILD_OUTPUT"/boot/grub/menu.lst
644
645       sed -i "s/%VERSION%/$VERSION/"           "$BUILD_OUTPUT"/boot/grub/grub.cfg
646       sed -i "s/%GRML_NAME%/$SHORT_GRML_NAME/" "$BUILD_OUTPUT"/boot/grub/grub.cfg
647
648       # make sure the squashfs filename is set accordingly:
649       GRML_NAME_SQUASHFS="$GRML_NAME.squashfs"
650       sed -i "s/%GRML_NAME_SQUASHFS%/$GRML_NAME_SQUASHFS/" "$BUILD_OUTPUT"/boot/isolinux/isolinux.cfg
651       sed -i "s/%GRML_NAME_SQUASHFS%/$GRML_NAME_SQUASHFS/" "$BUILD_OUTPUT"/boot/isolinux/syslinux.cfg
652       sed -i "s/%GRML_NAME_SQUASHFS%/$GRML_NAME_SQUASHFS/" "$BUILD_OUTPUT"/boot/grub/menu.lst
653
654       GRML_NAME_SQUASHFS="$(cut_string 20 "$GRML_NAME_SQUASHFS")"
655       GRML_NAME_SQUASHFS="$(extend_string_end 20 "$GRML_NAME_SQUASHFS")"
656       sed -i "s/%GRML_NAME_SQUASHFS%/$GRML_NAME_SQUASHFS/" "$BUILD_OUTPUT"/boot/isolinux/f4
657       sed -i "s/%GRML_NAME_SQUASHFS%/$GRML_NAME_SQUASHFS/" "$BUILD_OUTPUT"/boot/isolinux/f5
658
659       # jump back to grub from bsd4grml:
660       if [ -e "$BUILD_OUTPUT"/boot/addons/bsd4grml/boot.6 ]; then
661          if [ -e "$BUILD_OUTPUT"/boot/grub/core.img ]; then
662             GRUB_VERSION=2
663          else
664             GRUB_VERSION=1
665          fi
666          if [ -e "$BUILD_OUTPUT"/boot/grub/stage2 ]; then
667             GRUB_LEGACY=stage2
668          else
669             GRUB_LEGACY=stage2_eltorito
670          fi
671
672          # why not ed(1)?
673          for file in "$BUILD_OUTPUT"/boot/addons/bsd4grml/boot.6 \
674                      "$BUILD_OUTPUT"/boot/addons/bsd4grml/boot.cfg; do
675              sed -i -e "s!%GRUB_VERSION%!$GRUB_VERSION!g" \
676                     -e "s!%GRUB_LEGACY%!$GRUB_LEGACY!g" "$file"
677          done
678       fi
679
680       # autostart for Windows:
681       if [ -d "${TEMPLATE_DIRECTORY}/windows/autostart/" ] ; then
682          cp ${TEMPLATE_DIRECTORY}/windows/autostart/* "$BUILD_OUTPUT"/
683       fi
684
685       # windows-binaries:
686       if [ -n "$WINDOWS_BINARIES" ] ; then
687          if [ -f "$BUILD_OUTPUT"/windows/putty.exe ] ; then
688             log "$BUILD_OUTPUT/windows exists already, skipping stage 'WINDOWS_BINARIES'"
689             ewarn "$BUILD_OUTPUT/windows exists already, skipping stage 'WINDOWS_BINARIES'" ; eend 0
690          else
691             if ! [ -d "$BUILD_OUTPUT"/windows ] ; then
692                mkdir "$BUILD_OUTPUT"/windows
693                ( cd "$BUILD_OUTPUT"/windows
694                  for file in pageant plink pscp psftp putty puttygen ; do
695                     wget -O ${file}.exe ${WINDOWS_BINARIES}/${file}.exe
696                     md5sum ${file}.exe > ${file}.exe.md5
697                  done )
698             fi
699          fi
700       log "Finished execution of stage 'WINDOWS_BINARIES' [$(date)]"
701       einfo "Finished execution of stage 'WINDOWS_BINARIES'" ; eend 0
702       fi
703    einfo "Finished execution of stage 'boot'" ; eend 0
704    fi
705 # ppc:
706 elif [ "$ARCH" = powerpc ] ; then
707     ewarn 'Warning: formorer, it is your turn. :)'>&2
708 # unsuported:
709 else
710    eerror 'Error: Unsupported ARCH, sorry. Want to support it? Contribute!' ; eend 1
711 fi
712
713 if [ -f "$BUILD_OUTPUT"/live/${GRML_NAME}.squashfs -a -z "$UPDATE" -a -z "$BUILD_ONLY" -a -z "$BUILD_DIRTY" ] ; then
714    log "$BUILD_OUTPUT/live exists already, skipping stage 'squashfs'"
715    ewarn "$BUILD_OUTPUT/live exists already, skipping stage 'squashfs'" ; eend 0
716 elif [ -n "$SKIP_MKSQUASHFS" ] ; then
717    log "Skipping stage 'squashfs' as requested via option -q"
718    ewarn "Skipping stage 'squashfs' as requested via option -q" ; eend 0
719 else
720    [ -d "$BUILD_OUTPUT"/live ] || mkdir "$BUILD_OUTPUT"/live
721    # make sure we don't leave (even an empty) base.tgz:
722    [ -f "$CHROOT_OUTPUT/base.tgz" ] && rm -f "$CHROOT_OUTPUT/base.tgz"
723
724    # make sure mksquashfs can handle the according option:
725    if [ -n "$SQUASHFS_ZLIB" ] ; then
726       mksquashfs --help 2>&1 | grep -q -- "$SQUASHFS_ZLIB" || SQUASHFS_ZLIB=''
727    fi
728
729    if echo "$SQUASHFS_OPTIONS" | grep -q -- "-nolzma" ; then
730       if ! mksquashfs --help 2>&1 | grep -q -- '-nolzma' ; then
731          ewarn "mksquashfs does NOT support the nolzma option, just using default zlib mode."
732          SQUASHFS_OPTIONS="$(echo $SQUASHFS_OPTIONS | sed 's/-nolzma//g')"
733          eend 0
734       fi
735    fi
736
737    if echo "$SQUASHFS_OPTIONS" | grep -q -- "-lzma" ; then
738       if ! mksquashfs --help 2>&1 | grep -q -- '-lzma' ; then
739          ewarn "mksquashfs does NOT support the lzma option, falling back to zlib mode."
740          SQUASHFS_OPTIONS="$(echo $SQUASHFS_OPTIONS | sed 's/-lzma//g')"
741          eend 0
742       fi
743    fi
744
745    # support exclusion of files via exclude-file:
746    if [ -n "$SQUASHFS_EXCLUDES_FILE" -a "$SQUASHFS_EXCLUDES_FILE" ] ; then
747       SQUASHFS_OPTIONS="$SQUASHFS_OPTIONS -ef $SQUASHFS_EXCLUDES_FILE"
748    fi
749
750    # get rid of unnecessary files when building grml-small for final release:
751    if echo "$CLASSES" | grep -q GRML_SMALL ; then
752       SQUASHFS_OPTIONS="$SQUASHFS_OUTPUT -e initrd.img* vmlinuz*"
753    fi
754
755    SQUASHFS_OUTPUT="$(mktemp -t grml-live.XXXXXX)"
756    log "mksquashfs $CHROOT_OUTPUT/* $BUILD_OUTPUT/live/${GRML_NAME}.squashfs -noappend $SQUASHFS_OPTIONS $SQUASHFS_ZLIB"
757    if mksquashfs $CHROOT_OUTPUT/* $BUILD_OUTPUT/live/"${GRML_NAME}".squashfs \
758       -noappend $SQUASHFS_OPTIONS $SQUASHFS_ZLIB 2>"${SQUASHFS_OUTPUT}" ; then
759       echo "${GRML_NAME}.squashfs" > $BUILD_OUTPUT/live/filesystem.module
760       log "Finished execution of stage 'squashfs' [$(date)]"
761       einfo "Finished execution of stage 'squashfs'" ; eend 0
762       rm -f "${SQUASHFS_OUTPUT}"
763    else
764       log "There was an error executing stage 'squashfs' [$(date)]:"
765       log "$(cat $SQUASHFS_OUTPUT)"
766       eerror "There was an error executing stage 'squashfs':" ; eend 1
767       cat "${SQUASHFS_OUTPUT}"
768       rm -f "${SQUASHFS_OUTPUT}"
769       bailout
770    fi
771 fi
772
773 # create md5sum file:
774 ( cd $BUILD_OUTPUT/GRML &&
775 find .. -type f -not -name md5sums -not -name isolinux.bin -exec md5sum {} \; > md5sums )
776 # }}}
777
778 # ISO_OUTPUT - mkisofs {{{
779 [ -n "$ISO_OUTPUT" ] || ISO_OUTPUT="$OUTPUT/grml_isos"
780 [ -n "$ISO_NAME" ] || ISO_NAME="${GRML_NAME}_${VERSION}.iso"
781
782 if [ "$BOOT_METHOD" = "isolinux" ] ; then
783    BOOT_FILE="boot/isolinux/isolinux.bin -c boot/isolinux/boot.cat"
784 elif [ "$BOOT_METHOD" = "grub" ] ; then
785    BOOT_FILE="boot/grub/stage2_eltorito"
786 fi
787
788 if [ -f "${ISO_OUTPUT}/${ISO_NAME}" -a -z "$UPDATE" -a -z "$BUILD_ONLY" -a -z "$BUILD_DIRTY" ] ; then
789    log "$ISO_OUTPUT exists already, skipping stage 'iso build'"
790    ewarn "$ISO_OUTPUT exists already, skipping stage 'iso build'" ; eend 0
791 else
792    mkdir -p "$ISO_OUTPUT" || bailout 6 "Problem with creating $ISO_OUTPUT for stage 'iso build'"
793
794    # support mkisofs as well as genisoimage
795    if which mkisofs >/dev/null 2>&1; then
796       MKISOFS='mkisofs'
797    elif which genisoimage >/dev/null 2>&1; then
798       MKISOFS='genisoimage'
799    else
800       log "Sorry, neither mkisofs nor genisoimage available - can not create ISO."
801       eerror "Sorry, neither mkisofs nor genisoimage available - can not create ISO." ; eend 1
802       bailout
803    fi
804
805    CURRENT_DIR=$(pwd)
806    if cd "$BUILD_OUTPUT" ; then
807       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} ."
808       "$MKISOFS" -V "${GRML_NAME} ${VERSION}" -publisher 'grml-live | grml.org' \
809               -l -r -J -no-emul-boot -boot-load-size 4 -boot-info-table    \
810               -b $BOOT_FILE -no-pad \
811               -o "${ISO_OUTPUT}/${ISO_NAME}" . ; RC=$?
812
813       # pad the output ISO to multiples of 256 KiB for partition table support
814       siz=$($getfilesize "${ISO_OUTPUT}/${ISO_NAME}")
815       cyls=$((siz / 512 / 32 / 16 + 1))         # C=$cyls H=16 S=32
816       siz=$((cyls * 16 * 32 * 512))             # size after padding
817       dd if=/dev/zero bs=1 count=1 seek=$((siz - 1)) \
818          of="${ISO_OUTPUT}/${ISO_NAME}" 2>/dev/null
819
820       # support disabling hybrid ISO image
821       if [ "$HYBRID_METHOD" = "disable" ] ; then\
822          log "Skipping creation of hybrid ISO file as requested via HYBRID_METHOD=disable"
823          einfo "Skipping creation of hybrid ISO file as requested via HYBRID_METHOD=disable"
824          eend 0
825       # use isohybrid only on request
826       elif [ "$HYBRID_METHOD" = "isohybrid" ] ; then
827          if ! which isohybrid >/dev/null 2>&1 ; then
828            bailout 12 "isohybrid binary not found - please install syslinux/syslinux-common"
829          else
830            log "Creating hybrid ISO file with isohybrid method"
831            einfo "Creating hybrid ISO file with isohybrid method"
832            isohybrid "${ISO_OUTPUT}/${ISO_NAME}"
833            eend $?
834          fi
835       # by default use our manifold boot method:
836       else
837          if ! [ -r boot/grub/core.img ] ; then
838            ewarn "boot/grub/core.img not found, not creating manifold boot ISO file"
839          else
840            log "Creating hybrid ISO file with manifold method"
841            einfo "Creating hybrid ISO file with manifold method"
842            echo 1 63 | \
843                mksh /usr/share/grml-live/scripts/bootgrub.mksh -A -M 1 -p 0x83 -g $cyls:16:32 | \
844                cat - boot/grub/core.img | \
845                dd conv=notrunc of="${ISO_OUTPUT}/${ISO_NAME}" conv=notrunc 2>/dev/null
846            eend $?
847          fi
848       fi
849
850       # generate md5sum and sha1sum of ISO if we are using class 'RELEASE':
851       case $CLASSES in *RELEASE*)
852          [ "$RC" = 0 ] && \
853          (
854            if cd $ISO_OUTPUT ; then
855              md5sum ${ISO_NAME} > ${ISO_NAME}.md5 && \
856              touch -r ${ISO_NAME} ${ISO_NAME}.md5
857              sha1sum ${ISO_NAME} > ${ISO_NAME}.sha1 && \
858              touch -r ${ISO_NAME} ${ISO_NAME}.sha1
859            fi
860          )
861          ;;
862       esac
863
864       cd $CURRENT_DIR
865    fi
866
867    if [ "$RC" = 0 ] ; then
868       log "Finished execution of stage 'iso build' [$(date)]"
869       einfo "Finished execution of stage 'iso build'" ; eend 0
870    else
871       log "There was an error ($RC) executing stage 'iso build' [$(date)]"
872       eerror "There was an error executing stage 'iso build'" ; eend 1
873       bailout $RC
874    fi
875 fi
876 # }}}
877
878 # finalize {{{
879 [ -n "$start_seconds" ] && SECONDS="$[$(cut -d . -f 1 /proc/uptime)-$start_seconds]" || SECONDS="unknown"
880 einfo "Successfully finished execution of $PN [running ${SECONDS} seconds]" ; eend 0
881 log "Successfully finished execution of $PN [running ${SECONDS} seconds]"
882 bailout 0
883 # }}}
884
885 ## END OF FILE #################################################################
886 # vim:foldmethod=marker ts=2 ft=sh ai expandtab tw=80 sw=3