Merge branch 'ch/installrescue'
[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 # disable for now since it seems to cause some problems
24 # set -e
25
26 # global variables
27 GRML_LIVE_VERSION='0.16.1'
28 PN="$(basename $0)"
29 CMDLINE="$0 $@"
30 ADDONS_LIST_FILE='/boot/isolinux/addons_list.cfg'
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    -d <date>               use specified date instead of build time as date of release
47    -D <configdir>          use specified configuration directory instead of /etc/grml/fai
48    -F                      force execution without prompting
49    -g <grml_name>          set the grml flavour name
50    -h                      display short usage information and exit
51    -i <iso_name>           name of ISO
52    -I <src_directory>      directory which provides files that should become
53                            part of the chroot/ISO
54    -n                      skip generation of ISO
55    -N                      bootstrap (build chroot) only, do not create files for ISO
56    -o <output_directory>   main output directory of the build process
57    -q                      skip mksquashfs
58    -r <release_name>       release name
59    -s <suite>              Debian suite; values: etch, lenny, squeeze, sid
60    -t <template_directory> place of the templates
61    -u                      update existing chroot instead of rebuilding it from scratch
62    -v <version_number>     specify version number of the release
63    -V                      increase verbosity in the build process
64    -z                      use ZLIB instead of LZMA/XZ compression
65
66 Usage examples:
67
68     $PN
69     $PN -c GRMLBASE,GRML_MEDIUM,I386 -o /dev/shm/grml
70     $PN -c GRMLBASE,GRML_SMALL,REMOVE_DOCS,I386 -g grml-small -v 1.0
71     $PN -c GRMLBASE,GRML_FULL,I386 -i grml_0.0-1.iso -v 0.0-1
72     $PN -c GRMLBASE,GRML_FULL,I386 -s sid -V -r 'grml-live rocks'
73
74 More details: man grml-live + /usr/share/doc/grml-live/grml-live.html
75               http://grml.org/grml-live/
76
77 Please send your bug reports and feedback to the grml-team: http://grml.org/bugs/
78 "
79 }
80
81 # make sure it's possible to get usage information without being
82 # root or actually executing the script
83 if [ "$1" = '-h' -o "$1" = '--help' ] ; then
84    usage
85    [ "$(id -u 2>/dev/null)" != 0 ] && echo "Please notice that this script requires root permissions."
86    exit 0
87 fi
88 # }}}
89
90 # some runtime checks {{{
91 # we need root permissions for the build-process:
92 if [ "$(id -u 2>/dev/null)" != 0 ] ; then
93    echo "Error: please run this script with uid 0 (root)." >&2
94    exit 1
95 fi
96
97 if [ -r /var/run/fai/FAI_INSTALLATION_IN_PROGRESS ] ; then
98    echo "/usr/sbin/fai already running or was aborted before.">&2
99    echo "You may remove /var/run/fai/FAI_INSTALLATION_IN_PROGRESS and try again.">&2
100    exit 1
101 fi
102
103 # see #449236
104 if [ -r /var/run/fai/fai_softupdate_is_running ] ; then
105    echo "/usr/sbin/fai softupdate already running or was aborted before.">&2
106    echo "You may remove /var/run/fai/fai_softupdate_is_running and try again.">&2
107    exit 1
108 fi
109 # }}}
110
111 # lsb-functions and configuration stuff {{{
112 # make sure they are not set by default
113 VERBOSE=''
114 FORCE=''
115 UPDATE=''
116 BUILD_ONLY=''
117 BUILD_DIRTY=''
118 BOOTSTRAP_ONLY=''
119 HOSTNAME=''
120
121 if [ -r /etc/grml/lsb-functions ] ; then
122    . /etc/grml/lsb-functions
123 else
124    einfo()  { echo "  [*] $*" ;}
125    eerror() { echo "  [!] $*">&2 ;}
126    ewarn()  { echo "  [x] $*" ;}
127    eend()   { return 0 ;}
128    eindent()  { return 0 ;}
129    eoutdent() { return 0 ;}
130 fi
131
132 # source main configuration file:
133 LIVE_CONF=/etc/grml/grml-live.conf
134 . $LIVE_CONF
135 # }}}
136
137 # umount all directories {{{
138 umount_all() {
139    # make sure we don't leave any mounts - FAI doesn't remove them always
140    umount $CHROOT_OUTPUT/proc 2>/dev/null || /bin/true
141    umount $CHROOT_OUTPUT/sys  2>/dev/null || /bin/true
142    umount $CHROOT_OUTPUT/dev/pts 2>/dev/null || /bin/true
143    umount $CHROOT_OUTPUT/dev 2>/dev/null || /bin/true
144    [ -n "$MIRROR_DIRECTORY" ] && umount "${CHROOT_OUTPUT}/${MIRROR_DIRECTORY}"
145 }
146 # }}}
147
148 # clean exit {{{
149 bailout() {
150   rm -f /var/run/fai/fai_softupdate_is_running \
151         /var/run/fai/FAI_INSTALLATION_IN_PROGRESS
152   [ -n "$SQUASHFS_STDERR" ]  && rm -rf "$SQUASHFS_STDERR"
153   umount_all
154   [ -n "$1" ] && EXIT="$1" || EXIT="1"
155   [ -n "$2" ] && eerror "$2">&2
156   log "------------------------------------------------------------------------------"
157   exit "$EXIT"
158 }
159 trap bailout 1 2 3 3 6 9 14 15
160 trap umount_all EXIT
161 # }}}
162
163 # log file stuff {{{
164 [ -n "$LOGFILE" ] || LOGFILE=/var/log/grml-live.log
165 touch $LOGFILE
166 chown root:adm $LOGFILE
167 chmod 664 $LOGFILE
168 # }}}
169
170 # some important functions {{{
171
172 # log output:
173 # usage: log "string to log"
174 log() { echo "$*" >> $LOGFILE ; }
175
176 # cut string at character number int = $1
177 # usage: cut_string 5 "1234567890" will output "12345"
178 cut_string() {
179   [ -n "$2" ] || return 1
180   echo "$2" | head -c "$1"; echo -ne "\n"
181 }
182
183 # prepend int = $1 spaces before string = $2
184 # usage: extend_string_begin 5 "123" will output "  123"
185 extend_string_begin() {
186   [ -n "$2" ] || return 1
187   local COUNT="$(echo $2 | wc -c)"
188   local FILL="$(expr $COUNT - $1)"
189   while [ "$FILL" -gt 1 ] ; do
190     echo -n " "
191     local FILL=$(expr $FILL - 1)
192   done
193   while [ "$FILL" -lt 1 ] ; do
194     echo -n " "
195     local FILL=$(expr $FILL + 1)
196   done
197   echo "$2" | head -c "$1"; echo -ne "\n"
198 }
199
200 # append int = $1 spaces to string = $2
201 # usage: extend_string_begin 5 "123" will output "123  "
202 extend_string_end() {
203   [ -n "$2" ] || return 1
204   echo -n "$2" | head -c "$1"
205   local COUNT="$(echo $2 | wc -c)"
206   local FILL="$(expr $COUNT - $1)"
207   while [ "$FILL" -gt 1 ] ; do
208     echo -n " "
209     local FILL=$(expr $FILL - 1)
210   done
211   while [ "$FILL" -lt 1 ] ; do
212     echo -n " "
213     local FILL=$(expr $FILL + 1)
214   done
215   echo -ne "\n"
216 }
217
218 # Copy addonfile $1 from the TEMPLATE_DIRECTORY/compat (if exists),
219 # or from $2 (the system path), or warn about the missing file.
220 # This is because:
221 #   * On unstable, we Recommend the Debian packages containing
222 #     these files. The user can override them by putting his
223 #     "better" version into TEMPLATE_DIRECTORY/compat.
224 #   * On stable, the Debian packages are probably not available,
225 #     or outdated, so we look in TEMPLATE_DIRECTORY/compat first, where
226 #     our grml-live-compat package installs current file versions.
227 copy_addon_file() {
228   if [ ! -d "${BUILD_OUTPUT}/boot/$3/" ]; then
229     mkdir -p "${BUILD_OUTPUT}/boot/$3"
230   fi
231   if [ -e "${TEMPLATE_DIRECTORY}/compat/$3/$1" ]; then
232     cp "${TEMPLATE_DIRECTORY}/compat/$3/$1" "${BUILD_OUTPUT}/boot/$3/"
233     return $?
234   fi
235   if [ -e "$2/$1" ]; then
236     log   "Copying $1 from system"
237     cp "$2/$1" "${BUILD_OUTPUT}/boot/$3/"
238     return $?
239   fi
240
241   msg="Missing addon file: \"$1\""
242   ewarn "$msg" ; eend 1
243   log "copy_addon_file: $msg"
244 }
245 # }}}
246
247 # read local (non-packaged) configuration {{{
248 LOCAL_CONFIG=/etc/grml/grml-live.local
249 if [ -r "$LOCAL_CONFIG" ] ; then
250    log "Sourcing $LOCAL_CONFIG"
251    . $LOCAL_CONFIG
252 else
253    log "No $LOCAL_CONFIG found, not sourcing it"
254    LOCAL_CONFIG=''
255 fi
256 # }}}
257
258 # command line parsing {{{
259 while getopts "a:C:c:d:D:g:i:I:o:r:s:t:v:bBFnNquVz" opt; do
260   case "$opt" in
261     a) ARCH="$OPTARG" ;;
262     b) BUILD_ONLY=1 ;;
263     B) BUILD_DIRTY=1 ;;
264     c) CLASSES="$OPTARG" ;;
265     C) CONFIG="$OPTARG" ;;
266     d) DATE="$OPTARG" ;;
267     D) GRML_FAI_CONFIG="$OPTARG" ;;
268     g) GRML_NAME="$OPTARG" ;;
269     i) ISO_NAME="$OPTARG" ;;
270     I) CHROOT_INSTALL="$OPTARG" ;;
271     n) SKIP_MKISOFS=1 ;;
272     N) BOOTSTRAP_ONLY=1; SKIP_MKISOFS=1; SKIP_MKSQUASHFS=1 ;;
273     o) OUTPUT="$OPTARG" ;;
274     q) SKIP_MKSQUASHFS=1 ;;
275     r) RELEASENAME="$OPTARG" ;;
276     s) SUITE="$OPTARG" ;;
277     t) TEMPLATE_DIRECTORY="$OPTARG";;
278     v) VERSION="$OPTARG" ;;
279     F) FORCE=1 ;;
280     u) UPDATE=1 ;;
281     V) VERBOSE="-v" ;;
282     z) SQUASHFS_ZLIB=1 ;;
283     ?) echo "invalid option -$OPTARG" >&2; bailout 1 ;;
284   esac
285 done
286 shift $(($OPTIND - 1))  # set ARGV to the first not parsed commandline parameter
287 # }}}
288
289 # assume sane defaults (if not set already) {{{
290 [ -n "$ARCH" ]                    || ARCH="$(dpkg --print-architecture)"
291 [ -n "$BOOT_METHOD" ]             || BOOT_METHOD='isolinux'
292 [ -n "$CLASSES" ]                 || CLASSES="GRMLBASE,GRML_MEDIUM,I386"
293 [ -n "$DATE" ]                    || DATE="$(date +%Y-%m-%d)"
294 [ -n "$DISTRI_INFO" ]             || DISTRI_INFO='Grml - Live Linux for system administrators   '
295 [ -n "$DISTRI_NAME" ]             || DISTRI_NAME="grml"
296 [ -n "$DISTRI_SPLASH" ]           || DISTRI_SPLASH='grml.png'
297 [ -n "$FORCE_ISO_REBUILD" ]       || FORCE_ISO_REBUILD="false"
298 [ -n "$GRML_FAI_CONFIG" ]         || GRML_FAI_CONFIG='/etc/grml/fai'
299 [ -n "$GRML_NAME" ]               || GRML_NAME='grml'
300 [ -n "$HOSTNAME" ]                || HOSTNAME='grml'
301 [ -n "$HYBRID_METHOD" ]           || HYBRID_METHOD='manifold'
302 [ -n "$NFSROOT_CONF" ]            || NFSROOT_CONF="${GRML_FAI_CONFIG}/make-fai-nfsroot.conf"
303 [ -n "$RELEASENAME" ]             || RELEASENAME='grml-live rocks'
304 [ -n "$SOURCES_LIST_OUTPUT" ]     || SOURCES_LIST_OUTPUT="${GRML_FAI_CONFIG}/config/files/etc/apt/sources.list/GRML_LIVE_SOURCES_LIST"
305 [ -n "$SQUASHFS_EXCLUDES_FILE" ]  || SQUASHFS_EXCLUDES_FILE="${GRML_FAI_CONFIG}/config/grml/squashfs-excludes"
306 [ -n "$SUITE" ]                   || SUITE='squeeze'
307 [ -n "$TEMPLATE_DIRECTORY" ]      || TEMPLATE_DIRECTORY='/usr/share/grml-live/templates'
308 [ -n "$USERNAME" ]                || USERNAME='grml'
309 [ -n "$VERSION" ]                 || VERSION='0.0.1'
310
311 # output specific stuff, depends on $OUTPUT (iff not set):
312 [ -n "$OUTPUT" ]           || OUTPUT='/grml/grml-live'
313 [ -n "$BUILD_OUTPUT" ]     || BUILD_OUTPUT="$OUTPUT/grml_cd"
314 [ -n "$CHROOT_OUTPUT" ]    || CHROOT_OUTPUT="$OUTPUT/grml_chroot"
315 [ -n "$ISO_OUTPUT" ]       || ISO_OUTPUT="$OUTPUT/grml_isos"
316 # }}}
317
318 # some misc checks before executing FAI {{{
319 [ -n "$CLASSES" ] || bailout 1 "Error: \$CLASSES unset, please set it in $LIVE_CONF or
320 specify it on the command line using the -c option."
321 [ -n "$OUTPUT" ] || bailout 1 "Error: \$OUTPUT unset, please set it in $LIVE_CONF or
322 specify it on the command line using the -o option."
323
324 # trim characters that are known to cause problems inside $GRML_NAME;
325 # for example isolinux does not like '-' inside the directory name
326 [ -n "$GRML_NAME" ] && export SHORT_NAME="$(echo $GRML_NAME | tr -d ',./;\- ')"
327
328 # export variables to have them available in fai scripts:
329 [ -n "$GRML_NAME" ]   && export GRML_NAME="$GRML_NAME"
330 [ -n "$RELEASENAME" ] && export RELEASENAME="$RELEASENAME"
331 # }}}
332
333 # ZERO_LOGFILE - check for backwards compatibility reasons {{{
334 # this was default behaviour until grml-live 0.9.34:
335 if [ -n "$ZERO_LOGFILE" ] ; then
336    PRESERVE_LOGFILE='' # make sure it's cleaned then
337    ewarn "Please consider disabling the \$ZERO_LOGFILE option as grml-live clears..."
338    ewarn "... the logfile $LOGFILE by default (unless \$PRESERVE_LOGFILE is set) nowadays."
339    eend 0
340 fi
341 # }}}
342
343 # ask user whether the setup is ok {{{
344 if [ -z "$FORCE" ] ; then
345    echo
346    echo "${PN} [${GRML_LIVE_VERSION}]: check your configuration (or use -F to force execution):"
347    echo
348    echo "  FAI classes:       $CLASSES"
349    [ -r "$LOCAL_CONFIG" ]        && echo "  Local config:      /etc/grml/grml-live.local"
350    [ -n "$CONFIG" ]              && echo "  Configuration:     $CONFIG"
351    [ -n "$GRML_FAI_CONFIG" ]     && echo "  Config directory:  $GRML_FAI_CONFIG"
352    echo "  main directory:    $OUTPUT"
353    [ -n "$CHROOT_OUTPUT" ]       && echo "  Chroot target:     $CHROOT_OUTPUT"
354    [ -n "$BUILD_OUTPUT" ]        && echo "  Build target:      $BUILD_OUTPUT"
355    [ -n "$ISO_OUTPUT" ]          && echo "  ISO target:        $ISO_OUTPUT"
356    [ -n "$GRML_NAME" ]           && echo "  Grml name:         $GRML_NAME"
357    [ -n "$RELEASENAME" ]         && echo "  Release name:      $RELEASENAME"
358    [ -n "$DATE" ]                && echo "  Build date:        $DATE"
359    [ -n "$VERSION" ]             && echo "  Grml version:      $VERSION"
360    [ -n "$SUITE" ]               && echo "  Debian suite:      $SUITE"
361    [ -n "$ARCH" ]                && echo "  Architecture:      $ARCH"
362    [ -n "$BOOT_METHOD" ]         && echo "  Boot method:       $BOOT_METHOD"
363    [ -n "$HYBRID_METHOD" ]       && echo "  Hybrid method:     $HYBRID_METHOD"
364    [ -n "$TEMPLATE_DIRECTORY" ]  && echo "  Template files:    $TEMPLATE_DIRECTORY"
365    [ -n "$CHROOT_INSTALL" ]      && echo "  Install files from directory to chroot:  $CHROOT_INSTALL"
366    [ -n "$BOOTID" ]              && echo "  Boot identifier:   $BOOTID"
367    [ -n "$NO_BOOTID" ]           && echo "  Skipping bootid feature."
368    [ -n "$DEFAULT_BOOTOPTIONS" ] && echo "  Adding default bootoptions: \"$DEFAULT_BOOTOPTIONS\""
369    [ -n "$FAI_ARGS" ]            && echo "  Additional arguments for FAI: $FAI_ARGS"
370    [ -n "$LOGFILE" ]             && echo "  Logging to file:   $LOGFILE"
371    [ -n "$SQUASHFS_ZLIB" ]       && echo "  Using ZLIB (instead of LZMA/XZ) compression."
372    [ -n "$SQUASHFS_OPTIONS" ]    && echo "  Using SQUASHFS_OPTIONS ${SQUASHFS_OPTIONS}"
373    [ -n "$VERBOSE" ]             && echo "  Using VERBOSE mode."
374    [ -n "$UPDATE" ]              && echo "  Executing UPDATE instead of fresh installation."
375    if [ -n "$BOOTSTRAP_ONLY" ] ; then
376      echo "  Bootstrapping only and not building (files for) ISO."
377    else
378      [ -n "$SKIP_MKSQUASHFS" ]     && echo "  Skipping creation of SQUASHFS file."
379      [ -n "$SKIP_MKISOFS" ]        && echo "  Skipping creation of ISO file."
380      [ -n "$BUILD_ONLY" ]          && echo "  Executing BUILD_ONLY instead of fresh installation or UPDATE."
381      [ -n "$BUILD_DIRTY" ]         && echo "  Executing BUILD_DIRTY to leave chroot untouched."
382    fi
383    echo
384    echo -n "Is this ok for you? [y/N] "
385    read a
386    if ! [ "$a" = 'y' -o "$a" = 'Y' ] ; then
387       bailout 1 "Exiting as requested."
388    fi
389    echo
390 fi
391 # }}}
392
393 # clean/zero/remove logfiles {{{
394
395 if [ -n "$PRESERVE_LOGFILE" ] ; then
396    echo "Preserving logfile $LOGFILE as requested via \$PRESERVE_LOGFILE"
397 else
398    # make sure it is empty (as it is e.g. appended to grml-live-db)
399    echo -n > $LOGFILE
400 fi
401
402 if [ -n "$ZERO_FAI_LOGFILE" ] ; then
403    if [ -d /var/log/fai/"$HOSTNAME" ] ; then
404       rm -rf /var/log/fai/"$HOSTNAME"/"$(readlink /var/log/fai/"$HOSTNAME"/last)"
405       rm -rf /var/log/fai/"$HOSTNAME"/"$(readlink /var/log/fai/"$HOSTNAME"/last-dirinstall)"
406       rm -rf /var/log/fai/"$HOSTNAME"/"$(readlink /var/log/fai/"$HOSTNAME"/last-softupdate)"
407       rm -f /var/log/fai/"$HOSTNAME"/last \
408             /var/log/fai/"$HOSTNAME"/last-dirinstall \
409             /var/log/fai/"$HOSTNAME"/last-softupdate
410    fi
411 fi
412 # }}}
413
414 # source config and startup {{{
415 if [ -n "$CONFIG" ] ; then
416    if ! [ -f "$CONFIG" ] ; then
417       log    "Error: $CONFIG could not be read. Exiting. [$(date)]"
418       eerror "Error: $CONFIG could not be read. Exiting." ; eend 1
419       bailout 1
420    else
421       log "Sourcing $CONFIG"
422       . $CONFIG
423    fi
424 fi
425
426 start_seconds=$(cut -d . -f 1 /proc/uptime)
427 log "------------------------------------------------------------------------------"
428 log "Starting grml-live [${GRML_LIVE_VERSION}] run on $(date)"
429 log "Executed grml-live command line:"
430 log "$CMDLINE"
431
432 einfo "Logging actions to logfile $LOGFILE"
433 # }}}
434
435 # on-the-fly configuration {{{
436 mkdir -p "$(dirname $SOURCES_LIST_OUTPUT)" # might not be present in -D config space
437
438 cat > "$SOURCES_LIST_OUTPUT" << EOF
439 # NOTE: This file is *NOT* meant for manual customisation! This file is
440 # installed temporarily only by grml-live and will be overriden in the
441 # installation and configuration process then.
442 EOF
443
444 if [ -n "$MIRROR_DIRECTORY" ] ; then
445   if ! [ -d "$MIRROR_DIRECTORY/debian" ] ; then
446     log    "Error: $MIRROR_DIRECTORY/debian does not seem to exist. Exiting. [$(date)]"
447     eerror "Error: $MIRROR_DIRECTORY/debian does not seem to exist. Exiting." ; eend 1
448     bailout 1
449   fi
450   echo "$MIRROR_SOURCES" >> "$SOURCES_LIST_OUTPUT"
451 fi
452
453 if [ -n "$FAI_DEBOOTSTRAP" ] ; then
454   sed "s#^FAI_DEBOOTSTRAP=.*#FAI_DEBOOTSTRAP=\"$FAI_DEBOOTSTRAP\"#" "$NFSROOT_CONF" | sponge "$NFSROOT_CONF"
455 fi
456
457 # does this suck? YES!
458 # /usr/share/debootstrap/scripts/unstable does not exist, instead use 'sid':
459 case $SUITE in
460    unstable) SUITE='sid' ;;
461    # make sure that we *NEVER* write any broken suite name to sources.list,
462    # otherwise we won't be able to adjust it one next (correct) execution
463    stable)   ;;
464    testing)  ;;
465    etch)     ;;
466    lenny)    ;;
467    squeeze)  ;;
468    wheezy)   ;;
469    sid)      ;;
470    *) echo "Sorry, $SUITE is not a valid Debian suite, exiting.">&2; bailout 1 ;;
471 esac
472 export SUITE # make sure it's available in FAI scripts
473
474 if [ -n "${GRML_LIVE_SOURCES:-}" ] ; then
475   DIST=" etch\| stable\| lenny\| squeeze\| wheezy\| testing\| sid\| unstable"
476   echo "# generated based on \$GRML_LIVE_SOURCES by grml-live
477 $GRML_LIVE_SOURCES" | \
478     sed -e "s/\(^deb .\+\)\([ \t]*\)\($DIST\)\([ \t]*\)\(main \)/\1 \2$SUITE\4\5/;
479             s/\(^deb-src .\+\)\([ \t]*\)\($DIST\)\([ \t]*\)\(main \)/\1 \2$SUITE\4\5/" >> "$SOURCES_LIST_OUTPUT"
480 else
481   cat >> "$SOURCES_LIST_OUTPUT" << EOF
482 # generated by grml-live
483 deb http://deb.grml.org/            grml-stable  main
484 deb http://deb.grml.org/            grml-testing main
485 deb http://cdn.debian.net/debian $SUITE  main contrib non-free
486 EOF
487 fi
488
489 # notice: activate grml-live pool when building against unstable or testing:
490 if grep -qwe unstable -qwe sid -qwe testing -qwe wheezy "$SOURCES_LIST_OUTPUT" ; then
491    grep -q 'grml-live.*main' "$SOURCES_LIST_OUTPUT" || \
492    grep grml-stable "$SOURCES_LIST_OUTPUT" | \
493         sed 's/grml-stable/grml-live/' >> "$SOURCES_LIST_OUTPUT"
494 else
495    grep -q 'grml-live.*main' "$SOURCES_LIST_OUTPUT" && \
496    sed -i 's/.*grml-live.*main/# removed grml-live repository/' "$SOURCES_LIST_OUTPUT"
497 fi
498
499 for file in "$LIVE_CONF" "$CONFIG" "$LOCAL_CONFIG" "$NFSROOT_CONF" ; do
500     if [ -n "$file" ] ; then
501        sed "s|^FAI_DEBOOTSTRAP=\"[a-z]* |FAI_DEBOOTSTRAP=\"$SUITE |" "$file" | sponge "$file"
502     fi
503 done
504
505 # validate whether the specified architecture class matches the
506 # architecture (option), otherwise installation of kernel will fail
507 if echo $CLASSES | grep -qi i386 ; then
508    if ! [[ "$ARCH" == "i386" ]] ; then
509       log    "Error: You specified the I386 class but are trying to build something else (AMD64?)."
510       eerror "Error: You specified the I386 class but are trying to build something else (AMD64?)."
511       eerror "Tip:   Either invoke grml-live with '-a i386' or adjust the architecture class. Exiting."
512       eend 1
513       bailout
514    fi
515 elif echo $CLASSES | grep -qi amd64 ; then
516    if ! [[ "$ARCH" == "amd64" ]] ; then
517       log    "Error: You specified the AMD64 class but are trying to build something else (I386?)."
518       eerror "Error: You specified the AMD64 class but are trying to build something else (I386?)."
519       eerror "Tip:   Either invoke grml-live with '-a amd64' or adjust the architecture class. Exiting."
520       eend 1
521       bailout
522    fi
523 fi
524
525 if grep -q -- 'FAI_DEBOOTSTRAP_OPTS.*--arch' "$NFSROOT_CONF" ; then
526    sed "s/--arch [a-z0-9]* /--arch $ARCH /" "$NFSROOT_CONF" | sponge "$NFSROOT_CONF"
527 else
528    sed "s|^FAI_DEBOOTSTRAP_OPTS=\"\(.*\)|FAI_DEBOOTSTRAP_OPTS=\"--arch $ARCH \1|" "$NFSROOT_CONF" | sponge "$NFSROOT_CONF"
529 fi
530 # }}}
531
532 # CHROOT_OUTPUT - execute FAI {{{
533 if [ -n "$BUILD_DIRTY" ]; then
534    log   "Skipping stage 'fai' as requested via option -B"
535    ewarn "Skipping stage 'fai' as requested via option -B" ; eend 0
536 else
537    [ -n "$CHROOT_OUTPUT" ] || CHROOT_OUTPUT="$OUTPUT/grml_chroot"
538
539    # provide inform fai about the ISO we build
540    [ -d "$CHROOT_OUTPUT/etc/" ] || mkdir -p "$CHROOT_OUTPUT/etc/"
541    echo '# This file has been generated by grml-live.' > "$CHROOT_OUTPUT/etc/grml_live_version"
542    [ -n "$GRML_LIVE_VERSION" ] && echo "GRML_LIVE_VERSION=$GRML_LIVE_VERSION" >> "$CHROOT_OUTPUT/etc/grml_live_version"
543    [ -n "$SUITE" ] && echo "SUITE=$SUITE" >> "$CHROOT_OUTPUT/etc/grml_live_version"
544
545    if [ -n "$UPDATE" -o -n "$BUILD_ONLY" ] ; then
546       FAI_ACTION=softupdate
547    else
548       FAI_ACTION=dirinstall
549    fi
550
551    if [ -n "$UPDATE" -o -n "$BUILD_ONLY" ] ; then
552       if ! [ -r "$CHROOT_OUTPUT/etc/debian_version" ] ; then
553          log    "Error: does not look like you have a working chroot. Updating/building not possible."
554          eerror "Error: does not look like you have a working chroot. Updating/building not possible. (Drop -u/-b option?)"
555          eend 1
556          bailout 20
557       fi
558    fi
559
560    if [ -d "$CHROOT_OUTPUT/bin" -a -z "$UPDATE" -a -z "$BUILD_ONLY" ] ; then
561       log   "Skipping stage 'fai dirinstall' as $CHROOT_OUTPUT exists already."
562       ewarn "Skipping stage 'fai dirinstall' as $CHROOT_OUTPUT exists already." ; eend 0
563    else
564       mkdir -p "$CHROOT_OUTPUT" || bailout 5 "Problem with creating $CHROOT_OUTPUT for FAI"
565
566       if [ -n "${MIRROR_DIRECTORY}" ] ; then
567          mkdir -p "${CHROOT_OUTPUT}/${MIRROR_DIRECTORY}"
568          mount --bind "${MIRROR_DIRECTORY}" "${CHROOT_OUTPUT}/${MIRROR_DIRECTORY}"
569       fi
570
571       # tell dpkg to use "unsafe io" during the build
572       [ -d "$CHROOT_OUTPUT/etc/dpkg/dpkg.cfg.d" ] || mkdir -p "$CHROOT_OUTPUT/etc/dpkg/dpkg.cfg.d"
573       echo force-unsafe-io > "$CHROOT_OUTPUT/etc/dpkg/dpkg.cfg.d/unsafe-io"
574
575       log "Executed FAI command line:"
576       log "BUILD_ONLY=$BUILD_ONLY BOOTSTRAP_ONLY=$BOOTSTRAP_ONLY fai $VERBOSE -C $GRML_FAI_CONFIG -s file:///$GRML_FAI_CONFIG/config -c$CLASSES -u $HOSTNAME $FAI_ACTION $CHROOT_OUTPUT $FAI_ARGS"
577       BUILD_ONLY="$BUILD_ONLY" BOOTSTRAP_ONLY="$BOOTSTRAP_ONLY" fai $VERBOSE \
578                   -C "$GRML_FAI_CONFIG" -s "file:///$GRML_FAI_CONFIG/config" -c"$CLASSES" \
579                   -u "$HOSTNAME" "$FAI_ACTION" "$CHROOT_OUTPUT" $FAI_ARGS | tee -a $LOGFILE
580       RC="$PIPESTATUS" # notice: bash-only
581
582       rm -f "$CHROOT_OUTPUT/etc/dpkg/dpkg.cfg.d/unsafe-io"
583
584       FORCE_ISO_REBUILD=true
585
586       if [ "$RC" != 0 ] ; then
587          log    "Error: critical error while executing fai [exit code ${RC}]. Exiting."
588          eerror "Error: critical error while executing fai [exit code ${RC}]. Exiting." ; eend 1
589          bailout 1
590       else
591          einfo "Setting /etc/grml_version to $GRML_NAME $VERSION Release Codename $RELEASENAME [$DATE]"
592          log   "Setting /etc/grml_version to $GRML_NAME $VERSION Release Codename $RELEASENAME [$DATE]"
593          echo "$GRML_NAME $VERSION Release Codename $RELEASENAME [$DATE]" > $CHROOT_OUTPUT/etc/grml_version
594          chmod 644 $CHROOT_OUTPUT/etc/grml_version
595          einfo "Rebuilding initramfs"
596          # make sure new /etc/grml_version reaches initramfs, iterate over all
597          # present kernel versions (note: we can't really handle more than one
598          # kernel version anyway right now)
599          # chroot $CHROOT_OUTPUT update-initramfs -u -t => might break when using kernel-package :(
600          for initrd in "$(basename $CHROOT_OUTPUT/boot/vmlinuz-*)" ; do
601            if ! chroot $CHROOT_OUTPUT update-initramfs -k "${initrd##vmlinuz-}" -c ; then
602              einfo "Creating fresh initrd did not work, trying update instead:"
603              log   "Creating fresh initrd did not work, trying update instead:"
604              chroot $CHROOT_OUTPUT update-initramfs -k "${initrd##vmlinuz-}" -u
605            fi
606          done
607          eend $?
608       fi
609
610       # Remove all FAI logs from chroot if class RELEASE is used:
611       if [ -f "$CHROOT_OUTPUT"/etc/grml_fai_release ] ; then
612          rm -rf "$CHROOT_OUTPUT"/var/log/fai/*
613          rm -f "$CHROOT_OUTPUT"/var/log/install_packages.list
614       fi
615
616       umount_all
617
618       # notice: 'fai dirinstall' does not seem to exit appropriate, so:
619       ERROR=''
620       CHECKLOG=/var/log/fai/$HOSTNAME/last
621       if [ -r "$CHECKLOG/software.log" ] ; then
622          # 1 errors during executing of commands
623          grep 'dpkg: error processing' $CHECKLOG/software.log >> $LOGFILE && ERROR=1
624          grep 'E: Method http has died unexpectedly!' $CHECKLOG/software.log >> $LOGFILE && ERROR=2
625          grep 'ERROR: chroot' $CHECKLOG/software.log >> $LOGFILE && ERROR=3
626          grep 'E: Failed to fetch' $CHECKLOG/software.log >> $LOGFILE && ERROR=4
627          grep 'Unable to write mmap - msync (28 No space left on device)' $CHECKLOG/software.log >> $LOGFILE && ERROR=5
628       fi
629
630       if [ -r "$CHECKLOG/shell.log" ] ; then
631          grep 'FAILED with exit code' $CHECKLOG/shell.log >> $LOGFILE && ERROR=6
632       fi
633
634       if [ -n "$ERROR" ] ; then
635          log    "Error: there was a critical error [${ERROR}] during execution of stage 'fai dirinstall' [$(date)]"
636          eerror "Error: there was a critical error during execution of stage 'fai dirinstall'"
637          eerror "Note:  check out ${CHECKLOG}/ for details. [exit ${ERROR}]"
638          eend 1
639          bailout 1
640       else
641          log "Finished execution of stage 'fai dirinstall' [$(date)]"
642          einfo "Finished execution of stage 'fai dirinstall'"
643       fi
644
645       einfo "Find FAI build logs at $(readlink -f /var/log/fai/$HOSTNAME/last)"
646       log   "Find FAI build logs at $(readlink -f /var/log/fai/$HOSTNAME/last)"
647       eend 0
648    fi
649 fi # BUILD_DIRTY?
650 # }}}
651
652 # package validator {{{
653 CHECKLOG=/var/log/fai/$HOSTNAME/last
654 # package validator
655 if [ -r "$CHECKLOG/package_errors.log" ] && grep -q '[a-z]' "$CHECKLOG/package_errors.log" ; then
656
657    if [ -n "$EXIT_ON_MISSING_PACKAGES" -a -z "$BUILD_DIRTY" ] ; then
658       eerror "The following packages were requested for installation but could not be processed:"
659       cat $CHECKLOG/package_errors.log
660       eerror "... exiting as requested via \$EXIT_ON_MISSING_PACKAGES."
661       eend 1
662       bailout 13
663    else
664       ewarn "The following packages were requested for installation but could not be processed:"
665       cat $CHECKLOG/package_errors.log
666       eend 0
667    fi
668 fi
669 # }}}
670
671 # BUILD_OUTPUT - execute arch specific stuff and squashfs {{{
672 [ -n "$BUILD_OUTPUT" ] || BUILD_OUTPUT="$OUTPUT/grml_cd"
673 mkdir -p "$BUILD_OUTPUT" || bailout 6 "Problem with creating $BUILD_OUTPUT for stage ARCH"
674
675 # prepare ISO
676 if [ "$ARCH" = i386 ] || [ "$ARCH" = amd64 ] ; then
677   if [ -n "$BOOTSTRAP_ONLY" ] ; then
678      log   "Skipping stage 'boot' as building with bootstrap only."
679      ewarn "Skipping stage 'boot' as building with bootstrap only." ; eend 0
680   else
681     if [ -d "$BUILD_OUTPUT"/boot/isolinux -a -z "$UPDATE" -a -z "$BUILD_ONLY" ] ; then
682        log   "Skipping stage 'boot' as $BUILD_OUTPUT/boot/isolinux exists already."
683        ewarn "Skipping stage 'boot' as $BUILD_OUTPUT/boot/isolinux exists already." ; eend 0
684     else
685        # booting stuff:
686        [ -d "$BUILD_OUTPUT"/boot/isolinux ] || mkdir -p "$BUILD_OUTPUT"/boot/isolinux
687        [ -d "$BUILD_OUTPUT"/boot/"${SHORT_NAME}" ] || mkdir -p "$BUILD_OUTPUT"/boot/"${SHORT_NAME}"
688
689        # if we don't have an initrd we a) can't boot and b) there was an error
690        # during build, so check for the file:
691        INITRD="$(ls $CHROOT_OUTPUT/boot/initrd* 2>/dev/null| grep -v '.bak$' | sort -r | head -1)"
692        if [ -n "$INITRD" ] ; then
693           cp $INITRD "$BUILD_OUTPUT"/boot/"${SHORT_NAME}"/initrd.gz
694           find $CHROOT_OUTPUT/boot/ -name initrd\*.bak -exec rm {} \;
695        else
696           log    "Error: No initrd found inside $CHROOT_OUTPUT/boot/ - Exiting"
697           eerror "Error: No initrd found inside $CHROOT_OUTPUT/boot/ - Exiting" ; eend 1
698           bailout 10
699        fi
700
701        KERNEL_IMAGE="$(ls $CHROOT_OUTPUT/boot/vmlinuz* 2>/dev/null | sort -r | head -1)"
702        if [ -n "$KERNEL_IMAGE" ] ; then
703           cp "$KERNEL_IMAGE" "$BUILD_OUTPUT"/boot/"${SHORT_NAME}"/linux26
704        else
705           log    "Error: No kernel found inside $CHROOT_OUTPUT/boot/ - Exiting"
706           eerror "Error: No kernel found inside $CHROOT_OUTPUT/boot/ - Exiting" ; eend 1
707           bailout 11
708        fi
709
710        [ -n "$TEMPLATE_DIRECTORY" ] || TEMPLATE_DIRECTORY='/usr/share/grml-live/templates'
711        if ! [ -d "${TEMPLATE_DIRECTORY}"/boot ] ; then
712           log    "Error: ${TEMPLATE_DIRECTORY}/boot does not exist. Exiting."
713           eerror "Error: ${TEMPLATE_DIRECTORY}/boot does not exist. Exiting." ; eend 1
714           bailout 8
715        fi
716
717        # copy _required_ isolinux files
718        for file in ifcpu64.c32 isolinux.bin vesamenu.c32; do
719          copy_addon_file "${file}" /usr/lib/syslinux isolinux
720        done
721
722        # *always* copy files to output directory so the variables
723        # get adjusted according to the build.
724        cp ${TEMPLATE_DIRECTORY}/boot/isolinux/*  "$BUILD_OUTPUT"/boot/isolinux/
725
726        if [ -n "$NO_ADDONS" ] ; then
727           log   "Skipping installation of boot addons as requested via \$NO_ADDONS."
728           einfo "Skipping installation of boot addons as requested via \$NO_ADDONS."; eend 0
729        else
730           if ! [ -d "$TEMPLATE_DIRECTORY"/boot/addons ] ; then
731             log   "Boot addons not found, skipping therefore. (Consider installing package grml-live-addons)"
732             ewarn "Boot addons not found, skipping therefore. (Consider installing package grml-live-addons)" ; eend 0
733           else
734             # copy addons from system packages or grml-live-compat
735             copy_addon_file ipxe.lkrn /usr/lib/ipxe addons
736             copy_addon_file pci.ids /usr/share/misc addons
737             copy_addon_file memtest86+.bin /boot addons
738             for file in memdisk chain.c32 hdt.c32 menu.c32; do
739               copy_addon_file "${file}" /usr/lib/syslinux addons
740             done
741
742             # make memtest filename FAT16/8.3 compatible
743             mv "${BUILD_OUTPUT}/boot/addons/memtest86+.bin" \
744               "${BUILD_OUTPUT}/boot/addons/memtest"
745
746             # copy only files so we can handle bsd4grml on its own
747             for file in ${TEMPLATE_DIRECTORY}/boot/addons/* ; do
748               test -f $file && cp $file "$BUILD_OUTPUT"/boot/addons/
749             done
750
751             if [ -n "$NO_ADDONS_BSD4GRML" ] ; then
752                log   "Skipping installation of bsd4grml as requested via \$NO_ADDONS_BSD4GRML."
753                einfo "Skipping installation of bsd4grml as requested via \$NO_ADDONS_BSD4GRML."; eend 0
754             else
755                if [ -d "$TEMPLATE_DIRECTORY"/boot/addons/bsd4grml ] ; then
756                  cp -a ${TEMPLATE_DIRECTORY}/boot/addons/bsd4grml "$BUILD_OUTPUT"/boot/addons/
757                else
758                  log   "Missing addon file: bsd4grml"
759                  ewarn "Missing addon file: bsd4grml" ; eend 0
760                fi
761             fi
762
763           fi # no "$TEMPLATE_DIRECTORY"/boot/addons
764        fi # NO_ADDONS
765
766        if ! [ -d "${BUILD_OUTPUT}/boot/grub" ] ; then
767          mkdir -p "${BUILD_OUTPUT}/boot/grub"
768        fi
769        cp ${TEMPLATE_DIRECTORY}/boot/grub/* "$BUILD_OUTPUT"/boot/grub/
770
771        if [ -e ${TEMPLATE_DIRECTORY}/compat/grub/linux.mod ]; then
772          cp "${TEMPLATE_DIRECTORY}"/compat/grub/* "${BUILD_OUTPUT}"/boot/grub/
773        else
774          if ! which "grub-mkimage" >/dev/null 2>&1 ; then
775            log   "grub-mkimage not found, skipping Grub step therefore." ; eend 0
776            ewarn "grub-mkimage not found, skipping Grub step therefore."
777            ewarn "Please install grub-pc-bin or grub-common >= 1.98+20100804-14." ; eend 0
778          elif ! grub-mkimage --help | grep -q -- --format ; then
779            log   "grub-mkimage does not support --format=i386-pc, skipping Grub step therefore." ; eend 0
780            ewarn "grub-mkimage does not support --format=i386-pc, skipping Grub step therefore."
781            ewarn "Please install grub-common >= 1.98+20100804-14 or grub-pc-bin." ; eend 0
782          else
783            # copy system grub files if grml-live-compat is not installed
784            cp -a /usr/lib/grub/*-pc/*.mod "${BUILD_OUTPUT}"/boot/grub/
785            cp -a /usr/lib/grub/*-pc/*.o "${BUILD_OUTPUT}"/boot/grub/
786            cp -a /usr/lib/grub/*-pc/*.lst "${BUILD_OUTPUT}"/boot/grub/
787            cp -a /usr/share/grub/ascii.pf2 "${BUILD_OUTPUT}"/boot/grub/
788            grub-mkimage -d /usr/lib/grub/*-pc -o \
789              "${BUILD_OUTPUT}/boot/grub/core.img" biosdisk iso9660 --format=i386-pc
790          fi
791        fi
792
793        if ! [ -d "${TEMPLATE_DIRECTORY}"/GRML ] ; then
794           log    "Error: ${TEMPLATE_DIRECTORY}/GRML does not exist. Exiting."
795           eerror "Error: ${TEMPLATE_DIRECTORY}/GRML does not exist. Exiting." ; eend 1
796           bailout 9
797        fi
798
799        [ -d "$BUILD_OUTPUT"/GRML ] || mkdir "$BUILD_OUTPUT"/GRML
800        cp -a ${TEMPLATE_DIRECTORY}/GRML/* "$BUILD_OUTPUT"/GRML/
801
802        # adjust boot splash information:
803        RELEASE_INFO="$GRML_NAME $VERSION - Release Codename $RELEASENAME"
804        RELEASE_INFO="$(cut_string 68 "$RELEASE_INFO")"
805        RELEASE_INFO="$(extend_string_end 68 "$RELEASE_INFO")"
806
807        if [ -r "$BUILD_OUTPUT"/GRML/grml-version ] ; then
808           sed -i "s/%RELEASE_INFO%/$GRML_NAME $VERSION - $RELEASENAME/" "$BUILD_OUTPUT"/GRML/grml-version
809           sed -i "s/%DATE%/$DATE/"                                      "$BUILD_OUTPUT"/GRML/grml-version
810        fi
811
812        # make sure the squashfs filename is set accordingly:
813        SQUASHFS_NAME="$GRML_NAME.squashfs"
814
815        if [ -n "$NO_BOOTID" ] ; then
816           log   'Skipping bootid feature as requested via $NO_BOOTID.'
817           einfo 'Skipping bootid feature as requested via $NO_BOOTID.'
818        else
819           [ -n "$BOOTID" ] || BOOTID="$(echo ${GRML_NAME}${VERSION} | tr -d ',./;\- ')"
820           [ -d "$BUILD_OUTPUT"/conf ] || mkdir "$BUILD_OUTPUT"/conf
821           einfo "Generating /conf/bootid.txt with entry ${BOOTID}."
822           log   "Generating /conf/bootid.txt with entry ${BOOTID}."
823           echo "$BOOTID" > "$BUILD_OUTPUT"/conf/bootid.txt
824           eend $?
825        fi
826
827        # adjust all variables in the templates with the according distribution information
828        for file in "${BUILD_OUTPUT}"/boot/isolinux/*.cfg "${BUILD_OUTPUT}"/boot/isolinux/*.msg \
829                    "${BUILD_OUTPUT}"/boot/grub/* ; do
830          if [ -r "${file}" ] ; then
831            sed -i "s/%ARCH%/$ARCH/g"                    "${file}"
832            sed -i "s/%DATE%/$DATE/g"                    "${file}"
833            sed -i "s/%DISTRI_INFO%/$DISTRI_INFO/g"      "${file}"
834            sed -i "s/%DISTRI_NAME%/$DISTRI_NAME/g"      "${file}"
835            sed -i "s/%DISTRI_SPLASH%/$DISTRI_SPLASH/g"  "${file}"
836            sed -i "s/%GRML_NAME%/$GRML_NAME/g"          "${file}"
837            sed -i "s/%SQUASHFS_NAME%/$SQUASHFS_NAME/g"  "${file}"
838            sed -i "s/%RELEASE_INFO%/$RELEASE_INFO/g"    "${file}"
839            sed -i "s/%SHORT_NAME%/$SHORT_NAME/g"        "${file}"
840            sed -i "s/%VERSION%/$VERSION/g"              "${file}"
841
842            [ -n "$DEFAULT_BOOTOPTIONS" ] && sed -i "s/ boot=live/ boot=live $DEFAULT_BOOTOPTIONS/"  "${file}"
843
844            if [ -n "$NO_BOOTID" ] ; then
845               sed -i "s/ bootid=%BOOTID%//g" "${file}" # drop bootid bootoption
846            else
847               sed -i "s/%BOOTID%/$BOOTID/g" "${file}" # adjust bootid=... argument
848            fi
849          fi
850        done
851
852        # adjust bootsplash accordingly but make sure the string has the according lenght
853        SQUASHFS_NAME="$(cut_string 20 "$SQUASHFS_NAME")"
854        SQUASHFS_NAME="$(extend_string_end 20 "$SQUASHFS_NAME")"
855        for file in f4 f5 ; do
856           if [ -r "${BUILD_OUTPUT}/boot/isolinux/${file}" ] ; then
857              sed -i "s/%SQUASHFS_NAME%/$SQUASHFS_NAME/" "${BUILD_OUTPUT}/boot/isolinux/${file}"
858              sed -i "s/%SQUASHFS_NAME%/$SQUASHFS_NAME/" "${BUILD_OUTPUT}/boot/isolinux/${file}"
859           fi
860        done
861
862        # generate addon list
863        rm -f "${BUILD_OUTPUT}/${ADDONS_LIST_FILE}"
864        for name in "${BUILD_OUTPUT}"/boot/isolinux/addon_*.cfg ; do
865          include_name=$(basename "$name")
866          echo "include $include_name"  >> "${BUILD_OUTPUT}/${ADDONS_LIST_FILE}"
867        done
868
869        if ! [ -r "${BUILD_OUTPUT}/boot/isolinux/${DISTRI_NAME}.cfg" ] || [ "$DISTRI_NAME" = "grml" ] ; then
870           log "including grmlmain.cfg in ${BUILD_OUTPUT}/boot/isolinux/distri.cfg"
871           echo "include grmlmain.cfg"    >  "${BUILD_OUTPUT}/boot/isolinux/distri.cfg"
872           echo "include default.cfg"     >  "${BUILD_OUTPUT}/boot/isolinux/grmlmain.cfg"
873           echo "include menuoptions.cfg" >> "${BUILD_OUTPUT}/boot/isolinux/grmlmain.cfg"
874           echo "include grml.cfg"        >> "${BUILD_OUTPUT}/boot/isolinux/grmlmain.cfg"
875
876           for f in "${BUILD_OUTPUT}"/boot/isolinux/submenu*.cfg ; do
877             echo "include $(basename $f)"     >> "${BUILD_OUTPUT}/boot/isolinux/grmlmain.cfg"
878           done
879
880           echo "include options.cfg"     >> "${BUILD_OUTPUT}/boot/isolinux/grmlmain.cfg"
881           if [ ! -n "$NO_ADDONS" ] ; then
882             echo "include addons.cfg"    >> "${BUILD_OUTPUT}/boot/isolinux/grmlmain.cfg"
883           fi
884           echo "include isoprompt.cfg"   >> "${BUILD_OUTPUT}/boot/isolinux/grmlmain.cfg"
885           echo "include hd.cfg"          >> "${BUILD_OUTPUT}/boot/isolinux/grmlmain.cfg"
886           echo "include hidden.cfg"      >> "${BUILD_OUTPUT}/boot/isolinux/grmlmain.cfg"
887        else # assume we are building a custom distribution:
888           log "File ${BUILD_OUTPUT}/boot/isolinux/${DISTRI_NAME}.cfg found, using it."
889           einfo "File ${BUILD_OUTPUT}/boot/isolinux/${DISTRI_NAME}.cfg found, using it."
890           if grep -q "^include ${DISTRI_NAME}.cfg" "${BUILD_OUTPUT}/boot/isolinux/distri.cfg" ; then
891             log "include for ${DISTRI_NAME}.cfg already present, nothing to do."
892             eindent
893             einfo "include for ${DISTRI_NAME}.cfg already present, nothing to do."
894             eoutdent
895             eend $?
896          else
897             log "including ${DISTRI_NAME}.cfg in ${BUILD_OUTPUT}/boot/isolinux/distri.cfg"
898             echo "include ${DISTRI_NAME}.cfg" > "${BUILD_OUTPUT}/boot/isolinux/distri.cfg"
899             [ -n "$NO_ADDONS" ] || echo "include addons.cfg" >> "${BUILD_OUTPUT}/boot/isolinux/distri.cfg"
900           fi
901        fi
902
903        # use old style console based isolinux method only if requested:
904        if [[ "${ISOLINUX_METHOD}" == "console" ]] ; then
905           log 'Using console based isolinux method as requested via $ISOLINUX_METHOD.'
906           einfo 'Using console based isolinux method as requested via $ISOLINUX_METHOD.'
907           if grep -q '^include console.cfg' "${BUILD_OUTPUT}/boot/isolinux/distri.cfg" ; then
908             einfo "include for console.cfg already found, nothing to do."
909             eend 0
910           else
911             log "including console.cfg in ${BUILD_OUTPUT}/boot/isolinux/isolinux.cfg"
912             einfo "including console.cfg in ${BUILD_OUTPUT}/boot/isolinux/isolinux.cfg"
913             echo "include console.cfg" >> "${BUILD_OUTPUT}/boot/isolinux/isolinux.cfg"
914             eend $?
915           fi
916        else
917           log 'Using graphical boot menu.'
918           if grep -q '^include vesamenu.cfg' "${BUILD_OUTPUT}/boot/isolinux/isolinux.cfg" ; then
919             log "include for vesamenu.cfg already found, nothing to do."
920           else
921             log "including vesamenu.cfg in ${BUILD_OUTPUT}/boot/isolinux/isolinux.cfg"
922             echo "include vesamenu.cfg" >> "${BUILD_OUTPUT}/boot/isolinux/isolinux.cfg"
923           fi
924        fi
925
926        if [ -e "$BUILD_OUTPUT"/boot/addons/bsd4grml/boot.6 ]; then
927           sed -i "s/%RELEASE_INFO%/$GRML_NAME $VERSION - $RELEASENAME/" "$BUILD_OUTPUT"/boot/addons/bsd4grml/boot.6
928        fi
929
930        DPKG_LIST="/var/log/fai/$HOSTNAME/last/dpkg.list" # the dpkg --list output of the chroot
931        if ! [ -r "$DPKG_LIST" ] ; then
932           ewarn "$DPKG_LIST could not be read, ignoring to store package information on ISO therefore."
933        else
934           einfo "Storing package list information as /GRML/${GRML_NAME}-packages.txt on ISO."
935           cp "$DPKG_LIST" "${BUILD_OUTPUT}/GRML/${GRML_NAME}-packages.txt"
936           eend $?
937        fi
938
939        # autostart for Windows:
940        if [ -d "${TEMPLATE_DIRECTORY}/windows/autostart/" ] ; then
941           cp ${TEMPLATE_DIRECTORY}/windows/autostart/* "$BUILD_OUTPUT"/
942        fi
943
944     FORCE_ISO_REBUILD=true
945     einfo "Finished execution of stage 'boot'" ; eend 0
946     fi
947   fi # BOOTSTRAP_ONLY
948 else
949    log    'Error: Unsupported ARCH, sorry. Want to support it? Contribute!'
950    eerror 'Error: Unsupported ARCH, sorry. Want to support it? Contribute!' ; eend 1
951    bailout
952 fi
953
954 # support installation of local files into the chroot/ISO
955 if [ -n "$CHROOT_INSTALL" ] ; then
956   if ! [ -d "$CHROOT_INSTALL" ] ; then
957      log "Configuration variable \$CHROOT_INSTALL is set but not a directory; ignoring"
958      ewarn "Configuration variable \$CHROOT_INSTALL is set but not a directory; ignoring"
959   else
960      log "Copying local files to chroot as requested via \$CHROOT_INSTALL"
961      einfo "Copying local files to chroot as requested via \$CHROOT_INSTALL"
962      rsync -avz --inplace "$CHROOT_INSTALL"/ "$CHROOT_OUTPUT/"
963      eend $?
964      einfo "Make sure to run squashfs stage, otherwise your local files won't be part of the ISO."
965      FORCE_ISO_REBUILD=true
966   fi
967 fi
968
969 if [ -f "$BUILD_OUTPUT"/live/${GRML_NAME}.squashfs -a -z "$UPDATE" -a -z "$BUILD_ONLY" -a -z "$BUILD_DIRTY" ] ; then
970    log   "Skipping stage 'squashfs' as $BUILD_OUTPUT/live exists already."
971    ewarn "Skipping stage 'squashfs' as $BUILD_OUTPUT/live exists already." ; eend 0
972 elif [ -n "$SKIP_MKSQUASHFS" ] ; then
973    log   "Skipping stage 'squashfs' as requested via option -q or -N"
974    ewarn "Skipping stage 'squashfs' as requested via option -q or -N" ; eend 0
975 else
976    [ -d "$BUILD_OUTPUT"/live ] || mkdir "$BUILD_OUTPUT"/live
977    # make sure we don't leave (even an empty) base.tgz:
978    [ -f "$CHROOT_OUTPUT/base.tgz" ] && rm -f "$CHROOT_OUTPUT/base.tgz"
979
980    # if unconfigured default to squashfs-tools' mksquashfs binary
981    if [ -z "$SQUASHFS_BINARY" ] ; then
982       SQUASHFS_BINARY='mksquashfs'
983    fi
984
985    if which "$SQUASHFS_BINARY" >/dev/null 2>&1 ; then
986       log    "Using mksquashfs binary ${SQUASHFS_BINARY}"
987       einfo  "Using mksquashfs binary ${SQUASHFS_BINARY}" ; eend 0
988    else
989       log    "Error: mksquashfs binary ($SQUASHFS_BINARY) not found. Exiting."
990       eerror "Error: mksquashfs binary ($SQUASHFS_BINARY) not found. Exiting." ; eend 1
991       bailout
992    fi
993
994    # use sane defaults if $SQUASHFS_OPTIONS isn't set
995    if [ -z "$SQUASHFS_OPTIONS" ] ; then
996      # use blocksize 256k as this gives best result with regards to time + compression
997      SQUASHFS_OPTIONS="-b 256k"
998
999      # set lzma/xz compression by default, unless -z option has been specified on command line
1000      if [ -z "$SQUASHFS_ZLIB" ] ; then
1001         SQUASHFS_OPTIONS="$SQUASHFS_OPTIONS -comp xz"
1002      else
1003         SQUASHFS_OPTIONS="$SQUASHFS_OPTIONS -comp gzip"
1004      fi
1005    fi
1006
1007    # support exclusion of files via exclude-file:
1008    if [ -n "$SQUASHFS_EXCLUDES_FILE" -a "$SQUASHFS_EXCLUDES_FILE" ] ; then
1009       SQUASHFS_OPTIONS="$SQUASHFS_OPTIONS -ef $SQUASHFS_EXCLUDES_FILE -wildcards"
1010    fi
1011
1012    # get rid of unnecessary files when building grml-small for final release:
1013    if echo "$CLASSES" | grep -q GRML_SMALL ; then
1014       SQUASHFS_OPTIONS="$SQUASHFS_OPTIONS -e initrd.img* vmlinuz*"
1015    fi
1016
1017    # log stuff
1018    SQUASHFS_STDERR="$(mktemp -t grml-live.XXXXXX)"
1019
1020    # informational stuff
1021    [ -n "$SQUASHFS_OPTIONS" ]  && SQUASHFS_INFO_MSG="$SQUASHFS_OPTIONS"
1022    [ -n "$SQUASHFS_INFO_MSG" ] && SQUASHFS_INFO_MSG="using options: $SQUASHFS_INFO_MSG"
1023    einfo "Squashfs build information: running binary $SQUASHFS_BINARY $SQUASHFS_INFO_MSG"
1024
1025    log "$SQUASHFS_BINARY $CHROOT_OUTPUT/ $BUILD_OUTPUT/live/${GRML_NAME}.squashfs -noappend $SQUASHFS_OPTIONS"
1026
1027    if $SQUASHFS_BINARY $CHROOT_OUTPUT/ $BUILD_OUTPUT/live/"${GRML_NAME}".squashfs \
1028       -noappend $SQUASHFS_OPTIONS 2>"${SQUASHFS_STDERR}" ; then
1029       echo "${GRML_NAME}.squashfs" > $BUILD_OUTPUT/live/filesystem.module
1030       log "Finished execution of stage 'squashfs' [$(date)]"
1031       einfo "Finished execution of stage 'squashfs'" ; eend 0
1032    else
1033       log    "Error: there was a critical error executing stage 'squashfs' [$(date)]:"
1034       log    "$(cat $SQUASHFS_STDERR)"
1035       eerror "Error: there was a critical error executing stage 'squashfs':"
1036       cat    "${SQUASHFS_STDERR}"
1037       eend 1
1038       bailout
1039    fi
1040
1041    FORCE_ISO_REBUILD=true
1042 fi
1043
1044 # create md5sum file:
1045 if [ -z "$BOOTSTRAP_ONLY" ] ; then
1046   ( cd $BUILD_OUTPUT/GRML &&
1047   find .. -type f -not -name md5sums -not -name isolinux.bin -exec md5sum {} \; > md5sums )
1048 fi
1049 # }}}
1050
1051 # ISO_OUTPUT - mkisofs {{{
1052 [ -n "$ISO_OUTPUT" ] || ISO_OUTPUT="$OUTPUT/grml_isos"
1053 [ -n "$ISO_NAME" ] || ISO_NAME="${GRML_NAME}_${VERSION}.iso"
1054
1055 if [ "$BOOT_METHOD" = "isolinux" ] ; then
1056    BOOT_ARGS="-no-emul-boot -boot-load-size 4 -boot-info-table -b boot/isolinux/isolinux.bin -c boot/isolinux/boot.cat"
1057 elif [ "$BOOT_METHOD" = "grub2" ] ; then
1058    BOOT_ARGS="-no-emul-boot -boot-load-size 4 -b boot/grub/toriboot.bin"
1059 fi
1060
1061 # Just until http://bts.grml.org/grml/issue945 has been resolved.
1062 # HYBRID_METHOD defaults to manifold, so make sure the default works OOTB.
1063 if [[ $BOOT_METHOD != isolinux && ($HYBRID_METHOD = isohybrid || $HYBRID_METHOD = manifold) ]]; then
1064   log   "Setting HYBRID_METHOD to grub2 as hybrid mode does not work with isohybrid yet."
1065   ewarn "Setting HYBRID_METHOD to grub2 as hybrid mode does not work with isohybrid yet."
1066   HYBRID_METHOD='grub2'
1067   eend 0
1068 fi
1069
1070 if [ -f "${ISO_OUTPUT}/${ISO_NAME}" -a -z "$UPDATE" -a -z "$BUILD_ONLY" -a -z "$BUILD_DIRTY" -a "$FORCE_ISO_REBUILD" = "false" ]  ; then
1071    log   "Skipping stage 'iso build' as $ISO_OUTPUT/${ISO_NAME} exists already."
1072    ewarn "Skipping stage 'iso build' as $ISO_OUTPUT/${ISO_NAME} exists already." ; eend 0
1073 elif [ -n "$SKIP_MKISOFS" ] ; then
1074    log   "Skipping stage 'iso build' as requested via option -n or -N"
1075    ewarn "Skipping stage 'iso build' as requested via option -n or -N" ; eend 0
1076 else
1077    mkdir -p "$ISO_OUTPUT" || bailout 6 "Problem with creating $ISO_OUTPUT for stage 'iso build'"
1078
1079    if $FORCE_ISO_REBUILD && ! [ -f "${ISO_OUTPUT}/${ISO_NAME}" ] ; then
1080       log   "Forcing rebuild of ISO because files on ISO have been modified."
1081       einfo "Forcing rebuild of ISO because files on ISO have been modified."
1082    fi
1083
1084    # support mkisofs as well as genisoimage
1085    if which mkisofs >/dev/null 2>&1; then
1086       MKISOFS='mkisofs'
1087    elif which genisoimage >/dev/null 2>&1; then
1088       MKISOFS='genisoimage'
1089    else
1090       log    "Error: neither mkisofs nor genisoimage available - can not create ISO."
1091       eerror "Error: neither mkisofs nor genisoimage available - can not create ISO." ; eend 1
1092       bailout
1093    fi
1094
1095    CURRENT_DIR=$(pwd)
1096    if cd "$BUILD_OUTPUT" ; then
1097       if [ "$BOOT_METHOD" = "grub2" ]; then
1098          # make a 2048-byte bootsector for El Torito
1099          dd if=/dev/zero of=boot/grub/toriboot.bin bs=512 count=4 2>/dev/null
1100          # those are in 2048-byte sectors, so 1 16 matches 4 63 below
1101          echo 1 16 | mksh /usr/share/grml-live/scripts/bootgrub.mksh -B 11 | \
1102             dd of=boot/grub/toriboot.bin conv=notrunc 2>/dev/null
1103       fi
1104       log "$MKISOFS -V '${GRML_NAME} ${VERSION}' -publisher 'grml-live | grml.org' -l -r -J $BOOT_ARGS -o ${ISO_OUTPUT}/${ISO_NAME} ."
1105       "$MKISOFS" -V "${GRML_NAME} ${VERSION}" -publisher 'grml-live | grml.org' \
1106               -l -r -J $BOOT_ARGS -no-pad \
1107               -o "${ISO_OUTPUT}/${ISO_NAME}" . ; RC=$?
1108       # both of these need core.img there, so it’s easier to write it here
1109       if [ "$BOOT_METHOD" = "grub2" ] || [ "$HYBRID_METHOD" = "grub2" ]; then
1110          # must be <= 30720 bytes
1111          dd if=boot/grub/core.img of="${ISO_OUTPUT}/${ISO_NAME}" \
1112            conv=notrunc bs=512 seek=4 2>/dev/null
1113       fi
1114
1115       # pad the output ISO to multiples of 256 KiB for partition table support
1116       siz=$($getfilesize "${ISO_OUTPUT}/${ISO_NAME}")
1117       cyls=$((siz / 512 / 32 / 16 + 1))   # C=$cyls H=16 S=32
1118       siz=$((cyls * 16 * 32 * 512))   # size after padding
1119       dd if=/dev/zero bs=1 count=1 seek=$((siz - 1)) \
1120          of="${ISO_OUTPUT}/${ISO_NAME}" 2>/dev/null
1121
1122       # support disabling hybrid ISO image
1123       if [ "$HYBRID_METHOD" = "disable" ] ; then\
1124          log   "Skipping creation of hybrid ISO file as requested via HYBRID_METHOD=disable"
1125          einfo "Skipping creation of hybrid ISO file as requested via HYBRID_METHOD=disable"
1126          eend 0
1127       # use isohybrid only on request
1128       elif [ "$HYBRID_METHOD" = "isohybrid" ] ; then
1129          if ! which isohybrid >/dev/null 2>&1 ; then
1130            bailout 12 "isohybrid binary not found - please install syslinux/syslinux-common"
1131          else
1132            log "Creating hybrid ISO file with isohybrid method"
1133            einfo "Creating hybrid ISO file with isohybrid method"
1134            # Notes for consideration:
1135            # "-entry 4 -type 1c"
1136            # * using 4 as the partition number is supposed to help with BIOSes
1137            #   that only support USB-Zip boot
1138            # * using 1c (i.e. hidden FAT32 LBA), instead of the default 0x17
1139            #   (hidden NTFS, IIRC), as the partition type is sometimes needed
1140            #   to get the BIOS even look at the partition created by isohybrid
1141            isohybrid "${ISO_OUTPUT}/${ISO_NAME}"
1142            eend $?
1143          fi
1144       # by default use our manifold boot method:
1145       else
1146          # isoinfo is part of both mkisofs and genisoimage so we're good
1147          bootoff=$(isoinfo -l -i "${ISO_OUTPUT}/${ISO_NAME}" | \
1148            sed -n '/^.*\[ *\([0-9]*\)[] ].* ISOLINUX.BIN;1 *$/s//\1/p')
1149          if ! [ -r boot/grub/core.img ] ; then
1150            ewarn "boot/grub/core.img not found, not creating manifold boot ISO file"
1151          elif [ "${bootoff:-0}" -lt 1 ] ; then
1152            ewarn "isolinux.bin not found on the ISO file, disabling manifold boot"
1153          else
1154            log "Creating hybrid ISO file with manifold method"
1155            einfo "Creating hybrid ISO file with manifold method"
1156            if [ "$HYBRID_METHOD" = "grub2" ] ; then
1157                # 512 bytes: MBR, partition table, load GRUB 2
1158                echo 4 63 | mksh /usr/share/grml-live/scripts/bootgrub.mksh -A -M 4:0x96 -g $cyls:16:32
1159            else
1160               # read only one but 2048-byte sized (scale: << 2) sector
1161               echo $bootoff $bootoff | \
1162                  mksh /usr/share/grml-live/scripts/bootilnx.mksh -A -M 4:0x96 -g $cyls:16:32 -S 2
1163            fi | dd of="${ISO_OUTPUT}/${ISO_NAME}" conv=notrunc 2>/dev/null
1164            eend $?
1165          fi
1166       fi
1167
1168       # generate md5sum and sha1sum of ISO if we are using class 'RELEASE':
1169       case $CLASSES in *RELEASE*)
1170          [ "$RC" = 0 ] && \
1171          (
1172            if cd $ISO_OUTPUT ; then
1173              md5sum ${ISO_NAME} > ${ISO_NAME}.md5 && \
1174              touch -r ${ISO_NAME} ${ISO_NAME}.md5
1175              sha1sum ${ISO_NAME} > ${ISO_NAME}.sha1 && \
1176              touch -r ${ISO_NAME} ${ISO_NAME}.sha1
1177            fi
1178          )
1179          ;;
1180       esac
1181
1182       cd "$CURRENT_DIR"
1183    fi
1184
1185    if [ "$RC" = 0 ] ; then
1186       log   "Finished execution of stage 'iso build' [$(date)]"
1187       einfo "Finished execution of stage 'iso build'" ; eend 0
1188    else
1189       log    "Error: there was a critical error ($RC) executing stage 'iso build' [$(date)]"
1190       eerror "Error: there was a critical error executing stage 'iso build'" ; eend 1
1191       bailout $RC
1192    fi
1193 fi
1194 # }}}
1195
1196 # log build information to database if grml-live-db is installed and enabled {{{
1197 dpkg_to_db() {
1198 if [ -d /usr/share/grml-live-db ] ; then
1199
1200   # safe defaults
1201   DPKG_LIST="/var/log/fai/$HOSTNAME/last/dpkg.list" # the dpkg --list output of the chroot:
1202   [ -n "$DPKG_DATABASE" ]  || DPKG_DATABASE=/var/log/grml-live.db
1203   [ -n "$DPKG_DBSCRIPT" ]  || DPKG_DBSCRIPT=/usr/share/grml-live-db/scripts/dpkg-to-db
1204   [ -n "$DPKG_DBOPTIONS" ] || DPKG_DBOPTIONS="--database $DPKG_DATABASE --logfile $LOGFILE --flavour $GRML_NAME --dpkg $DPKG_LIST"
1205
1206   if ! [ -x "$DPKG_DBSCRIPT" ] ; then
1207     log "Error: $DPKG_DBSCRIPT is not executable, can not log dpkg information."
1208     eerror "Error: $DPKG_DBSCRIPT is not executable, can not log dpkg information." ; eend 1
1209     bailout 14
1210   fi
1211
1212   # disable by default for now, not sure whether really everyone is using a local db file
1213   #if ! touch "$DPKG_DATABASE" ; then
1214   #  eerror "Error: can not write to ${DPKG_DATABASE}, can not log dpkg information." ; eend 1
1215   #  bailout 14
1216   #fi
1217
1218   if ! [ -r "$DPKG_LIST" ] ; then
1219      log   "Warning: can not read $DPKG_LIST - can not provide information to $DPKG_DBSCRIPT (dirty build?)"
1220      ewarn "Warning: can not read $DPKG_LIST - can not provide information to $DPKG_DBSCRIPT (dirty build?)" ; eend 0
1221   else
1222      einfo "Logging $DPKG_LIST to database $DPKG_DATABASE"
1223      log "Logging $DPKG_LIST to database $DPKG_DATABASE"
1224      log "Executing $DPKG_DBSCRIPT $DPKG_DBOPTIONS"
1225      eindent
1226
1227      if DB_INFO=$("$DPKG_DBSCRIPT" $DPKG_DBOPTIONS 2>&1) ; then
1228        einfo "$DB_INFO"
1229        eend 0
1230      else
1231        eerror "$DB_INFO"
1232        eend 1
1233      fi
1234
1235      eoutdent
1236   fi
1237
1238 fi
1239 }
1240 # }}}
1241
1242 # finalize {{{
1243 [ -n "$start_seconds" ] && SECONDS="$[$(cut -d . -f 1 /proc/uptime)-$start_seconds]" || SECONDS="unknown"
1244 log "Successfully finished execution of $PN [$(date) - running ${SECONDS} seconds]"
1245
1246 dpkg_to_db # make sure we catch the last log line as well, therefore execute between log + einfo
1247
1248 einfo "Successfully finished execution of $PN [$(date) - running ${SECONDS} seconds]" ; eend 0
1249 bailout 0
1250 # }}}
1251
1252 ## END OF FILE #################################################################
1253 # vim:foldmethod=marker ts=2 ft=sh ai expandtab tw=80 sw=2