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