|
#!/bin/bash |
|
|
|
VERBOSITY=0 |
|
TEMP_D="" |
|
MY_PATH=$(readlink -f "$0") |
|
MY_D=$(cd "${MY_PATH%/*}" && pwd) |
|
DEF_PRESEED="$MY_D/preseed" |
|
DEF_SIZE=4 |
|
DEF_MIRROR="http://archive.ubuntu.com/ubuntu" |
|
DEF_ARCH=$(uname -m) |
|
|
|
[ "$DEF_ARCH" = "x86_64" ] && DEF_ARCH=amd64 |
|
|
|
error() { echo "$@" 1>&2; } |
|
errorp() { printf "$@" 1>&2; } |
|
fail() { [ $# -eq 0 ] || error "$@"; exit 1; } |
|
failp() { [ $# -eq 0 ] || errorp "$@"; exit 1; } |
|
|
|
Usage() { |
|
cat <<EOF |
|
Usage: ${0##*/} [ options ] release [ arch [ size ] ] |
|
|
|
Do an install of Ubuntu for release. |
|
arch : the arch to use (amd64 i386). Default: ${DEF_ARCH} |
|
size : size of the image (in GigaBytes). Default: ${DEF_SIZE} |
|
|
|
options: |
|
-o | --output IMAGE_FILE write the image to IMAGE_FILE |
|
default: <release>-<arch>.img |
|
-s | --preseed PRESEED use the preseed at preseed |
|
default: ${DEF_PRESEED} |
|
-m | --mirror MIRROR mirror to download iso from MIRROR |
|
default: ${DEF_MIRROR} |
|
--iso ISO use ISO file rather than downloading |
|
--late-command-file F execute file F in target as late command |
|
EOF |
|
} |
|
|
|
bad_Usage() { Usage 1>&2; [ $# -eq 0 ] || error "$@"; exit 1; } |
|
cleanup() { |
|
[ -z "${TEMP_D}" -o ! -d "${TEMP_D}" ] || rm -Rf "${TEMP_D}" |
|
} |
|
|
|
dl() { |
|
local url="$1" out="${2}" opts="" |
|
[ "$url" = "$out" ] && return |
|
[ $VERBOSITY -lt 1 ] && opts="-q" |
|
local tfile="" tdir="" ret="" |
|
tdir=$(dirname "$out") |
|
[ -d "$tdir" ] || mkdir -p "$tdir" || return 1 |
|
tdir=$(cd "$tdir" &&pwd) |
|
tfile=$(mktemp $tdir/${out##*/}.XXXXXX) |
|
case "$url" in |
|
http://*|ftp://*) wget $opts "$url" -O "$tfile";; |
|
*) cat "${url}" > "$tfile";; |
|
esac |
|
ret=$? |
|
[ $ret -eq 0 ] && mv "$tfile" "$out" && return 0 |
|
rm -f "$tfile" |
|
return $ret |
|
} |
|
|
|
debug() { |
|
local level=${1}; shift; |
|
[ "${level}" -ge "${VERBOSITY}" ] && return |
|
error "${@}" |
|
} |
|
|
|
extract_file_from_iso() { |
|
# extract_file_from_iso(iso, file, out) |
|
# extract file from iso and store in output. |
|
# does not work for zero length files |
|
local iso="$1" file="$2" out="$3" ret=$? |
|
local tmp="$out.tmp.$$" |
|
isoinfo -RJ -x "$file" -i "$iso" > "$tmp" && [ -s "$tmp" ] |
|
ret=$? |
|
[ $ret -eq 0 ] && mv "$tmp" "$out" && return 0 |
|
rm -f "$tmp" |
|
return $ret |
|
} |
|
|
|
search_iso_for_file() { |
|
# search_iso_for_file(iso, output, files) |
|
# search iso for each file in files. store the first found in output |
|
local iso="$1" out="$2" i="" |
|
shift 2 |
|
for i in "$@"; do |
|
extract_file_from_iso "$iso" "$i" "$out" && return 0 |
|
done |
|
error "did not find $out on $iso. searched $*" |
|
return 1 |
|
} |
|
|
|
|
|
short_opts="hm:o:p:v" |
|
long_opts="help,iso:,late-command-file:,mirror:,output:,preseed:,verbose" |
|
getopt_out=$(getopt --name "${0##*/}" \ |
|
--options "${short_opts}" --long "${long_opts}" -- "$@") && |
|
eval set -- "${getopt_out}" || |
|
bad_Usage |
|
|
|
release="${DEF_RELEASE}" |
|
preseed="${DEF_PRESEED}" |
|
output="" |
|
mirror="${DEF_MIRROR}" |
|
arch="${DEF_ARCH}" |
|
iso="" |
|
late_command_file="" |
|
|
|
while [ $# -ne 0 ]; do |
|
cur=${1}; next=${2}; |
|
case "$cur" in |
|
-h|--help) Usage ; exit 0;; |
|
-i|--iso) iso=${2}; shift;; |
|
-m|--mirror) mirror=${2}; shift;; |
|
-o|--output) output=${2}; shift;; |
|
-p|--preseed) preseed=${2}; shift;; |
|
-v|--verbose) VERBOSITY=$((${VERBOSITY}+1));; |
|
--late-command-file) late_command_file="$2";; |
|
--) shift; break;; |
|
esac |
|
shift; |
|
done |
|
|
|
[ $# -ne 0 ] || bad_Usage "must provide arguments" |
|
[ $# -lt 1 -o $# -gt 3 ] && bad_Usage "must provide 1,2, or 3 args" |
|
release=$1 |
|
arch=${2:-${DEF_ARCH}} |
|
size=${3:-${DEF_SIZE}} |
|
size=${size%G} |
|
|
|
[ -n "$output" ] || output="${release}-${arch}.img" |
|
|
|
TEMP_D=$(mktemp -d "${TMPDIR:-/tmp}/${0##*/}.XXXXXX") || |
|
fail "failed to make tempdir" |
|
trap cleanup EXIT |
|
|
|
if [ -z "$iso" ]; then |
|
iso="${release}-${arch}-mini.iso" |
|
if [ -f "$iso" ]; then |
|
debug 1 "using existing iso ${iso}" |
|
else |
|
found=false |
|
for pocket in "$release-updates" "$release"; do |
|
url="${mirror}/dists/$pocket/main/installer-$arch/current/images/netboot/mini.iso" |
|
debug 1 "downloading ${url}" |
|
dl "${url}" "${iso}" && found=true && break |
|
done |
|
$found || fail "failed to download $iso from $url" |
|
fi |
|
fi |
|
|
|
dl "$preseed" preseed.cfg || |
|
fail "failed to download preseed: ${preseed}" |
|
late_command="" |
|
if [ -n "${late_command_file}" ]; then |
|
[ -f "$late_command_file" ] || fail "$late_command_file: not a file" |
|
sed -i '/^d-i.*preseed.late_command/d' "$preseed.cfg" || |
|
fail "failed to remove late command from $preseed.cfg" |
|
out=$("${MY_D}/script-to-latecommand" "$late_command_file") || |
|
fail "failed script-to-latecommand $late_command_file" |
|
printf "d-i\tpreseed/late_command\tstring\t%s" "$out" >> "$preseed.cfg" |
|
fi |
|
|
|
debug 1 "extracting kernel and ramdisk" |
|
kernel_paths="/linux /install/vmlinuz" |
|
initrd_paths="/initrd.gz /install/initrd.gz" |
|
search_iso_for_file "$iso" "linux.dist" $kernel_paths || fail |
|
search_iso_for_file "$iso" "initrd.gz.dist" $initrd_paths || fail |
|
|
|
debug 1 "repacking initramfs" |
|
zcat initrd.gz.dist > initrd && |
|
echo "./preseed.cfg" | cpio -o --format=newc --append -F initrd && |
|
gzip -9 initrd -c > initrd.gz && rm -f initrd || |
|
fail "failed to repack initrd" |
|
|
|
cp "linux.dist" "linux" |
|
|
|
debug 1 "creating a ${size}G disk in ${output}" |
|
qemu-img create -f qcow2 "${output}" "${size}G" || |
|
fail "failed to create image" |
|
|
|
kparms="nomodeset fb=false" |
|
set -x |
|
${KVM:-kvm} -kernel linux -initrd initrd.gz \ |
|
-append "priority=critical locale=en_US --- $kparms" \ |
|
-drive "file=${output},if=virtio,cache=unsafe" \ |
|
-cdrom "${iso}" -m 512 -boot d -no-reboot \ |
|
-vga std -curses |
|
|
|
# vi: ts=4 noexpandtab |