29d19f973e3d637d32c621b407acae7c490b5423
[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 # set $ARCH
420 [ -n "$ARCH" ] || ARCH="$(dpkg --print-architecture)"
421 if grep -q -- 'FAI_DEBOOTSTRAP_OPTS.*--arch' "$NFSROOT_CONF" ; then
422    sed "s/--arch [a-z0-9]* /--arch $ARCH /" "$NFSROOT_CONF" | sponge "$NFSROOT_CONF"
423 else
424    sed "s|^FAI_DEBOOTSTRAP_OPTS=\"\(.*\)|FAI_DEBOOTSTRAP_OPTS=\"--arch $ARCH \1|" "$NFSROOT_CONF" | sponge "$NFSROOT_CONF"
425 fi
426 # }}}
427
428 # CHROOT_OUTPUT - execute FAI {{{
429 if [ -n "$BUILD_DIRTY" ]; then
430   einfo "Skipping FAI" ; eend 0
431 else
432    [ -n "$CHROOT_OUTPUT" ] || CHROOT_OUTPUT="$OUTPUT/grml_chroot"
433
434    if [ -n "$UPDATE" -o -n "$BUILD_ONLY" ] ; then
435       FAI_ACTION=softupdate
436    else
437       FAI_ACTION=dirinstall
438    fi
439
440    if [ -n "$UPDATE" -o -n "$BUILD_ONLY" ] ; then
441       if ! [ -r "$CHROOT_OUTPUT/etc/grml_version" ] ; then
442          log "Error: does not look like you have a working chroot. Updating/building not possible."
443          eerror "Error: does not look like you have a working chroot. Updating/building not possible. (Drop -u/-b option?)"
444          eend 1
445          bailout 20
446       fi
447    fi
448
449    if [ -d "$CHROOT_OUTPUT/bin" -a -z "$UPDATE" -a -z "$BUILD_ONLY" ] ; then
450       log "$CHROOT_OUTPUT exists already, skipping stage 'fai dirinstall'"
451       ewarn "$CHROOT_OUTPUT exists already, skipping stage 'fai dirinstall'" ; eend 0
452    else
453       mkdir -p "$CHROOT_OUTPUT" || bailout 5 "Problem with creating $CHROOT_OUTPUT for FAI"
454
455       if [ -n "${MIRROR_DIRECTORY}" ] ; then
456          mkdir -p "${CHROOT_OUTPUT}/${MIRROR_DIRECTORY}"
457          mount --bind "${MIRROR_DIRECTORY}" "${CHROOT_OUTPUT}/${MIRROR_DIRECTORY}"
458       fi
459
460       log "Executed FAI command line:"
461       log "BUILD_ONLY=$BUILD_ONLY fai $VERBOSE -C $GRML_FAI_CONFIG -c$CLASSES -u $HOSTNAME $FAI_ACTION $CHROOT_OUTPUT $FAI_ARGS"
462       BUILD_ONLY="$BUILD_ONLY" fai $VERBOSE -C "$GRML_FAI_CONFIG" -c"$CLASSES" -u \
463       "$HOSTNAME" $FAI_ACTION "$CHROOT_OUTPUT" $FAI_ARGS | tee -a $LOGFILE
464       RC="$PIPESTATUS" # notice: bash-only
465
466       if [ "$RC" != 0 ] ; then
467          log "Error while executing fai [exit code ${RC}]. Exiting."
468          eerror "Error while executing fai [exit code ${RC}]. Exiting." ; eend 1
469          bailout 1
470       else
471          log "Setting /etc/grml_version to $GRML_NAME $VERSION Release Codename $RELEASENAME [$ISO_DATE]"
472          echo "$GRML_NAME $VERSION Release Codename $RELEASENAME [$ISO_DATE]" > $CHROOT_OUTPUT/etc/grml_version
473          chmod 644 $CHROOT_OUTPUT/etc/grml_version
474          einfo "Rebuilding initramfs"
475          # make sure new /etc/grml_version reaches the initramfs:
476          chroot $CHROOT_OUTPUT update-initramfs -u -t
477          eend $?
478       fi
479
480       # Remove all FAI logs from chroot if class RELEASE is used:
481       if [ -f "$CHROOT_OUTPUT"/etc/grml_fai_release ] ; then
482          rm -rf "$CHROOT_OUTPUT"/var/log/fai/*
483       fi
484
485       # make sure we don't leave any mounts - FAI doesn't remove them always
486       umount $CHROOT_OUTPUT/proc 2>/dev/null || /bin/true
487       umount $CHROOT_OUTPUT/sys  2>/dev/null || /bin/true
488       umount $CHROOT_OUTPUT/dev/pts 2>/dev/null || /bin/true
489       umount $CHROOT_OUTPUT/dev 2>/dev/null || /bin/true
490
491       [ -n "$MIRROR_DIRECTORY" ] && umount "${CHROOT_OUTPUT}/${MIRROR_DIRECTORY}"
492
493       # notice: 'fai dirinstall' does not seem to exit appropriate, so:
494       ERROR=''
495       CHECKLOG=/var/log/fai/$HOSTNAME/last
496       if [ -r "$CHECKLOG/software.log" ] ; then
497          # 1 errors during executing of commands
498          grep 'dpkg: error processing' $CHECKLOG/software.log >> $LOGFILE && ERROR=1
499          grep 'E: Method http has died unexpectedly!' $CHECKLOG/software.log >> $LOGFILE && ERROR=2
500          grep 'ERROR: chroot' $CHECKLOG/software.log >> $LOGFILE && ERROR=3
501          grep 'E: Failed to fetch' $CHECKLOG/software.log >> $LOGFILE && ERROR=4
502          grep 'Unable to write mmap - msync (28 No space left on device)' $CHECKLOG/software.log >> $LOGFILE && ERROR=5
503       fi
504
505       if [ -r "$CHECKLOG/shell.log" ] ; then
506          grep 'FAILED with exit code' $CHECKLOG/shell.log >> $LOGFILE && ERROR=2
507       fi
508
509       if [ -n "$ERROR" ] ; then
510          log "There was an error [${ERROR}] during execution of stage 'fai dirinstall' [$(date)]"
511          eerror "There was an error during execution of stage 'fai dirinstall'"
512          echo "   Check out ${CHECKLOG}/ for details. [exit ${ERROR}]"
513          eend 1
514          bailout 1
515       else
516          log "Finished execution of stage 'fai dirinstall' [$(date)]"
517          einfo "Finished execution of stage 'fai dirinstall'"
518       fi
519
520    fi
521 fi # BUILD_DIRTY?
522 # }}}
523
524 # BUILD_OUTPUT - execute arch specific stuff and squashfs {{{
525 [ -n "$BUILD_OUTPUT" ] || BUILD_OUTPUT="$OUTPUT/grml_cd"
526 mkdir -p "$BUILD_OUTPUT" || bailout 6 "Problem with creating $BUILD_OUTPUT for stage ARCH"
527
528 # i386:
529 if [ "$ARCH" = i386 ] || [ "$ARCH" = amd64 ] ; then
530    if [ -d "$BUILD_OUTPUT"/boot -a -z "$UPDATE" -a -z "$BUILD_ONLY" ] ; then
531       log "$BUILD_OUTPUT/boot exists already, skipping stage 'boot'"
532       ewarn "$BUILD_OUTPUT/boot exists already, skipping stage 'boot'" ; eend 0
533    else
534       # booting stuff:
535       [ -d "$BUILD_OUTPUT"/boot/isolinux ] || mkdir -p "$BUILD_OUTPUT"/boot/isolinux
536       [ -d "$BUILD_OUTPUT"/boot/"${SHORT_GRML_NAME}" ] || mkdir -p "$BUILD_OUTPUT"/boot/"${SHORT_GRML_NAME}"
537
538       if [ -z "$NO_ADDONS" ] ; then
539          [ -d "$BUILD_OUTPUT"/boot/addons   ] || mkdir -p "$BUILD_OUTPUT"/boot/addons
540          cp /boot/memtest86+.bin "$BUILD_OUTPUT"/boot/addons/memtest
541       fi
542
543       # if we don't have an initrd we a) can't boot and b) there was an error
544       # during build, so check for the file:
545       INITRD="$(ls $CHROOT_OUTPUT/boot/initrd* 2>/dev/null| grep -v '.bak$' | sort -r | head -1)"
546       if [ -n "$INITRD" ] ; then
547          cp $INITRD "$BUILD_OUTPUT"/boot/"${SHORT_GRML_NAME}"/initrd.gz
548          find $CHROOT_OUTPUT/boot/ -name initrd\*.bak -exec rm {} \;
549       else
550          log "No initrd found inside $CHROOT_OUTPUT/boot/ - Exiting"
551          eerror "No initrd found inside $CHROOT_OUTPUT/boot/ - Exiting" ; eend 1
552          bailout 10
553       fi
554
555       KERNEL_IMAGE="$(ls $CHROOT_OUTPUT/boot/vmlinuz* 2>/dev/null | sort -r | head -1)"
556       if [ -n "$KERNEL_IMAGE" ] ; then
557          cp "$KERNEL_IMAGE" "$BUILD_OUTPUT"/boot/"${SHORT_GRML_NAME}"/linux26
558       else
559          log "No kernel found inside $CHROOT_OUTPUT/boot/ - Exiting"
560          eerror "No kernel found inside $CHROOT_OUTPUT/boot/ - Exiting" ; eend 1
561          bailout 11
562       fi
563
564       [ -n "$TEMPLATE_DIRECTORY" ] || TEMPLATE_DIRECTORY='/usr/share/grml-live/templates'
565       if ! [ -d "${TEMPLATE_DIRECTORY}"/boot ] ; then
566          log "${TEMPLATE_DIRECTORY}/boot does not exist. Exiting."
567          eerror "${TEMPLATE_DIRECTORY}/boot does not exist. Exiting." ; eend 1
568          bailout 8
569       fi
570
571       cp ${TEMPLATE_DIRECTORY}/boot/isolinux/*  "$BUILD_OUTPUT"/boot/isolinux/
572
573       if [ -z "$NO_ADDONS" ] ; then
574          if ! [ -d /usr/share/grml-live/templates/boot/addons/bsd4grml ] ; then
575            ewarn "Boot addons not found, skipping therefore. (Consider installing package grml-live-addons)" ; eend 0
576          else
577            # copy only files so we can handle bsd4grml on its own
578            for file in ${TEMPLATE_DIRECTORY}/boot/addons/* ; do
579                test -f $file && cp $file "$BUILD_OUTPUT"/boot/addons/
580            done
581
582            if [ -z "$NO_ADDONS_BSD4GRML" ] ; then
583               cp -a ${TEMPLATE_DIRECTORY}/boot/addons/bsd4grml "$BUILD_OUTPUT"/boot/addons/
584            fi
585          fi
586       fi
587
588       if ! [ -d "${BUILD_OUTPUT}/boot/grub" ] ; then
589          cp -a ${TEMPLATE_DIRECTORY}/boot/grub  "$BUILD_OUTPUT"/boot/
590       fi
591       # make sure we have recent template files available, otherwise updating
592       # the strings like $GRML_NAME and $VERSION might be out of date
593       cp ${TEMPLATE_DIRECTORY}/boot/grub/* "$BUILD_OUTPUT"/boot/grub/
594
595       if ! [ -d "${TEMPLATE_DIRECTORY}"/GRML ] ; then
596          log "${TEMPLATE_DIRECTORY}/GRML does not exist. Exiting."
597          eerror "${TEMPLATE_DIRECTORY}/GRML does not exist. Exiting." ; eend 1
598          bailout 9
599       fi
600
601       [ -d "$BUILD_OUTPUT"/GRML ] || mkdir "$BUILD_OUTPUT"/GRML
602       cp -a ${TEMPLATE_DIRECTORY}/GRML/* "$BUILD_OUTPUT"/GRML/
603
604       # adjust boot splash information:
605       RELEASE_INFO="$GRML_NAME $VERSION - Release Codename $RELEASENAME"
606       RELEASE_INFO="$(cut_string 68 "$RELEASE_INFO")"
607       RELEASE_INFO="$(extend_string_end 68 "$RELEASE_INFO")"
608
609       sed -i "s/%RELEASE_INFO%/$GRML_NAME $VERSION - $RELEASENAME/" "$BUILD_OUTPUT"/GRML/grml-version
610       sed -i "s/%DATE%/$ISO_DATE/"             "$BUILD_OUTPUT"/GRML/grml-version
611
612       sed -i "s/%RELEASE_INFO%/$RELEASE_INFO/" "$BUILD_OUTPUT"/boot/isolinux/boot.msg
613       sed -i "s/%DATE%/$ISO_DATE/"             "$BUILD_OUTPUT"/boot/isolinux/boot.msg
614
615       sed -i "s/%GRML_NAME%/$SHORT_GRML_NAME/" "$BUILD_OUTPUT"/boot/isolinux/isolinux.cfg
616       sed -i "s/%GRML_NAME%/$SHORT_GRML_NAME/" "$BUILD_OUTPUT"/boot/isolinux/syslinux.cfg
617
618       sed -i "s/%RELEASE_INFO%/$RELEASE_INFO/" "$BUILD_OUTPUT"/boot/isolinux/boot-beep.msg
619       sed -i "s/%DATE%/$ISO_DATE/"             "$BUILD_OUTPUT"/boot/isolinux/boot-beep.msg
620
621       sed -i "s/%VERSION%/$VERSION/"           "$BUILD_OUTPUT"/boot/grub/menu.lst
622       sed -i "s/%GRML_NAME%/$SHORT_GRML_NAME/" "$BUILD_OUTPUT"/boot/grub/menu.lst
623
624       sed -i "s/%VERSION%/$VERSION/"           "$BUILD_OUTPUT"/boot/grub/grub.cfg
625       sed -i "s/%GRML_NAME%/$SHORT_GRML_NAME/" "$BUILD_OUTPUT"/boot/grub/grub.cfg
626
627       # make sure the squashfs filename is set accordingly:
628       GRML_NAME_SQUASHFS="$GRML_NAME.squashfs"
629       sed -i "s/%GRML_NAME_SQUASHFS%/$GRML_NAME_SQUASHFS/" "$BUILD_OUTPUT"/boot/isolinux/isolinux.cfg
630       sed -i "s/%GRML_NAME_SQUASHFS%/$GRML_NAME_SQUASHFS/" "$BUILD_OUTPUT"/boot/isolinux/syslinux.cfg
631       sed -i "s/%GRML_NAME_SQUASHFS%/$GRML_NAME_SQUASHFS/" "$BUILD_OUTPUT"/boot/grub/menu.lst
632
633       GRML_NAME_SQUASHFS="$(cut_string 20 "$GRML_NAME_SQUASHFS")"
634       GRML_NAME_SQUASHFS="$(extend_string_end 20 "$GRML_NAME_SQUASHFS")"
635       sed -i "s/%GRML_NAME_SQUASHFS%/$GRML_NAME_SQUASHFS/" "$BUILD_OUTPUT"/boot/isolinux/f4
636       sed -i "s/%GRML_NAME_SQUASHFS%/$GRML_NAME_SQUASHFS/" "$BUILD_OUTPUT"/boot/isolinux/f5
637
638       # jump back to grub from bsd4grml:
639       if [ -e "$BUILD_OUTPUT"/boot/addons/bsd4grml/boot.6 ]; then
640          if [ -e "$BUILD_OUTPUT"/boot/grub/core.img ]; then
641             GRUB_VERSION=2
642          else
643             GRUB_VERSION=1
644          fi
645          if [ -e "$BUILD_OUTPUT"/boot/grub/stage2 ]; then
646             GRUB_LEGACY=stage2
647          else
648             GRUB_LEGACY=stage2_eltorito
649          fi
650
651          # why not ed(1)?
652          for file in "$BUILD_OUTPUT"/boot/addons/bsd4grml/boot.6 \
653                      "$BUILD_OUTPUT"/boot/addons/bsd4grml/boot.cfg; do
654              sed -i -e "s!%GRUB_VERSION%!$GRUB_VERSION!g" \
655                     -e "s!%GRUB_LEGACY%!$GRUB_LEGACY!g" "$file"
656          done
657       fi
658
659       # autostart for Windows:
660       if [ -d "${TEMPLATE_DIRECTORY}/windows/autostart/" ] ; then
661          cp ${TEMPLATE_DIRECTORY}/windows/autostart/* "$BUILD_OUTPUT"/
662       fi
663
664       # windows-binaries:
665       if [ -n "$WINDOWS_BINARIES" ] ; then
666          if [ -f "$BUILD_OUTPUT"/windows/putty.exe ] ; then
667             log "$BUILD_OUTPUT/windows exists already, skipping stage 'WINDOWS_BINARIES'"
668             ewarn "$BUILD_OUTPUT/windows exists already, skipping stage 'WINDOWS_BINARIES'" ; eend 0
669          else
670             if ! [ -d "$BUILD_OUTPUT"/windows ] ; then
671                mkdir "$BUILD_OUTPUT"/windows
672                ( cd "$BUILD_OUTPUT"/windows
673                  for file in pageant plink pscp psftp putty puttygen ; do
674                     wget -O ${file}.exe ${WINDOWS_BINARIES}/${file}.exe
675                     md5sum ${file}.exe > ${file}.exe.md5
676                  done )
677             fi
678          fi
679       log "Finished execution of stage 'WINDOWS_BINARIES' [$(date)]"
680       einfo "Finished execution of stage 'WINDOWS_BINARIES'" ; eend 0
681       fi
682    einfo "Finished execution of stage 'boot'" ; eend 0
683    fi
684 # ppc:
685 elif [ "$ARCH" = powerpc ] ; then
686     ewarn 'Warning: formorer, it is your turn. :)'>&2
687 # unsuported:
688 else
689    eerror 'Error: Unsupported ARCH, sorry. Want to support it? Contribute!' ; eend 1
690 fi
691
692 if [ -f "$BUILD_OUTPUT"/live/${GRML_NAME}.squashfs -a -z "$UPDATE" -a -z "$BUILD_ONLY" -a -z "$BUILD_DIRTY" ] ; then
693    log "$BUILD_OUTPUT/live exists already, skipping stage 'squashfs'"
694    ewarn "$BUILD_OUTPUT/live exists already, skipping stage 'squashfs'" ; eend 0
695 elif [ -n "$SKIP_MKSQUASHFS" ] ; then
696    log "Skipping stage 'squashfs' as requested via option -q"
697    ewarn "Skipping stage 'squashfs' as requested via option -q" ; eend 0
698 else
699    [ -d "$BUILD_OUTPUT"/live ] || mkdir "$BUILD_OUTPUT"/live
700    # make sure we don't leave (even an empty) base.tgz:
701    [ -f "$CHROOT_OUTPUT/base.tgz" ] && rm -f "$CHROOT_OUTPUT/base.tgz"
702
703    # make sure mksquashfs can handle the according option:
704    if [ -n "$SQUASHFS_ZLIB" ] ; then
705       mksquashfs --help 2>&1 | grep -q -- "$SQUASHFS_ZLIB" || SQUASHFS_ZLIB=''
706    fi
707
708    if echo "$SQUASHFS_OPTIONS" | grep -q -- "-nolzma" ; then
709       if ! mksquashfs --help 2>&1 | grep -q -- '-nolzma' ; then
710          ewarn "mksquashfs does NOT support the nolzma option, just using default zlib mode."
711          SQUASHFS_OPTIONS="$(echo $SQUASHFS_OPTIONS | sed 's/-nolzma//g')"
712          eend 0
713       fi
714    fi
715
716    if echo "$SQUASHFS_OPTIONS" | grep -q -- "-lzma" ; then
717       if ! mksquashfs --help 2>&1 | grep -q -- '-lzma' ; then
718          ewarn "mksquashfs does NOT support the lzma option, falling back to zlib mode."
719          SQUASHFS_OPTIONS="$(echo $SQUASHFS_OPTIONS | sed 's/-lzma//g')"
720          eend 0
721       fi
722    fi
723
724    # support exclusion of files via exclude-file:
725    if [ -n "$SQUASHFS_EXCLUDES_FILE" -a "$SQUASHFS_EXCLUDES_FILE" ] ; then
726       SQUASHFS_OPTIONS="$SQUASHFS_OPTIONS -ef $SQUASHFS_EXCLUDES_FILE"
727    fi
728
729    # get rid of unnecessary files when building grml-small for final release:
730    if echo "$CLASSES" | grep -q GRML_SMALL ; then
731       SQUASHFS_OPTIONS="$SQUASHFS_OUTPUT -e initrd.img* vmlinuz*"
732    fi
733
734    SQUASHFS_OUTPUT="$(mktemp -t grml-live.XXXXXX)"
735    log "mksquashfs $CHROOT_OUTPUT/* $BUILD_OUTPUT/live/${GRML_NAME}.squashfs -noappend $SQUASHFS_OPTIONS $SQUASHFS_ZLIB"
736    if mksquashfs $CHROOT_OUTPUT/* $BUILD_OUTPUT/live/"${GRML_NAME}".squashfs \
737       -noappend $SQUASHFS_OPTIONS $SQUASHFS_ZLIB 2>"${SQUASHFS_OUTPUT}" ; then
738       echo "${GRML_NAME}.squashfs" > $BUILD_OUTPUT/live/filesystem.module
739       log "Finished execution of stage 'squashfs' [$(date)]"
740       einfo "Finished execution of stage 'squashfs'" ; eend 0
741       rm -f "${SQUASHFS_OUTPUT}"
742    else
743       log "There was an error executing stage 'squashfs' [$(date)]:"
744       log "$(cat $SQUASHFS_OUTPUT)"
745       eerror "There was an error executing stage 'squashfs':" ; eend 1
746       cat "${SQUASHFS_OUTPUT}"
747       rm -f "${SQUASHFS_OUTPUT}"
748       bailout
749    fi
750 fi
751
752 # create md5sum file:
753 ( cd $BUILD_OUTPUT/GRML &&
754 find .. -type f -not -name md5sums -not -name isolinux.bin -exec md5sum {} \; > md5sums )
755 # }}}
756
757 # ISO_OUTPUT - mkisofs {{{
758 [ -n "$ISO_OUTPUT" ] || ISO_OUTPUT="$OUTPUT/grml_isos"
759 [ -n "$ISO_NAME" ] || ISO_NAME="${GRML_NAME}_${VERSION}.iso"
760
761 if [ "$BOOT_METHOD" = "isolinux" ] ; then
762    BOOT_FILE="boot/isolinux/isolinux.bin -c boot/isolinux/boot.cat"
763 elif [ "$BOOT_METHOD" = "grub" ] ; then
764    BOOT_FILE="boot/grub/stage2_eltorito"
765 fi
766
767 if [ -f "${ISO_OUTPUT}/${ISO_NAME}" -a -z "$UPDATE" -a -z "$BUILD_ONLY" -a -z "$BUILD_DIRTY" ] ; then
768
769    # support mkisofs as well as genisoimage
770    if which mkisofs >/dev/null 2>&1; then
771       MKISOFS=mkisofs
772    elif which genisoimage >/dev/null 2>&1; then
773       MKISOFS=genisoimage
774    else
775       log "Sorry, neither mkisofs nor genisoimage available - can not create ISO."
776       eerror "Sorry, neither mkisofs nor genisoimage available - can not create ISO." ; eend 1
777       bailout
778    fi
779
780    log "$ISO_OUTPUT exists already, skipping stage 'iso build'"
781    ewarn "$ISO_OUTPUT exists already, skipping stage 'iso build'" ; eend 0
782 else
783    mkdir -p "$ISO_OUTPUT" || bailout 6 "Problem with creating $ISO_OUTPUT for stage 'iso build'"
784
785    CURRENT_DIR=$(pwd)
786    if cd "$BUILD_OUTPUT" ; then
787       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} ."
788       $MKISOFS -V "${GRML_NAME} ${VERSION}" -publisher 'grml-live | grml.org' \
789               -l -r -J -no-emul-boot -boot-load-size 4 -boot-info-table    \
790               -b $BOOT_FILE -no-pad \
791               -o "${ISO_OUTPUT}/${ISO_NAME}" . ; RC=$?
792
793       # pad the output ISO to multiples of 256 KiB for partition table support
794       siz=$($getfilesize "${ISO_OUTPUT}/${ISO_NAME}")
795       cyls=$((siz / 512 / 32 / 16 + 1))         # C=$cyls H=16 S=32
796       siz=$((cyls * 16 * 32 * 512))             # size after padding
797       dd if=/dev/zero bs=1 count=1 seek=$((siz - 1)) \
798          of="${ISO_OUTPUT}/${ISO_NAME}" 2>/dev/null
799
800       # support disabling hybrid ISO image
801       if [ "$HYBRID_METHOD" = "disable" ] ; then\
802          log "Skipping creation of hybrid ISO file as requested via HYBRID_METHOD=disable"
803          einfo "Skipping creation of hybrid ISO file as requested via HYBRID_METHOD=disable"
804          eend 0
805       # use isohybrid only on request
806       elif [ "$HYBRID_METHOD" = "isohybrid" ] ; then
807          if ! which isohybrid >/dev/null 2>&1 ; then
808            bailout 12 "isohybrid binary not found - please install syslinux/syslinux-common"
809          else
810            log "Creating hybrid ISO file with isohybrid method"
811            einfo "Creating hybrid ISO file with isohybrid method"
812            isohybrid "${ISO_OUTPUT}/${ISO_NAME}"
813            eend $?
814          fi
815       # by default use our manifold boot method:
816       else
817          if ! [ -r boot/grub/core.img ] ; then
818            ewarn "boot/grub/core.img not found, not creating manifold boot ISO file"
819          else
820            log "Creating hybrid ISO file with manifold method"
821            einfo "Creating hybrid ISO file with manifold method"
822            echo 1 63 | \
823                mksh /usr/share/grml-live/scripts/bootgrub.mksh -A -M 1 -p 0x83 -g $cyls:16:32 | \
824                cat - boot/grub/core.img | \
825                dd conv=notrunc of="${ISO_OUTPUT}/${ISO_NAME}" conv=notrunc 2>/dev/null
826            eend $?
827          fi
828       fi
829
830       # generate md5sum and sha1sum of ISO if we are using class 'RELEASE':
831       case $CLASSES in *RELEASE*)
832          [ "$RC" = 0 ] && \
833          (
834            if cd $ISO_OUTPUT ; then
835              md5sum ${ISO_NAME} > ${ISO_NAME}.md5 && \
836              touch -r ${ISO_NAME} ${ISO_NAME}.md5
837              sha1sum ${ISO_NAME} > ${ISO_NAME}.sha1 && \
838              touch -r ${ISO_NAME} ${ISO_NAME}.sha1
839            fi
840          )
841          ;;
842       esac
843
844       cd $CURRENT_DIR
845    fi
846
847    if [ "$RC" = 0 ] ; then
848       log "Finished execution of stage 'iso build' [$(date)]"
849       einfo "Finished execution of stage 'iso build'" ; eend 0
850    else
851       log "There was an error ($RC) executing stage 'iso build' [$(date)]"
852       eerror "There was an error executing stage 'iso build'" ; eend 1
853       bailout $RC
854    fi
855 fi
856 # }}}
857
858 # finalize {{{
859 [ -n "$start_seconds" ] && SECONDS="$[$(cut -d . -f 1 /proc/uptime)-$start_seconds]" || SECONDS="unknown"
860 einfo "Sucessfully finished execution of $PN [running ${SECONDS} seconds]" ; eend 0
861 log "Sucessfully finished execution of $PN [running ${SECONDS} seconds]"
862 bailout 0
863 # }}}
864
865 ## END OF FILE #################################################################
866 # vim:foldmethod=marker ts=2 ft=sh ai expandtab tw=80 sw=3