8f4d7b841965cc15f99032f8cabcfe9bd576c417
[grml2usb.git] / grml2iso
1 #!/usr/bin/env bash
2 # Filename:      grml2iso
3 # Purpose:       create a multiboot grml ISO using grml2usb
4 # Authors:       Michael Prokop <mika@grml.org>,
5 #                Thorsten Glaser <tg@mirbsd.org>
6 # Bug-Reports:   see http://grml.org/bugs/
7 # License:       This file is licensed under the GPL v2 or any later version.
8 ################################################################################
9
10 # define function getfilesize before "set -e" {{{
11   if stat --help >/dev/null 2>&1; then
12     getfilesize='stat -c %s'        # GNU stat
13   else
14     getfilesize='stat -f %z'        # BSD stat
15   fi
16 # }}}
17
18 # adjust variables if necessary through environment {{{
19 # path to the grml2usb script you'd like to use
20   [ -n "$GRML2USB" ] || GRML2USB='grml2usb'
21 # work directory for creating the filesystem
22   [ -n "$TMPDIR" ]   && WRKDIR="${TMPDIR}/grml2iso.tmp"
23   [ -n "$WRKDIR" ]   || WRKDIR='/tmp/grml2iso.tmp'
24 # support mkisofs as well as genisoimage
25 if which xorriso >/dev/null 2>&1 ; then
26   MKISOFS='xorriso -as mkisofs'
27 elif which mkisofs >/dev/null 2>&1; then
28   MKISOFS='mkisofs'
29 elif which genisoimage >/dev/null 2>&1; then
30   MKISOFS='genisoimage'
31 else
32   echo >&2 "Error: neither mkisofs nor genisoimage available - can not create ISO."
33   exit 1
34 fi
35
36 if ! which isohybrid >/dev/null 2>&1 ; then
37   echo "Error: isohybrid executable not found (install syslinux?)." >&2
38   exit 1
39 fi
40 # }}}
41
42 # helper stuff {{{
43   set -e
44
45   usage() {
46     echo >&2 "Usage: $0 [OPTIONS] -o target.iso source1.iso [source2.iso ...]"
47     echo >&2 "
48 Options:
49      -b Boot Params      Additional boot parameters passed to grml2usb
50      -c Directory        Copy files from directory to generated ISO
51      -f                  Force overwrite of existing target.iso
52      -r BootParam        Remove specified boot params.
53                          Can be specified multiple times.
54      -p <grml2usb param> Add the specified parameter to the grml2usb
55                          commandline. For a list of valid parameters have
56                          a look at the grml2usb manpage.
57                          Can be specified multiple times.
58      -s URI              Generate a small ISO file which downloads the squashfs
59                          file from the specified URI. Please note that due to
60                          restrictions in the bootprocess only IPs are allowed.
61                          Supported protocols are: http and ftp
62      -t Directory        Directory that should be used for temporary files
63                          during build. Defaults to /tmp/grml2iso.tmp if unset.
64
65      Examples:
66      $0 -s http://192.168.23.42:8000/grml/ -o small.iso grml64_2010.12.iso
67
68      Will generate a file small.iso which tries to download the squashfs file from
69      http://192.168.23.42:8000/grml/ - the squashfs file is placed in the same
70      output directory as the ISO file.
71 "
72     [ -n "$1" ] && exit $1 || exit 1
73   }
74 # }}}
75
76 # command line handling {{{
77   [[ $# -gt 2 ]] || usage 1
78
79   ISOFILE=''
80   DIR=''
81   ADD_OPTS=''
82   FORCE=''
83   URI=''
84   typeset -a GRML2USB_OPTS
85   while getopts fb:c:o:r:p:s:t: name; do
86     case $name in
87       o)   ISOFILE="$OPTARG";;
88       b)   GRML2USB_OPTS+=(--bootoptions="$OPTARG");;
89       c)   DIR="$(readlink -f "$OPTARG")"; [ -n "$DIR" ] || { echo "Could not read $OPTARG - exiting" >&2 ; exit 1 ; } ;;
90       f)   FORCE='true';;
91       r)   GRML2USB_OPTS+=(--remove-bootoption="$OPTARG");;
92       p)   GRML2USB_OPTS+=("$OPTARG");;
93       s)   URI="$OPTARG";;
94       t)   WRKDIR="$OPTARG";;
95       ?)   usage 2;;
96     esac
97   done
98
99   # test for specified URI
100   if [ -n "$URI" ] ; then
101     GRML2USB_OPTS+=(--bootoptions="fetch=$URI")
102   fi
103
104   if [ -n "$WRKDIR" ] ; then
105     GRML2USB_OPTS+=(--tmpdir="$WRKDIR")
106   fi
107
108 # make sure -o is specified
109   [ -n "$ISOFILE" ] || usage 1
110
111 # we don't to override any files by accident
112   if [ -e "$ISOFILE" -a ! -n "$FORCE" ]; then
113     echo "Error: target file $ISOFILE exists already." >&2
114     exit 1
115   fi
116
117   if [ ! -z "$DIR" -a ! -d "$DIR" ] ; then
118      echo "Error: specified parameter for -c is not a directory" >&2
119      exit 1
120   fi
121 # }}}
122
123 # we need root permissions for executing grml2usb {{{
124   if [[ $(id -u) != 0 ]]; then
125     echo >&2 "Error: please run $0 as root."
126     exit 1
127   fi
128 # }}}
129
130 # check for grml2usb {{{
131   if [ ! -x "$(which $GRML2USB)" ] && [ ! -x "$GRML2USB" ] ; then
132     echo >&2 "Error: Could not find grml2usb"
133     if [ -x "./$GRML2USB" ] ; then
134       echo >&2 "If you executed grml2iso from the grml2usb repository use"
135       echo >&2 "GRML2USB=./grml2usb $0 $*"
136     fi
137     exit 1
138   fi
139 # }}}
140
141 # variables {{{
142   ORIG_DIR="$(pwd)"
143
144 # normalise path
145   case $ISOFILE in
146     /*) ;;
147     *) ISOFILE=$ORIG_DIR/$ISOFILE ;;
148   esac
149 # }}}
150
151 # create necessary stuff under WRKDIR {{{
152   [ -d "$WRKDIR" ] && WRKDIR_EXISTED='true' || WRKDIR_EXISTED='false'
153   rm -rf "$WRKDIR/cddir" "$WRKDIR/grub_tmp"
154   mkdir -p "$WRKDIR/cddir"
155 # }}}}
156
157 # execute grml2usb with all ISOs you'd like to install {{{
158   # remove all parameters
159   shift $(($OPTIND - 1))
160
161   $GRML2USB "${GRML2USB_OPTS[@]}" "$@" "$WRKDIR/cddir"
162 # }}}
163
164 # move syslinux to isolinux {{{
165   mv "$WRKDIR"/cddir/boot/syslinux "$WRKDIR"/cddir/boot/isolinux
166   echo "menu label ^Isolinux prompt" > "$WRKDIR"/cddir/boot/isolinux/promptname.cfg
167   echo "include hd.cfg" >> "$WRKDIR"/cddir/boot/isolinux/grmlmain.cfg
168 # }}}
169
170 # change to $WRKDIR {{{
171   # make sure $WRKDIR is an absolute path, otherwise accessing files
172   # in it will fail later in the code path if user provided a
173   # relative directory
174   WRKDIR=$(realpath $WRKDIR)
175   cd "$WRKDIR/cddir"
176 # }}}
177
178 # efi boot {{{
179   # default, independent of UEFI support
180   BOOT_ARGS="-no-emul-boot -boot-load-size 4 -boot-info-table -b boot/isolinux/isolinux.bin -c boot/isolinux/boot.cat"
181
182   case "$MKISOFS" in
183     xorriso*)
184       echo "Using xorriso for ISO generation."
185       if ! dpkg --compare-versions $(dpkg-query -W -f='${Version}\n' xorriso 2>/dev/null) gt-nl 1.1.6-1 ; then
186         echo "Disabling (U)EFI boot support since xorriso version is not recent enough."
187       else
188         echo "xorriso with -eltorito-alt-boot support present"
189
190         if ! [ -r "${WRKDIR}/cddir/boot/efi.img" ] ; then
191           echo "File /boot/efi.img not found, not extending boot arguments for (U)EFI boot."
192         else
193           echo "/boot/efi.img found, extending boot arguments for (U)EFI boot."
194           BOOT_ARGS="$BOOT_ARGS -boot-info-table -eltorito-alt-boot -e boot/efi.img -no-emul-boot"
195         fi
196       fi
197       ;;
198   esac
199 # }}}
200
201 # adjust ISO for small output if necessary {{{
202   if [ -n "$URI" ] ; then
203      bootloader_files=$(find . -name "*.cfg" -type f)
204      bootloader_files+=" "
205      bootloader_files+=$(find . -name "*.lst" -type f)
206      output_dir=$(dirname "$ISOFILE")
207      for squashfs in $(find . -name *.squashfs) ; do
208         media_path="$(dirname "$squashfs")"
209         filename="$(basename "$squashfs")"
210         target="$output_dir/$filename"
211         if [ -f "$target" ] && [ ! -n "$FORCE" ] ; then
212            echo >&2 "Warning: $target already exists, and -force not specified, not overwriting"
213         else
214            mv $squashfs $target
215            OUTPUT_FILES+=("$target")
216         fi
217         sed -i -e "s#^\(^.*$media_path.*\)\($URI\)\(.*$\)#\1$URI/$filename\3#g" $bootloader_files
218
219    done
220   fi
221 # }}}
222
223 # copy specified directory to cd {{{
224   if [ -n "$DIR" ] ; then
225      echo >&2 "Copying ${DIR} to generated ISO"
226      for param in GRML_NAME VERSION RELEASENAME DATE SHORT_NAME \
227          VERSION BOOTID RELEASE_INFO ; do
228        EXCLUDE_PARAM="$EXCLUDE_PARAM --exclude **%${param}%**"
229      done
230      rsync -a ${DIR}/ $EXCLUDE_PARAM .
231   fi
232
233   # adjust files from overlay directory
234   for GRML_VERSION_FILE in $(find . -name grml-version) ; do
235     GRML_NAME=$(awk '{print $1}' "$GRML_VERSION_FILE")
236     VERSION=$(awk '{print $2}' "$GRML_VERSION_FILE")
237     RELEASENAME=$(sed 's/.*- \(.*\).*\[.*/\1/' "$GRML_VERSION_FILE")
238     DATE=$(sed 's/.*\[\(.*\)].*/\1/' "$GRML_VERSION_FILE")
239     SHORT_NAME="$(echo $GRML_NAME | tr -d ',./;\- ')"
240     RELEASE_INFO="$GRML_NAME $VERSION - $RELEASENAME"
241     BOOTID=$(cat conf/bootid.txt)
242
243     for param in GRML_NAME VERSION RELEASENAME DATE SHORT_NAME \
244         RELEASE_INFO BOOTID  ; do
245       value="$(eval echo '$'"$param")"
246
247       # copy parameterized files from the overlay directory
248       for file in $(find ${DIR} -name "*%$param%*") ; do
249         file=${file##$DIR/}
250         target_dir="$(dirname ${file})"
251         mkdir -p "$target_dir" || true
252         cp -r ${DIR}/${file} ./${target_dir}/"$(basename ${file/\%${param}\%/$value})"
253       done
254
255       # adjust config files
256       for file in ./boot/isolinux/*.cfg ./boot/isolinux/*.msg \
257         ./boot/grub/*.cfg ; do
258         sed -i "s/%$param%/$value/g" ${file} 2>/dev/null || true
259       done
260     done
261   done
262 # }}}
263
264 # generate the CD/DVD ISO {{{
265   $MKISOFS -V 'grml-multiboot' -l -r -J -no-pad $BOOT_ARGS \
266     -o "$ISOFILE" .
267 # }}}
268
269 # pad the output ISO to multiples of 256 KiB for partition table support {{{
270   siz=$($getfilesize "$ISOFILE")
271   cyls=$(($siz / 512 / 32 / 16 + 1))  # C=$cyls H=16 S=32
272   ofs=$(($cyls * 16 * 32 * 512 - 1))  # padding offset (size - 1)
273   dd if=/dev/zero bs=1 count=1 seek=$ofs of="$ISOFILE" 2>/dev/null
274 # }}}
275
276 # make ISO dd-able {{{
277   if isohybrid --help | grep -q -- --uefi ; then
278     echo "isohybrid version supports --uefi option, enabling"
279     ISOHYBRID_OPTIONS=--uefi
280  else
281     echo "isohybrid version does NOT support --uefi option, disabling"
282   fi
283
284   echo "Creating dd-able ISO using isohybrid"
285   isohybrid $ISOHYBRID_OPTIONS "$ISOFILE"
286 # }}}
287
288 # cleanup {{{
289   cd "$ORIG_DIR"
290   sync
291   rm -rf "$WRKDIR/cddir" "$WRKDIR/grub_tmp"
292   [[ $WRKDIR_EXISTED = 'false' ]] && rmdir "$WRKDIR"
293   echo "Generated $ISOFILE"
294   if [ -n "$URI" ] ; then
295      echo "
296 Information:
297 ==============
298 You requested to generate a small ISO image. Your generated
299 ISO image $ISOFILE does _not_ contain the squashfs files from
300 the source ISO images.
301
302 You have to provide the extracted squashfs files under $URI.
303
304 ISO image: $ISOFILE
305 Squashfs files: ${OUTPUT_FILES[@]}
306 URI: $URI
307 "
308   fi
309 # }}}
310
311 ## EOF #########################################################################
312 # vim:foldmethod=marker ts=2 ft=sh ai expandtab tw=80 sw=3