Loose dependencies so we can be installed on squeeze
[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.15.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     echo "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             # make memtest filename FAT16/8.3 compatible
742             mv "${BUILD_OUTPUT}/boot/addons/memtest86+.bin" \
743               "${BUILD_OUTPUT}/boot/addons/memtest"
744
745             # copy only files so we can handle bsd4grml on its own
746             for file in ${TEMPLATE_DIRECTORY}/boot/addons/* ; do
747               test -f $file && cp $file "$BUILD_OUTPUT"/boot/addons/
748             done
749
750             if [ -n "$NO_ADDONS_BSD4GRML" ] ; then
751                log   "Skipping installation of bsd4grml as requested via \$NO_ADDONS_BSD4GRML."
752                einfo "Skipping installation of bsd4grml as requested via \$NO_ADDONS_BSD4GRML."; eend 0
753             else
754                if [ -d "$TEMPLATE_DIRECTORY"/boot/addons/bsd4grml ] ; then
755                  cp -a ${TEMPLATE_DIRECTORY}/boot/addons/bsd4grml "$BUILD_OUTPUT"/boot/addons/
756                else
757                  log   "bsd4grml addon not found, skipping therefore."
758                  ewarn "bsd4grml addon not found, skipping therefore." ; eend 0
759                fi
760             fi
761
762           fi # no "$TEMPLATE_DIRECTORY"/boot/addons
763        fi # NO_ADDONS
764
765        if ! [ -d "${BUILD_OUTPUT}/boot/grub" ] ; then
766          mkdir -p "${BUILD_OUTPUT}/boot/grub"
767        fi
768        cp ${TEMPLATE_DIRECTORY}/boot/grub/* "$BUILD_OUTPUT"/boot/grub/
769
770        if [ -e ${TEMPLATE_DIRECTORY}/compat/grub/linux.mod ]; then
771          cp "${TEMPLATE_DIRECTORY}"/compat/grub/* "${BUILD_OUTPUT}"/boot/grub/
772        else
773          # copy system grub files if grml-live-compat is not
774          # installed.
775          cp -a /usr/lib/grub/*-pc/*.mod "${BUILD_OUTPUT}"/boot/grub/
776          cp -a /usr/lib/grub/*-pc/*.o "${BUILD_OUTPUT}"/boot/grub/
777          cp -a /usr/lib/grub/*-pc/*.lst "${BUILD_OUTPUT}"/boot/grub/
778          cp -a /usr/share/grub/ascii.pf2 "${BUILD_OUTPUT}"/boot/grub/
779          /usr/bin/grub-mkimage -d /usr/lib/grub/*-pc -o \
780            "${BUILD_OUTPUT}/boot/grub/core.img" biosdisk iso9660 --format=i386-pc
781        fi
782
783        if ! [ -d "${TEMPLATE_DIRECTORY}"/GRML ] ; then
784           log    "Error: ${TEMPLATE_DIRECTORY}/GRML does not exist. Exiting."
785           eerror "Error: ${TEMPLATE_DIRECTORY}/GRML does not exist. Exiting." ; eend 1
786           bailout 9
787        fi
788
789        [ -d "$BUILD_OUTPUT"/GRML ] || mkdir "$BUILD_OUTPUT"/GRML
790        cp -a ${TEMPLATE_DIRECTORY}/GRML/* "$BUILD_OUTPUT"/GRML/
791
792        # adjust boot splash information:
793        RELEASE_INFO="$GRML_NAME $VERSION - Release Codename $RELEASENAME"
794        RELEASE_INFO="$(cut_string 68 "$RELEASE_INFO")"
795        RELEASE_INFO="$(extend_string_end 68 "$RELEASE_INFO")"
796
797        if [ -r "$BUILD_OUTPUT"/GRML/grml-version ] ; then
798           sed -i "s/%RELEASE_INFO%/$GRML_NAME $VERSION - $RELEASENAME/" "$BUILD_OUTPUT"/GRML/grml-version
799           sed -i "s/%DATE%/$DATE/"                                      "$BUILD_OUTPUT"/GRML/grml-version
800        fi
801
802        # make sure the squashfs filename is set accordingly:
803        SQUASHFS_NAME="$GRML_NAME.squashfs"
804
805        if [ -n "$NO_BOOTID" ] ; then
806           log   'Skipping bootid feature as requested via $NO_BOOTID.'
807           einfo 'Skipping bootid feature as requested via $NO_BOOTID.'
808        else
809           [ -n "$BOOTID" ] || BOOTID="$(echo ${GRML_NAME}${VERSION} | tr -d ',./;\- ')"
810           [ -d "$BUILD_OUTPUT"/conf ] || mkdir "$BUILD_OUTPUT"/conf
811           einfo "Generating /conf/bootid.txt with entry ${BOOTID}."
812           log   "Generating /conf/bootid.txt with entry ${BOOTID}."
813           echo "$BOOTID" > "$BUILD_OUTPUT"/conf/bootid.txt
814           eend $?
815        fi
816
817        # adjust all variables in the templates with the according distribution information
818        for file in "${BUILD_OUTPUT}"/boot/isolinux/*.cfg "${BUILD_OUTPUT}"/boot/isolinux/*.msg \
819                    "${BUILD_OUTPUT}"/boot/grub/* ; do
820          if [ -r "${file}" ] ; then
821            sed -i "s/%ARCH%/$ARCH/g"                    "${file}"
822            sed -i "s/%DATE%/$DATE/g"                    "${file}"
823            sed -i "s/%DISTRI_INFO%/$DISTRI_INFO/g"      "${file}"
824            sed -i "s/%DISTRI_NAME%/$DISTRI_NAME/g"      "${file}"
825            sed -i "s/%DISTRI_SPLASH%/$DISTRI_SPLASH/g"  "${file}"
826            sed -i "s/%GRML_NAME%/$GRML_NAME/g"          "${file}"
827            sed -i "s/%SQUASHFS_NAME%/$SQUASHFS_NAME/g"  "${file}"
828            sed -i "s/%RELEASE_INFO%/$RELEASE_INFO/g"    "${file}"
829            sed -i "s/%SHORT_NAME%/$SHORT_NAME/g"        "${file}"
830            sed -i "s/%VERSION%/$VERSION/g"              "${file}"
831
832            [ -n "$DEFAULT_BOOTOPTIONS" ] && sed -i "s/ boot=live/ boot=live $DEFAULT_BOOTOPTIONS/"  "${file}"
833
834            if [ -n "$NO_BOOTID" ] ; then
835               sed -i "s/ bootid=%BOOTID%//g" "${file}" # drop bootid bootoption
836            else
837               sed -i "s/%BOOTID%/$BOOTID/g" "${file}" # adjust bootid=... argument
838            fi
839          fi
840        done
841
842        # adjust bootsplash accordingly but make sure the string has the according lenght
843        SQUASHFS_NAME="$(cut_string 20 "$SQUASHFS_NAME")"
844        SQUASHFS_NAME="$(extend_string_end 20 "$SQUASHFS_NAME")"
845        for file in f4 f5 ; do
846           if [ -r "${BUILD_OUTPUT}/boot/isolinux/${file}" ] ; then
847              sed -i "s/%SQUASHFS_NAME%/$SQUASHFS_NAME/" "${BUILD_OUTPUT}/boot/isolinux/${file}"
848              sed -i "s/%SQUASHFS_NAME%/$SQUASHFS_NAME/" "${BUILD_OUTPUT}/boot/isolinux/${file}"
849           fi
850        done
851
852        # generate addon list
853        rm -f "${BUILD_OUTPUT}/${ADDONS_LIST_FILE}"
854        for name in "${BUILD_OUTPUT}"/boot/isolinux/addon_*.cfg ; do
855          include_name=$(basename "$name")
856          echo "include $include_name"  >> "${BUILD_OUTPUT}/${ADDONS_LIST_FILE}"
857        done
858
859        if ! [ -r "${BUILD_OUTPUT}/boot/isolinux/${DISTRI_NAME}.cfg" ] || [ "$DISTRI_NAME" = "grml" ] ; then
860           log "including grmlmain.cfg in ${BUILD_OUTPUT}/boot/isolinux/distri.cfg"
861           echo "include grmlmain.cfg"    >  "${BUILD_OUTPUT}/boot/isolinux/distri.cfg"
862           echo "include default.cfg"     >  "${BUILD_OUTPUT}/boot/isolinux/grmlmain.cfg"
863           echo "include menuoptions.cfg" >> "${BUILD_OUTPUT}/boot/isolinux/grmlmain.cfg"
864           echo "include grml.cfg"        >> "${BUILD_OUTPUT}/boot/isolinux/grmlmain.cfg"
865
866           for f in "${BUILD_OUTPUT}"/boot/isolinux/submenu*.cfg ; do
867             echo "include $(basename $f)"     >> "${BUILD_OUTPUT}/boot/isolinux/grmlmain.cfg"
868           done
869
870           echo "include options.cfg"     >> "${BUILD_OUTPUT}/boot/isolinux/grmlmain.cfg"
871           if [ ! -n "$NO_ADDONS" ] ; then
872             echo "include addons.cfg"    >> "${BUILD_OUTPUT}/boot/isolinux/grmlmain.cfg"
873           fi
874           echo "include isoprompt.cfg"   >> "${BUILD_OUTPUT}/boot/isolinux/grmlmain.cfg"
875           echo "include hd.cfg"          >> "${BUILD_OUTPUT}/boot/isolinux/grmlmain.cfg"
876           echo "include hidden.cfg"      >> "${BUILD_OUTPUT}/boot/isolinux/grmlmain.cfg"
877        else # assume we are building a custom distribution:
878           log "File ${BUILD_OUTPUT}/boot/isolinux/${DISTRI_NAME}.cfg found, using it."
879           einfo "File ${BUILD_OUTPUT}/boot/isolinux/${DISTRI_NAME}.cfg found, using it."
880           if grep -q "^include ${DISTRI_NAME}.cfg" "${BUILD_OUTPUT}/boot/isolinux/distri.cfg" ; then
881             log "include for ${DISTRI_NAME}.cfg already present, nothing to do."
882             eindent
883             einfo "include for ${DISTRI_NAME}.cfg already present, nothing to do."
884             eoutdent
885             eend $?
886          else
887             log "including ${DISTRI_NAME}.cfg in ${BUILD_OUTPUT}/boot/isolinux/distri.cfg"
888             echo "include ${DISTRI_NAME}.cfg" > "${BUILD_OUTPUT}/boot/isolinux/distri.cfg"
889             [ -n "$NO_ADDONS" ] || echo "include addons.cfg" >> "${BUILD_OUTPUT}/boot/isolinux/distri.cfg"
890           fi
891        fi
892
893        # use old style console based isolinux method only if requested:
894        if [[ "${ISOLINUX_METHOD}" == "console" ]] ; then
895           log 'Using console based isolinux method as requested via $ISOLINUX_METHOD.'
896           einfo 'Using console based isolinux method as requested via $ISOLINUX_METHOD.'
897           if grep -q '^include console.cfg' "${BUILD_OUTPUT}/boot/isolinux/distri.cfg" ; then
898             einfo "include for console.cfg already found, nothing to do."
899             eend 0
900           else
901             log "including console.cfg in ${BUILD_OUTPUT}/boot/isolinux/isolinux.cfg"
902             einfo "including console.cfg in ${BUILD_OUTPUT}/boot/isolinux/isolinux.cfg"
903             echo "include console.cfg" >> "${BUILD_OUTPUT}/boot/isolinux/isolinux.cfg"
904             eend $?
905           fi
906        else
907           log 'Using graphical boot menu.'
908           if grep -q '^include vesamenu.cfg' "${BUILD_OUTPUT}/boot/isolinux/isolinux.cfg" ; then
909             log "include for vesamenu.cfg already found, nothing to do."
910           else
911             log "including vesamenu.cfg in ${BUILD_OUTPUT}/boot/isolinux/isolinux.cfg"
912             echo "include vesamenu.cfg" >> "${BUILD_OUTPUT}/boot/isolinux/isolinux.cfg"
913           fi
914        fi
915
916        if [ -e "$BUILD_OUTPUT"/boot/addons/bsd4grml/boot.6 ]; then
917           sed -i "s/%RELEASE_INFO%/$GRML_NAME $VERSION - $RELEASENAME/" "$BUILD_OUTPUT"/boot/addons/bsd4grml/boot.6
918        fi
919
920        DPKG_LIST="/var/log/fai/$HOSTNAME/last/dpkg.list" # the dpkg --list output of the chroot
921        if ! [ -r "$DPKG_LIST" ] ; then
922           ewarn "$DPKG_LIST could not be read, ignoring to store package information on ISO therefore."
923        else
924           einfo "Storing package list information as /GRML/${GRML_NAME}-packages.txt on ISO."
925           cp "$DPKG_LIST" "${BUILD_OUTPUT}/GRML/${GRML_NAME}-packages.txt"
926           eend $?
927        fi
928
929        # autostart for Windows:
930        if [ -d "${TEMPLATE_DIRECTORY}/windows/autostart/" ] ; then
931           cp ${TEMPLATE_DIRECTORY}/windows/autostart/* "$BUILD_OUTPUT"/
932        fi
933
934     FORCE_ISO_REBUILD=true
935     einfo "Finished execution of stage 'boot'" ; eend 0
936     fi
937   fi # BOOTSTRAP_ONLY
938 else
939    log    'Error: Unsupported ARCH, sorry. Want to support it? Contribute!'
940    eerror 'Error: Unsupported ARCH, sorry. Want to support it? Contribute!' ; eend 1
941    bailout
942 fi
943
944 # support installation of local files into the chroot/ISO
945 if [ -n "$CHROOT_INSTALL" ] ; then
946   if ! [ -d "$CHROOT_INSTALL" ] ; then
947      log "Configuration variable \$CHROOT_INSTALL is set but not a directory; ignoring"
948      ewarn "Configuration variable \$CHROOT_INSTALL is set but not a directory; ignoring"
949   else
950      log "Copying local files to chroot as requested via \$CHROOT_INSTALL"
951      einfo "Copying local files to chroot as requested via \$CHROOT_INSTALL"
952      rsync -avz --inplace "$CHROOT_INSTALL"/ "$CHROOT_OUTPUT/"
953      eend $?
954      einfo "Make sure to run squashfs stage, otherwise your local files won't be part of the ISO."
955      FORCE_ISO_REBUILD=true
956   fi
957 fi
958
959 if [ -f "$BUILD_OUTPUT"/live/${GRML_NAME}.squashfs -a -z "$UPDATE" -a -z "$BUILD_ONLY" -a -z "$BUILD_DIRTY" ] ; then
960    log   "Skipping stage 'squashfs' as $BUILD_OUTPUT/live exists already."
961    ewarn "Skipping stage 'squashfs' as $BUILD_OUTPUT/live exists already." ; eend 0
962 elif [ -n "$SKIP_MKSQUASHFS" ] ; then
963    log   "Skipping stage 'squashfs' as requested via option -q or -N"
964    ewarn "Skipping stage 'squashfs' as requested via option -q or -N" ; eend 0
965 else
966    [ -d "$BUILD_OUTPUT"/live ] || mkdir "$BUILD_OUTPUT"/live
967    # make sure we don't leave (even an empty) base.tgz:
968    [ -f "$CHROOT_OUTPUT/base.tgz" ] && rm -f "$CHROOT_OUTPUT/base.tgz"
969
970    # if unconfigured default to squashfs-tools' mksquashfs binary
971    if [ -z "$SQUASHFS_BINARY" ] ; then
972       SQUASHFS_BINARY='mksquashfs'
973    fi
974
975    if which "$SQUASHFS_BINARY" >/dev/null 2>&1 ; then
976       log    "Using mksquashfs binary ${SQUASHFS_BINARY}"
977       einfo  "Using mksquashfs binary ${SQUASHFS_BINARY}" ; eend 0
978    else
979       log    "Error: mksquashfs binary ($SQUASHFS_BINARY) not found. Exiting."
980       eerror "Error: mksquashfs binary ($SQUASHFS_BINARY) not found. Exiting." ; eend 1
981       bailout
982    fi
983
984    # use sane defaults if $SQUASHFS_OPTIONS isn't set
985    if [ -z "$SQUASHFS_OPTIONS" ] ; then
986      # use blocksize 256k as this gives best result with regards to time + compression
987      SQUASHFS_OPTIONS="-b 256k"
988
989      # set lzma/xz compression by default, unless -z option has been specified on command line
990      if [ -z "$SQUASHFS_ZLIB" ] ; then
991         SQUASHFS_OPTIONS="$SQUASHFS_OPTIONS -comp xz"
992      else
993         SQUASHFS_OPTIONS="$SQUASHFS_OPTIONS -comp gzip"
994      fi
995    fi
996
997    # support exclusion of files via exclude-file:
998    if [ -n "$SQUASHFS_EXCLUDES_FILE" -a "$SQUASHFS_EXCLUDES_FILE" ] ; then
999       SQUASHFS_OPTIONS="$SQUASHFS_OPTIONS -ef $SQUASHFS_EXCLUDES_FILE -wildcards"
1000    fi
1001
1002    # get rid of unnecessary files when building grml-small for final release:
1003    if echo "$CLASSES" | grep -q GRML_SMALL ; then
1004       SQUASHFS_OPTIONS="$SQUASHFS_OPTIONS -e initrd.img* vmlinuz*"
1005    fi
1006
1007    # log stuff
1008    SQUASHFS_STDERR="$(mktemp -t grml-live.XXXXXX)"
1009
1010    # informational stuff
1011    [ -n "$SQUASHFS_OPTIONS" ]  && SQUASHFS_INFO_MSG="$SQUASHFS_OPTIONS"
1012    [ -n "$SQUASHFS_INFO_MSG" ] && SQUASHFS_INFO_MSG="using options: $SQUASHFS_INFO_MSG"
1013    einfo "Squashfs build information: running binary $SQUASHFS_BINARY $SQUASHFS_INFO_MSG"
1014
1015    log "$SQUASHFS_BINARY $CHROOT_OUTPUT/ $BUILD_OUTPUT/live/${GRML_NAME}.squashfs -noappend $SQUASHFS_OPTIONS"
1016
1017    if $SQUASHFS_BINARY $CHROOT_OUTPUT/ $BUILD_OUTPUT/live/"${GRML_NAME}".squashfs \
1018       -noappend $SQUASHFS_OPTIONS 2>"${SQUASHFS_STDERR}" ; then
1019       echo "${GRML_NAME}.squashfs" > $BUILD_OUTPUT/live/filesystem.module
1020       log "Finished execution of stage 'squashfs' [$(date)]"
1021       einfo "Finished execution of stage 'squashfs'" ; eend 0
1022    else
1023       log    "Error: there was a critical error executing stage 'squashfs' [$(date)]:"
1024       log    "$(cat $SQUASHFS_STDERR)"
1025       eerror "Error: there was a critical error executing stage 'squashfs':"
1026       cat    "${SQUASHFS_STDERR}"
1027       eend 1
1028       bailout
1029    fi
1030
1031    FORCE_ISO_REBUILD=true
1032 fi
1033
1034 # create md5sum file:
1035 if [ -z "$BOOTSTRAP_ONLY" ] ; then
1036   ( cd $BUILD_OUTPUT/GRML &&
1037   find .. -type f -not -name md5sums -not -name isolinux.bin -exec md5sum {} \; > md5sums )
1038 fi
1039 # }}}
1040
1041 # ISO_OUTPUT - mkisofs {{{
1042 [ -n "$ISO_OUTPUT" ] || ISO_OUTPUT="$OUTPUT/grml_isos"
1043 [ -n "$ISO_NAME" ] || ISO_NAME="${GRML_NAME}_${VERSION}.iso"
1044
1045 if [ "$BOOT_METHOD" = "isolinux" ] ; then
1046    BOOT_ARGS="-no-emul-boot -boot-load-size 4 -boot-info-table -b boot/isolinux/isolinux.bin -c boot/isolinux/boot.cat"
1047 elif [ "$BOOT_METHOD" = "grub2" ] ; then
1048    BOOT_ARGS="-no-emul-boot -boot-load-size 4 -b boot/grub/toriboot.bin"
1049 fi
1050
1051 # Just until http://bts.grml.org/grml/issue945 has been resolved.
1052 # HYBRID_METHOD defaults to manifold, so make sure the default works OOTB.
1053 if [[ $BOOT_METHOD != isolinux && ($HYBRID_METHOD = isohybrid || $HYBRID_METHOD = manifold) ]]; then
1054   log   "Setting HYBRID_METHOD to grub2 as hybrid mode does not work with isohybrid yet."
1055   ewarn "Setting HYBRID_METHOD to grub2 as hybrid mode does not work with isohybrid yet."
1056   HYBRID_METHOD='grub2'
1057   eend 0
1058 fi
1059
1060 if [ -f "${ISO_OUTPUT}/${ISO_NAME}" -a -z "$UPDATE" -a -z "$BUILD_ONLY" -a -z "$BUILD_DIRTY" -a "$FORCE_ISO_REBUILD" = "false" ]  ; then
1061    log   "Skipping stage 'iso build' as $ISO_OUTPUT/${ISO_NAME} exists already."
1062    ewarn "Skipping stage 'iso build' as $ISO_OUTPUT/${ISO_NAME} exists already." ; eend 0
1063 elif [ -n "$SKIP_MKISOFS" ] ; then
1064    log   "Skipping stage 'iso build' as requested via option -n or -N"
1065    ewarn "Skipping stage 'iso build' as requested via option -n or -N" ; eend 0
1066 else
1067    mkdir -p "$ISO_OUTPUT" || bailout 6 "Problem with creating $ISO_OUTPUT for stage 'iso build'"
1068
1069    if $FORCE_ISO_REBUILD && ! [ -f "${ISO_OUTPUT}/${ISO_NAME}" ] ; then
1070       log   "Forcing rebuild of ISO because files on ISO have been modified."
1071       einfo "Forcing rebuild of ISO because files on ISO have been modified."
1072    fi
1073
1074    # support mkisofs as well as genisoimage
1075    if which mkisofs >/dev/null 2>&1; then
1076       MKISOFS='mkisofs'
1077    elif which genisoimage >/dev/null 2>&1; then
1078       MKISOFS='genisoimage'
1079    else
1080       log    "Error: neither mkisofs nor genisoimage available - can not create ISO."
1081       eerror "Error: neither mkisofs nor genisoimage available - can not create ISO." ; eend 1
1082       bailout
1083    fi
1084
1085    CURRENT_DIR=$(pwd)
1086    if cd "$BUILD_OUTPUT" ; then
1087       if [ "$BOOT_METHOD" = "grub2" ]; then
1088          # make a 2048-byte bootsector for El Torito
1089          dd if=/dev/zero of=boot/grub/toriboot.bin bs=512 count=4 2>/dev/null
1090          # those are in 2048-byte sectors, so 1 16 matches 4 63 below
1091          echo 1 16 | mksh /usr/share/grml-live/scripts/bootgrub.mksh -B 11 | \
1092             dd of=boot/grub/toriboot.bin conv=notrunc 2>/dev/null
1093       fi
1094       log "$MKISOFS -V '${GRML_NAME} ${VERSION}' -publisher 'grml-live | grml.org' -l -r -J $BOOT_ARGS -o ${ISO_OUTPUT}/${ISO_NAME} ."
1095       "$MKISOFS" -V "${GRML_NAME} ${VERSION}" -publisher 'grml-live | grml.org' \
1096               -l -r -J $BOOT_ARGS -no-pad \
1097               -o "${ISO_OUTPUT}/${ISO_NAME}" . ; RC=$?
1098       # both of these need core.img there, so it’s easier to write it here
1099       if [ "$BOOT_METHOD" = "grub2" ] || [ "$HYBRID_METHOD" = "grub2" ]; then
1100          # must be <= 30720 bytes
1101          dd if=boot/grub/core.img of="${ISO_OUTPUT}/${ISO_NAME}" \
1102            conv=notrunc bs=512 seek=4 2>/dev/null
1103       fi
1104
1105       # pad the output ISO to multiples of 256 KiB for partition table support
1106       siz=$($getfilesize "${ISO_OUTPUT}/${ISO_NAME}")
1107       cyls=$((siz / 512 / 32 / 16 + 1))   # C=$cyls H=16 S=32
1108       siz=$((cyls * 16 * 32 * 512))   # size after padding
1109       dd if=/dev/zero bs=1 count=1 seek=$((siz - 1)) \
1110          of="${ISO_OUTPUT}/${ISO_NAME}" 2>/dev/null
1111
1112       # support disabling hybrid ISO image
1113       if [ "$HYBRID_METHOD" = "disable" ] ; then\
1114          log   "Skipping creation of hybrid ISO file as requested via HYBRID_METHOD=disable"
1115          einfo "Skipping creation of hybrid ISO file as requested via HYBRID_METHOD=disable"
1116          eend 0
1117       # use isohybrid only on request
1118       elif [ "$HYBRID_METHOD" = "isohybrid" ] ; then
1119          if ! which isohybrid >/dev/null 2>&1 ; then
1120            bailout 12 "isohybrid binary not found - please install syslinux/syslinux-common"
1121          else
1122            log "Creating hybrid ISO file with isohybrid method"
1123            einfo "Creating hybrid ISO file with isohybrid method"
1124            # Notes for consideration:
1125            # "-entry 4 -type 1c"
1126            # * using 4 as the partition number is supposed to help with BIOSes
1127            #   that only support USB-Zip boot
1128            # * using 1c (i.e. hidden FAT32 LBA), instead of the default 0x17
1129            #   (hidden NTFS, IIRC), as the partition type is sometimes needed
1130            #   to get the BIOS even look at the partition created by isohybrid
1131            isohybrid "${ISO_OUTPUT}/${ISO_NAME}"
1132            eend $?
1133          fi
1134       # by default use our manifold boot method:
1135       else
1136          # isoinfo is part of both mkisofs and genisoimage so we're good
1137          bootoff=$(isoinfo -l -i "${ISO_OUTPUT}/${ISO_NAME}" | \
1138            sed -n '/^.*\[ *\([0-9]*\)[] ].* ISOLINUX.BIN;1 *$/s//\1/p')
1139          if ! [ -r boot/grub/core.img ] ; then
1140            ewarn "boot/grub/core.img not found, not creating manifold boot ISO file"
1141          elif [ "${bootoff:-0}" -lt 1 ] ; then
1142            ewarn "isolinux.bin not found on the ISO file, disabling manifold boot"
1143          else
1144            log "Creating hybrid ISO file with manifold method"
1145            einfo "Creating hybrid ISO file with manifold method"
1146            if [ "$HYBRID_METHOD" = "grub2" ] ; then
1147                # 512 bytes: MBR, partition table, load GRUB 2
1148                echo 4 63 | mksh /usr/share/grml-live/scripts/bootgrub.mksh -A -M 4:0x96 -g $cyls:16:32
1149            else
1150               # read only one but 2048-byte sized (scale: << 2) sector
1151               echo $bootoff $bootoff | \
1152                  mksh /usr/share/grml-live/scripts/bootilnx.mksh -A -M 4:0x96 -g $cyls:16:32 -S 2
1153            fi | dd of="${ISO_OUTPUT}/${ISO_NAME}" conv=notrunc 2>/dev/null
1154            eend $?
1155          fi
1156       fi
1157
1158       # generate md5sum and sha1sum of ISO if we are using class 'RELEASE':
1159       case $CLASSES in *RELEASE*)
1160          [ "$RC" = 0 ] && \
1161          (
1162            if cd $ISO_OUTPUT ; then
1163              md5sum ${ISO_NAME} > ${ISO_NAME}.md5 && \
1164              touch -r ${ISO_NAME} ${ISO_NAME}.md5
1165              sha1sum ${ISO_NAME} > ${ISO_NAME}.sha1 && \
1166              touch -r ${ISO_NAME} ${ISO_NAME}.sha1
1167            fi
1168          )
1169          ;;
1170       esac
1171
1172       cd "$CURRENT_DIR"
1173    fi
1174
1175    if [ "$RC" = 0 ] ; then
1176       log   "Finished execution of stage 'iso build' [$(date)]"
1177       einfo "Finished execution of stage 'iso build'" ; eend 0
1178    else
1179       log    "Error: there was a critical error ($RC) executing stage 'iso build' [$(date)]"
1180       eerror "Error: there was a critical error executing stage 'iso build'" ; eend 1
1181       bailout $RC
1182    fi
1183 fi
1184 # }}}
1185
1186 # log build information to database if grml-live-db is installed and enabled {{{
1187 dpkg_to_db() {
1188 if [ -d /usr/share/grml-live-db ] ; then
1189
1190   # safe defaults
1191   DPKG_LIST="/var/log/fai/$HOSTNAME/last/dpkg.list" # the dpkg --list output of the chroot:
1192   [ -n "$DPKG_DATABASE" ]  || DPKG_DATABASE=/var/log/grml-live.db
1193   [ -n "$DPKG_DBSCRIPT" ]  || DPKG_DBSCRIPT=/usr/share/grml-live-db/scripts/dpkg-to-db
1194   [ -n "$DPKG_DBOPTIONS" ] || DPKG_DBOPTIONS="--database $DPKG_DATABASE --logfile $LOGFILE --flavour $GRML_NAME --dpkg $DPKG_LIST"
1195
1196   if ! [ -x "$DPKG_DBSCRIPT" ] ; then
1197     log "Error: $DPKG_DBSCRIPT is not executable, can not log dpkg information."
1198     eerror "Error: $DPKG_DBSCRIPT is not executable, can not log dpkg information." ; eend 1
1199     bailout 14
1200   fi
1201
1202   # disable by default for now, not sure whether really everyone is using a local db file
1203   #if ! touch "$DPKG_DATABASE" ; then
1204   #  eerror "Error: can not write to ${DPKG_DATABASE}, can not log dpkg information." ; eend 1
1205   #  bailout 14
1206   #fi
1207
1208   if ! [ -r "$DPKG_LIST" ] ; then
1209      log   "Warning: can not read $DPKG_LIST - can not provide information to $DPKG_DBSCRIPT (dirty build?)"
1210      ewarn "Warning: can not read $DPKG_LIST - can not provide information to $DPKG_DBSCRIPT (dirty build?)" ; eend 0
1211   else
1212      einfo "Logging $DPKG_LIST to database $DPKG_DATABASE"
1213      log "Logging $DPKG_LIST to database $DPKG_DATABASE"
1214      log "Executing $DPKG_DBSCRIPT $DPKG_DBOPTIONS"
1215      eindent
1216
1217      if DB_INFO=$("$DPKG_DBSCRIPT" $DPKG_DBOPTIONS 2>&1) ; then
1218        einfo "$DB_INFO"
1219        eend 0
1220      else
1221        eerror "$DB_INFO"
1222        eend 1
1223      fi
1224
1225      eoutdent
1226   fi
1227
1228 fi
1229 }
1230 # }}}
1231
1232 # finalize {{{
1233 [ -n "$start_seconds" ] && SECONDS="$[$(cut -d . -f 1 /proc/uptime)-$start_seconds]" || SECONDS="unknown"
1234 log "Successfully finished execution of $PN [$(date) - running ${SECONDS} seconds]"
1235
1236 dpkg_to_db # make sure we catch the last log line as well, therefore execute between log + einfo
1237
1238 einfo "Successfully finished execution of $PN [$(date) - running ${SECONDS} seconds]" ; eend 0
1239 bailout 0
1240 # }}}
1241
1242 ## END OF FILE #################################################################
1243 # vim:foldmethod=marker ts=2 ft=sh ai expandtab tw=80 sw=2