File: //sbin/setup-bootable
#!/bin/sh
prog=${0##*/}
version=3.18.1-r1
files_to_move="boot efi apks syslinux.cfg .alpine-release"
read_only_mounts=
umounts=
uninstalls=
destdir=
cleanup_tmpdata() {
if [ -d "$destdir" -a -d "$destdir/.new" ]; then
rm -rf "$destdir"/.new
fi
}
cleanup_mounts() {
local i=
cd /
sync
sleep 1
for i in $read_only_mounts; do
mount -o remount,ro "$i" || echo "Warning: Failed to remount as read-only. Is modloop mounted?"
done
read_only_mounts=""
if [ -n "$umounts" ]; then
umount $umounts
umounts=""
fi
}
cleanup_installs() {
if [ -n "$uninstalls" ]; then
apk del --quiet $uninstalls
uninstalls=""
fi
}
cleanup() {
cleanup_tmpdata
cleanup_mounts
cleanup_installs
}
trap cleanup EXIT
trap "exit 2" INT TERM QUIT
die() {
echo "$@" >&2
exit 1
}
# find device for mountpoint
find_dev() {
local mnt="${1%/}" # strip trailing /
awk "\$2 == \"$mnt\" {print \$1}" /proc/mounts
}
# check if given device is on usb bus
on_usb_bus() {
local dev="$1"
[ -e /sys/block/$dev ] || return 1
local sysdev="$(readlink -f /sys/block/$dev/device)"
test "${sysdev##*/usb[0-9]}" != "$sysdev"
}
vecho() {
[ -z "$verbose" ] && return 0
echo "$@"
}
# check if given dir is read-only
is_read_only() {
local tmpfile="$(mktemp -p "$1" 2>/dev/null)"
[ -z "$tmpfile" ] && return 0
rm -f "$tmpfile"
return 1
}
# find what disk this partition belongs to
find_disk_dev() {
local i= sysfsname="${1#/dev/}"
sysfsname=${sysfsname//\/!} # cciss/c0d0 -> cciss!c0d0
if [ -e /sys/block/$sysfsname ]; then
echo "/dev/${sysfsname//!/'/'}"
return 0
fi
for i in /sys/block/*/$sysfsname; do
[ -e "$i" ] || continue
echo "$i" | cut -d/ -f4 | sed -e 's:!:/:g' -e 's:^:/dev/:'
return 0
done
return 1
}
find_syslinux_cfg() {
# find where new syslinux.cfg is
for i in boot/syslinux/syslinux.cfg syslinux.cfg; do
if [ -e "$1"/$i ]; then
syslinux_cfg=$i
vecho "Found $syslinux_cfg"
break
fi
done
}
fix_syslinux_kernel() {
echo "Fixing $syslinux_cfg: kernel $1 -> $2"
sed -i -e "/^\s*[Kk][Ee][Rr][Nn][Ee][Ll]\s/s|$1|$2|" \
"$destdir/$syslinux_cfg"
}
fix_syslinux_initrd() {
echo "Fixing $syslinux_cfg: initrd $1 -> $2"
sed -i -e "/^\s*[Ii][Nn][Ii][Tt][Rr][Dd]\s/s|$1|$2|" \
-e "/^\s*[Aa][Pp][Pp][Ee][Nn][Dd]\s/s|initrd=$1|initrd=$2|" \
"$destdir/$syslinux_cfg"
}
check_syslinux() {
if [ -z "$syslinux_cfg" ]; then
find_syslinux_cfg "$destdir"
fi
if [ -z "$syslinux_cfg" ]; then
die "Could not find any syslinux.cfg. Aborting"
fi
# kernels
for i in $(awk 'tolower($1) == "kernel" {print $2}' "$destdir"/$syslinux_cfg); do
k="${destdir%/}/${i#/}"
f=${k##*/}
if [ -e "$k" ] && [ "${f#vmlinuz}" != "$f" ]; then
continue
fi
if [ -e "${k%/*}"/vmlinuz-$f ] && [ -n "$fix_syslinux_cfg" ]; then
fix_syslinux_kernel "$i" "${i%/*}"/vmlinuz-$f
elif ! [ -e "$k" ]; then
echo "Warning: $syslinux_cfg: kernel $k was not found"
echo " Run $0 -f -c "$destdir" to fix"
fi
done
#initramfs
initrds=$(awk 'tolower($1) == "initrd" {gsub(",", " "); for (i=2; i<=NF; i++) print $i}' \
"$destdir"/$syslinux_cfg)
for i in $(awk 'tolower($1) == "append" {print $0}' \
"$destdir"/$syslinux_cfg); do
case $i in
initrd=*) initrds=${i#initrd=};;
esac
done
for i in $initrds; do
if [ -e "$destdir"/$i ]; then
continue
fi
fname=${i##*/}
flavor=${fname%.gz}
new=${i%/*}/initramfs-$flavor
if [ -e "$destdir"/$new ] && [ -n "$fix_syslinux_cfg" ]; then
fix_syslinux_initrd "$i" "$new"
else
echo "Warning: initrd $i was not found. System will likely not boot"
echo " Run $0 -f -c "$destdir" to fix"
fi
done
}
version_check() {
local new_dir="$1" old_dir="$2"
# check if its same version
local to_version="$(cat "$new_dir"/.alpine-release)"
if [ -n "$upgrade" ] && [ -e "$old_dir"/.alpine-release ]; then
local from_version="$(cat "$old_dir"/.alpine-release)"
if [ -z "$force" ] && [ -n "$to_version" ] && [ "$from_version" = "$to_version" ]; then
die "Source and target seems to have same version ($from_version). Aborting."
fi
echo "Upgrading $dest from $from_version to $to_version"
else
echo "Installing $dest to $to_version"
fi
}
usage() {
cat <<-__EOF__
$prog $version
usage: $prog [-fhUusv] SOURCE [DEST]
$prog -c DIR
Copy the contents of SOURCE to DEST and make DEST bootable.
SOURCE can be a directory or a ISO image. DEST can be a mounted directory
or a device. If DEST is ommitted /media/usb will be used.
Options:
-f Force overwrite existing files. Will overwrite syslinux.cfg if upgrade.
-h Show this help.
-k fix kernel and initrd name in syslinux.cfg if needed.
-U Replace current alpine_dev in syslinux.cfg with UUID if UUID found.
-u Upgrade mode. Keep existing syslinux.cfg and don't run syslinux.
-s Force run syslinux, even if upgrade mode.
-v Verbose mode. Display whats going on.
-c Check syslinux.cfg in destination DIR. Use with -f to fix.
__EOF__
exit 1
}
while getopts "c:fhkUusv" opt; do
case "$opt" in
c) check_syslinux="$OPTARG";;
f) force=1; fix_syslinux_cfg=1;;
h) usage;;
k) fix_syslinux_cfg=1;;
U) replace_alpine_dev=1;;
u) upgrade=1;;
s) syslinux=1;;
v) verbose=1;;
esac
done
shift $(($OPTIND - 1))
src=${1}
dest=${2:-/media/usb}
if [ -n "$check_syslinux" ]; then
destdir="$check_syslinux"
check_syslinux
exit 0
fi
[ -z "$src" ] && usage
# find target device
if [ -d "$dest" ]; then
dest=${dest%/} # strip trailing /
if ! awk '{print $2}' /proc/mounts | grep -q "^$dest\$"; then
mount "$dest" || die "Failed to mount $dest"
umounts="$umounts $dest"
elif [ -n "$syslinux" ]; then
die "Cannot run syslinux on mounted device"
else
nosyslinux=1
fi
destdir="$dest"
dest=$(find_dev "$destdir")
elif [ -b "$dest" ]; then
destdir="/media/${dest##*/}"
mkdir -p "$destdir"
mount "$dest" "$destdir" || die "Failed to mount $dest on $destdir"
umounts="$umounts $destdir"
fi
# remount as rw if needed
if is_read_only "$destdir"; then
vecho "Remounting $destdir as read/write"
mount -o remount,rw "$dest" || die "Failed to remount $destdir as rw"
read_only_mounts="$read_only_mounts $destdir"
fi
# fish out label, uuid and type
eval $(blkid $dest | cut -d: -f2-)
vecho "Using $dest as target (mounted on $destdir)"
# find parent device (i.e sda)
dev="$dest"
while [ -L "$dev" ]; do
dev=$(readlink -f $dev)
done
parent_dev=$(find_disk_dev $dev)
# check if this files exist and not in upgrade mode
if [ -z "$upgrade" ] && [ -z "$force" ]; then
for i in $files_to_move; do
[ -e "$destdir"/$i ] && die "$destdir/$i already exists. Use -u to upgrade."
done
fi
# remove partial upgrades if any
rm -rf "$destdir"/.new "$destdir"/.old
mkdir -p "$destdir"/.new || die "Failed to create $destdir/.new"
# copy data from source to .new
if [ -f "$src"/.alpine-release ]; then
srcdir="$(echo $src | sed -r 's,/$,,')"
version_check "$srcdir" "$destdir"
for i in $files_to_move; do
if [ -e "$srcdir"/$i ]; then
vecho "Copying $srcdir/$i to $destdir/.new/"
cp -dR "$srcdir"/$i "$destdir"/.new/
fi
done
else
vecho "Extracting $src to $destdir/.new/"
case "$src" in
https://*|http://*|ftp://*)
${WGET:-wget} -O - "$src" | (cd "$destdir"/.new; exec ${UNISO:-uniso}) \
|| die "Failed to download or extract $src"
echo ""
;;
*)
(cd "$destdir"/.new; exec ${UNISO:-uniso}) < "$src" \
|| die "Failed to download or extract $src"
;;
esac
version_check "$destdir/.new" "$destdir"
fi
# find where new syslinux.cfg is
find_syslinux_cfg "$destdir"/.new
# abort early in case unexpected trouble
if [ -z "$syslinux_cfg" ]; then
die "Could not find any syslinux.cfg on new iso?"
fi
# make sure files are really there before we replace existing
vecho "Flushing cache..."
sync
vecho "Replacing existing files..."
mkdir -p "$destdir"/.old || die "Failed to create $destdir/.old"
# move current files to .old
for i in $files_to_move; do
if [ -e "$destdir"/$i ]; then
mv "$destdir"/$i "$destdir"/.old/ || die "Failed to move $destdir/$i to $destdir/.old/"
fi
done
# keep any existing syslinux.cfg
if [ -e "$destdir"/.old/$syslinux_cfg ]; then
mv "$destdir"/.old/$syslinux_cfg "$destdir"/.new/$syslinux_cfg
elif [ -e "$destdir"/.old/syslinux.cfg ] \
&& [ -e "$destdir"/.new/boot/syslinux/syslinux.cfg ]; then
echo "Warning: moving syslinux.cfg to boot/syslinux/syslinux.cfg" >&2
mv "$destdir"/.old/syslinux.cfg "$destdir"/.new/boot/syslinux
if [ -z "$syslinux" ]; then
echo " You might need run: syslinux $dest" >&2
fi
fi
# move .new to current
for i in $files_to_move; do
if [ -e "$destdir"/.new/$i ]; then
mv "$destdir"/.new/$i "$destdir"/ \
|| die "Failed to move $destdir/.new/ to $destdir"
fi
done
if [ -n "$replace_alpine_dev" -o -z "$upgrade" ] && [ -n "$UUID" ]; then
sed -E -i -e "s/alpine_dev=[^ \t:]+/alpine_dev=UUID=$UUID/" \
"$destdir"/$syslinux_cfg
fi
# verify syslinux.cfg
check_syslinux
# cleanup
[ -z "$keep_old" ] && rm -rf "$destdir"/.old "$destdir"/.new
# If we only copy then we are done.
if [ -n "$upgrade" ] && [ -z "$syslinux" ]; then
exit 0
fi
# prevent running syslinux on mounted device
if [ -n "$nosyslinux" ]; then
echo "Warning: Can not run syslinux on a mounted device"
echo " You might need run syslinux manually and install MBR manually"
exit 0
fi
echo "Making $dest bootable..."
if ! [ -x "$(which syslinux)" ]; then
apk add --quiet syslinux || die "Failed to install syslinux"
uninstalls="syslinux"
fi
# we need to unmount the device before we can run syslinux
cleanup_mounts
fsync $dest
syslinux $dest
if [ -b $parent_dev ]; then
dd if=/usr/share/syslinux/mbr.bin of=$parent_dev status=none
else
echo "Warning: Could not find the parent device for $dest"
fi