mirror of
https://github.com/zerotier/edge.git
synced 2026-05-22 16:25:05 -07:00
398 lines
9.4 KiB
Bash
Executable File
398 lines
9.4 KiB
Bash
Executable File
#!/bin/sh
|
|
|
|
prog=${0##*/}
|
|
version=3.8.0-r0
|
|
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 -c -f "$destdir" to fix"
|
|
fi
|
|
done
|
|
|
|
#initramfs
|
|
initrds=$(awk 'tolower($1) == "initrd" {print $2}' \
|
|
"$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
|
|
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
|