632806abc829023fec77dadf0a9bb88bca1805bd
[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 "Error: neither xorriso nor mkisofs nor genisoimage available - can not create ISO." >&2
33   exit 1
34 fi
35
36 if ! which isohybrid >/dev/null 2>&1 ; then
37   echo "Error: isohybrid executable not found (install syslinux/isolinux?)." >&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="$(readlink -f "$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 "Error: Could not find grml2usb executable. Is /usr/sbin missing in PATH?" >&2
133     echo "Tip: run GRML2USB=/usr/sbin/grml2usb grml2iso ... as workaround" >&2
134     if [ -x "./$GRML2USB" ] ; then
135       echo >&2 "If you executed grml2iso from the grml2usb repository use"
136       echo >&2 "GRML2USB=./grml2usb $0 $*"
137     fi
138     exit 1
139   fi
140 # }}}
141
142 # variables {{{
143   ORIG_DIR="$(pwd)"
144
145 # normalise path
146   case $ISOFILE in
147     /*) ;;
148     *) ISOFILE=$ORIG_DIR/$ISOFILE ;;
149   esac
150 # }}}
151
152 # create necessary stuff under WRKDIR {{{
153   [ -d "$WRKDIR" ] && WRKDIR_EXISTED='true' || WRKDIR_EXISTED='false'
154   rm -rf "$WRKDIR/cddir" "$WRKDIR/grub_tmp"
155   mkdir -p "$WRKDIR/cddir"
156 # }}}}
157
158 # execute grml2usb with all ISOs you'd like to install {{{
159   # remove all parameters
160   shift $(($OPTIND - 1))
161
162   $GRML2USB "${GRML2USB_OPTS[@]}" "$@" "$WRKDIR/cddir"
163 # }}}
164
165 # move syslinux to isolinux {{{
166   mv "$WRKDIR"/cddir/boot/syslinux "$WRKDIR"/cddir/boot/isolinux
167   echo "menu label ^Isolinux prompt" > "$WRKDIR"/cddir/boot/isolinux/promptname.cfg
168   echo "include hd.cfg" >> "$WRKDIR"/cddir/boot/isolinux/grmlmain.cfg
169 # }}}
170
171 # change to $WRKDIR {{{
172   # make sure $WRKDIR is an absolute path, otherwise accessing files
173   # in it will fail later in the code path if user provided a
174   # relative directory
175   WRKDIR=$(realpath $WRKDIR)
176   cd "$WRKDIR/cddir"
177 # }}}
178
179 # efi boot {{{
180   # default, independent of UEFI support
181   BOOT_ARGS="-no-emul-boot -boot-load-size 4 -boot-info-table -b boot/isolinux/isolinux.bin -c boot/isolinux/boot.cat"
182   UEFI_ENABLE=false
183
184   case "$MKISOFS" in
185     xorriso*)
186       echo "Using xorriso for ISO generation."
187       if ! dpkg --compare-versions $(dpkg-query -W -f='${Version}\n' xorriso 2>/dev/null) gt-nl 1.1.6-1 ; then
188         echo "Disabling (U)EFI boot support since xorriso version is not recent enough."
189       else
190         echo "xorriso with -eltorito-alt-boot support present"
191         UEFI_ENABLE=true
192
193         if ! [ -r "${WRKDIR}/cddir/boot/efi.img" ] ; then
194           echo "Warning: File /boot/efi.img not found, not extending boot arguments for (U)EFI boot."
195           UEFI_ENABLE=false
196         else
197           echo "/boot/efi.img found, extending boot arguments for (U)EFI boot."
198           BOOT_ARGS="$BOOT_ARGS -boot-info-table -eltorito-alt-boot -e boot/efi.img -no-emul-boot"
199         fi
200       fi
201       ;;
202     *)
203       echo "Using $MKISOFS for ISO generation (lacking UEFI option), disabling (U)EFI boot support."
204       ;;
205   esac
206 # }}}
207
208 # adjust ISO for small output if necessary {{{
209   if [ -n "$URI" ] ; then
210      bootloader_files=$(find . -name "*.cfg" -type f)
211      bootloader_files+=" "
212      bootloader_files+=$(find . -name "*.lst" -type f)
213      output_dir=$(dirname "$ISOFILE")
214      for squashfs in $(find . -name *.squashfs) ; do
215         media_path="$(dirname "$squashfs")"
216         filename="$(basename "$squashfs")"
217         target="$output_dir/$filename"
218         if [ -f "$target" ] && [ ! -n "$FORCE" ] ; then
219            echo >&2 "Warning: $target already exists, and -force not specified, not overwriting"
220         else
221            mv $squashfs $target
222            OUTPUT_FILES+=("$target")
223         fi
224         sed -i -e "s#^\(^.*$media_path.*\)\($URI\)\(.*$\)#\1$URI/$filename\3#g" $bootloader_files
225
226    done
227   fi
228 # }}}
229
230 # copy specified directory to cd {{{
231   if [ -n "$DIR" ] ; then
232      echo >&2 "Copying ${DIR} to generated ISO"
233      for param in GRML_NAME VERSION RELEASENAME DATE SHORT_NAME \
234          VERSION BOOTID RELEASE_INFO ; do
235        EXCLUDE_PARAM="$EXCLUDE_PARAM --exclude **%${param}%**"
236      done
237      rsync -a ${DIR}/ $EXCLUDE_PARAM .
238   fi
239
240   # adjust files from overlay directory
241   for GRML_VERSION_FILE in $(find . -name grml-version) ; do
242     GRML_NAME=$(awk '{print $1}' "$GRML_VERSION_FILE")
243     VERSION=$(awk '{print $2}' "$GRML_VERSION_FILE")
244     RELEASENAME=$(sed 's/.*- \(.*\).*\[.*/\1/' "$GRML_VERSION_FILE")
245     DATE=$(sed 's/.*\[\(.*\)].*/\1/' "$GRML_VERSION_FILE")
246     SHORT_NAME="$(echo $GRML_NAME | tr -d ',./;\- ')"
247     RELEASE_INFO="$GRML_NAME $VERSION - $RELEASENAME"
248     BOOTID=$(cat conf/bootid.txt)
249
250     for param in GRML_NAME VERSION RELEASENAME DATE SHORT_NAME \
251         RELEASE_INFO BOOTID  ; do
252       value="$(eval echo '$'"$param")"
253
254       # copy parameterized files from the overlay directory
255       for file in $(find ${DIR} -name "*%$param%*") ; do
256         file=${file##$DIR/}
257         target_dir="$(dirname ${file})"
258         mkdir -p "$target_dir" || true
259         cp -r ${DIR}/${file} ./${target_dir}/"$(basename ${file/\%${param}\%/$value})"
260       done
261
262       # adjust config files
263       for file in ./boot/isolinux/*.cfg ./boot/isolinux/*.msg \
264         ./boot/grub/*.cfg ; do
265         sed -i "s/%$param%/$value/g" ${file} 2>/dev/null || true
266       done
267     done
268   done
269 # }}}
270
271 # generate the CD/DVD ISO {{{
272   $MKISOFS -V 'grml-multiboot' -l -r -J -no-pad $BOOT_ARGS \
273     -o "$ISOFILE" .
274 # }}}
275
276 # pad the output ISO to multiples of 256 KiB for partition table support {{{
277   siz=$($getfilesize "$ISOFILE")
278   cyls=$(($siz / 512 / 32 / 16 + 1))  # C=$cyls H=16 S=32
279   ofs=$(($cyls * 16 * 32 * 512 - 1))  # padding offset (size - 1)
280   dd if=/dev/zero bs=1 count=1 seek=$ofs of="$ISOFILE" 2>/dev/null
281 # }}}
282
283 # make ISO dd-able {{{
284   if ! $UEFI_ENABLE ; then
285     echo "Skipping check for --uefi option in isohybrid since prerequisites are not fulfilled."
286   else
287     if ! isohybrid --help | grep -q -- --uefi ; then
288       echo "isohybrid version does NOT support --uefi option, disabling"
289     else
290       echo "isohybrid version supports --uefi option"
291       ISOHYBRID_OPTIONS=--uefi
292     fi
293
294     echo "Creating dd-able ISO using isohybrid"
295     isohybrid $ISOHYBRID_OPTIONS "$ISOFILE"
296   fi
297 # }}}
298
299 # cleanup {{{
300   cd "$ORIG_DIR"
301   sync
302   rm -rf "$WRKDIR/cddir" "$WRKDIR/grub_tmp"
303   [[ $WRKDIR_EXISTED = 'false' ]] && rmdir "$WRKDIR"
304   echo "Generated $ISOFILE"
305   if [ -n "$URI" ] ; then
306      echo "
307 Information:
308 ==============
309 You requested to generate a small ISO image. Your generated
310 ISO image $ISOFILE does _not_ contain the squashfs files from
311 the source ISO images.
312
313 You have to provide the extracted squashfs files under $URI.
314
315 ISO image: $ISOFILE
316 Squashfs files: ${OUTPUT_FILES[@]}
317 URI: $URI
318 "
319   fi
320 # }}}
321
322 ## EOF #########################################################################
323 # vim:foldmethod=marker ts=2 ft=sh ai expandtab tw=80 sw=2