setup-bootable.in 9.44 KB
Newer Older
1 2
#!/bin/sh

3
prog=${0##*/}
4
version=@VERSION@
5
files_to_move="boot efi apks syslinux.cfg .alpine-release"
6 7 8
read_only_mounts=
umounts=
uninstalls=
9 10 11 12 13 14 15
destdir=

cleanup_tmpdata() {
	if [ -d "$destdir" -a -d "$destdir/.new" ]; then
		rm -rf "$destdir"/.new
	fi
}
16

17
cleanup_mounts() {
18
	local i=
19
	cd /
20 21
	sync
	sleep 1
22
	for i in $read_only_mounts; do
23
		mount -o remount,ro "$i" || echo "Warning: Failed to remount as read-only. Is modloop mounted?"
24
	done
25
	read_only_mounts=""
26 27
	if [ -n "$umounts" ]; then
		umount $umounts
28
		umounts=""
29 30 31
	fi
}

32 33
cleanup_installs() {
	if [ -n "$uninstalls" ]; then
34 35
		apk del --quiet $uninstalls
		uninstalls=""
36 37 38
	fi
}

39
cleanup() {
40
	cleanup_tmpdata
41 42
	cleanup_mounts
	cleanup_installs
43 44 45 46 47 48 49
}

trap cleanup EXIT
trap "exit 2" INT TERM QUIT

die() {
	echo "$@" >&2
50 51 52
	exit 1
}

53

54 55 56 57 58 59 60 61 62 63 64 65 66 67
# 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"
}

68 69 70 71 72
vecho() {
	[ -z "$verbose" ] && return 0
	echo "$@"
}

73 74 75 76 77 78 79 80
# 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
}

81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96
# 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
}

97 98 99 100 101 102 103 104 105 106 107 108 109 110
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|" \
111
 			"$destdir/$syslinux_cfg"
112 113 114 115 116 117
}

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|" \
118
			"$destdir/$syslinux_cfg"
119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172
}

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
}

173 174 175 176 177 178 179 180 181 182 183 184 185 186 187
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
}

188
usage() {
189 190 191 192
	cat <<-__EOF__
		$prog $version
		usage: $prog [-fhUusv] SOURCE [DEST]
		       $prog -c DIR
193

194
		Copy the contents of SOURCE to DEST and make DEST bootable.
195

196 197
		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.
198

199 200 201 202 203 204 205 206
		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.
207

208
		 -c  Check syslinux.cfg in destination DIR. Use with -f to fix.
209

210
	__EOF__
211 212
	exit 1
}
213

214
while getopts "c:fhkUusv" opt; do
215
	case "$opt" in
216 217
	c) check_syslinux="$OPTARG";;
	f) force=1; fix_syslinux_cfg=1;;
218
	h) usage;;
219
	k) fix_syslinux_cfg=1;;
220
	U) replace_alpine_dev=1;;
221 222 223
	u) upgrade=1;;
	s) syslinux=1;;
	v) verbose=1;;
224 225
	esac
done
226

227
shift $(($OPTIND - 1))
228

229
src=${1}
230 231
dest=${2:-/media/usb}

232 233 234 235 236
if [ -n "$check_syslinux" ]; then
	destdir="$check_syslinux"
	check_syslinux
	exit 0
fi
237

238 239
[ -z "$src" ] && usage

240 241
# find target device
if [ -d "$dest" ]; then
242
	dest=${dest%/} # strip trailing /
243 244 245
	if ! awk '{print $2}' /proc/mounts | grep -q "^$dest\$"; then
		mount "$dest" || die "Failed to mount $dest"
		umounts="$umounts $dest"
246 247 248 249
	elif [ -n "$syslinux" ]; then
		die "Cannot run syslinux on mounted device"
	else
		nosyslinux=1
250 251
	fi
	destdir="$dest"
252
	dest=$(find_dev "$destdir")
253 254 255 256
elif [ -b "$dest" ]; then
	destdir="/media/${dest##*/}"
	mkdir -p "$destdir"
	mount "$dest" "$destdir" || die "Failed to mount $dest on $destdir"
257
	umounts="$umounts $destdir"
258
fi
259

260 261 262 263 264 265 266
# 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

267 268 269
# fish out label, uuid and type
eval $(blkid $dest | cut -d: -f2-)

270
vecho "Using $dest as target (mounted on $destdir)"
271 272 273 274 275 276

# find parent device (i.e sda)
dev="$dest"
while [ -L "$dev" ]; do
	dev=$(readlink -f $dev)
done
277
parent_dev=$(find_disk_dev $dev)
278

279 280
# check if this files exist and not in upgrade mode
if [ -z "$upgrade" ] && [ -z "$force" ]; then
281
	for i in $files_to_move; do
282
		[ -e "$destdir"/$i ] && die "$destdir/$i already exists. Use -u to upgrade."
283 284 285
	done
fi

286
# remove partial upgrades if any
287
rm -rf "$destdir"/.new "$destdir"/.old
288
mkdir -p "$destdir"/.new || die "Failed to create $destdir/.new"
289

290 291 292 293
# copy data from source to .new
if [ -f "$src"/.alpine-release ]; then
	srcdir="$(echo $src | sed -r 's,/$,,')"
	version_check "$srcdir" "$destdir"
294
	for i in $files_to_move; do
295 296
		if [ -e "$srcdir"/$i ]; then
			vecho "Copying $srcdir/$i to $destdir/.new/"
297
			cp -dR "$srcdir"/$i "$destdir"/.new/
298
		fi
299
	done
300 301 302 303 304 305 306 307 308 309 310 311 312 313
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"
314
fi
315

316
# find where new syslinux.cfg is
317 318
find_syslinux_cfg "$destdir"/.new

319 320 321 322 323
# abort early in case unexpected trouble
if [ -z "$syslinux_cfg" ]; then
	die "Could not find any syslinux.cfg on new iso?"
fi

324
# make sure files are really there before we replace existing
325
vecho "Flushing cache..."
326 327
sync

328
vecho "Replacing existing files..."
329 330 331
mkdir -p "$destdir"/.old || die "Failed to create $destdir/.old"

# move current files to .old
332
for i in $files_to_move; do
333 334 335 336 337
	if [ -e "$destdir"/$i ]; then
		mv "$destdir"/$i "$destdir"/.old/ || die "Failed to move $destdir/$i to $destdir/.old/"
	fi
done

338 339 340 341 342 343 344 345 346 347 348 349
# 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

350
# move .new to current
351
for i in $files_to_move; do
352 353 354 355
	if [ -e "$destdir"/.new/$i ]; then
		mv "$destdir"/.new/$i "$destdir"/ \
			|| die "Failed to move $destdir/.new/ to $destdir"
	fi
356 357
done

358
if [ -n "$replace_alpine_dev" -o -z "$upgrade" ] && [ -n "$UUID" ]; then
359
	sed -E -i -e "s/alpine_dev=[^ \t:]+/alpine_dev=UUID=$UUID/" \
360
		"$destdir"/$syslinux_cfg
361 362
fi

363 364 365
# verify syslinux.cfg
check_syslinux

366 367 368 369
# cleanup
[ -z "$keep_old" ] && rm -rf "$destdir"/.old "$destdir"/.new

# If we only copy then we are done.
370
if [ -n "$upgrade" ] && [ -z "$syslinux" ]; then
371 372 373 374 375 376 377
	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"
378 379
	exit 0
fi
380

381 382 383
echo "Making $dest bootable..."

if ! [ -x "$(which syslinux)" ]; then
384
	apk add --quiet syslinux || die "Failed to install syslinux"
385 386
	uninstalls="syslinux"
fi
387

388 389 390
# we need to unmount the device before we can run syslinux
cleanup_mounts
fsync $dest
391
syslinux $dest
392

393
if [ -b $parent_dev ]; then
394
	dd if=/usr/share/syslinux/mbr.bin of=$parent_dev status=none
395
else
396
	echo "Warning: Could not find the parent device for $dest"
397
fi