setup-disk.in 12.8 KB
Newer Older
Natanael Copa's avatar
Natanael Copa committed
1 2 3 4 5
#!/bin/sh

PREFIX=
. "$PREFIX/lib/libalpine.sh"

6
MBR=${MBR:-"/usr/share/syslinux/mbr.bin"}
Natanael Copa's avatar
Natanael Copa committed
7 8 9 10 11 12 13 14 15 16 17

in_list() {
	local i="$1"
	shift
	while [ $# -gt 0 ]; do
		[ "$i" = "$1" ] && return 0
		shift
	done
	return 1
}

18 19 20 21 22 23 24 25
# wrapper to only show given device
_blkid() {
	blkid | grep "^$1:"
}

# if given device have an UUID display it, otherwise return the device
uuid_or_device() {
	local i=
26 27 28
	case "$1" in
		/dev/md*) echo "$1" && return 0;;
	esac
29 30 31 32 33 34 35 36 37 38 39 40 41
	for i in $(_blkid "$1"); do
		case "$i" in
			UUID=*) eval $i;;
		esac
	done
	if [ -n "$UUID" ]; then
		echo "UUID=$UUID"
	else
		echo "$1"
	fi
}

# generate an fstab from a given mountpoint. Convert to UUID if possible
42
enumerate_fstab() {
43
	local mnt="$1"
44
	local fs_spec= fs_file= fs_vfstype= fs_mntops= fs_freq= fs_passno=
45
	[ -z "$mnt" ] && return
46 47
	local escaped_mnt=$(echo $mnt | sed 's:/:\\/:g')
	awk "\$2 ~ /^$escaped_mnt/ {print \$0}" /proc/mounts | \
48 49 50 51
		sed "s:$mnt:/:g; s: :\t:g" | sed 's:/\+:/:g' | \
		while read fs_spec fs_file fs_vfstype fs_mntops fs_freq fs_passno; do
			echo -e "$(uuid_or_device $fs_spec)\t${fs_file}\t${fs_vfstype}\t${fs_mntops} ${fs_freq} ${fs_passno}"
		done
52 53
}

54 55 56 57 58
is_vmware() {
	grep -q VMware /proc/scsi/scsi 2>/dev/null \
		|| grep -q VMware /proc/ide/hd*/model 2>/dev/null
}

59 60 61 62
is_xen() {
	[ -d /proc/xen ]
}

63 64 65 66 67
# return true (0) if given device is lvm
is_lvm() {
	lvs "$1" >/dev/null 2>&1
}

68 69
# Find the disk device from given partition
disk_from_part() {
70 71
	# we need convert cciss/c0d0* cciss!c0d0*...
	local i= part=$(echo ${1#/dev/} | sed 's:/:!:g')
72 73
	for i in /sys/block/*/$part; do
		i=${i%/*}
74
		# ...and back from cciss!c0d0 to cciss/c0d0
75 76 77 78
		if [ -b "/dev/${i##*/}" ]; then
			echo "/dev/${i##*/}" | sed 's:!:/:g'
			return 0
		fi
79 80 81 82
	done
	return 1
}

83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117
unpack_apkovl() {
	local ovl="$1"
	local dest="$2"
	local suffix=${ovl##*.}
	local i
	ovlfiles=/tmp/ovlfiles
	if [ "$suffix" = "gz" ]; then
		if ! tar -C "$dest" --numeric-owner -zxvf "$ovl" > $ovlfiles; then
			echo -n "Continue anyway? [Y/n]: "
			read i
			case "$i" in
				n*|N*) return 1;;
			esac
		fi
		return 0 
	fi

	apk add -q openssl

	if ! openssl list-cipher-commands | grep "^$suffix$" > /dev/null; then
		errstr="Cipher $suffix is not supported"
		return 1
	fi
	local count=0
	# beep
	echo -e "\007"
	while [ $count -lt 3 ]; do
		openssl enc -d -$suffix -in "$ovl" | tar --numeric-owner \
			-C "$dest" -zxv >$ovlfiles 2>/dev/null && return 0
		count=$(( $count + 1 ))
	done
	ovlfiles=
	return 1
}

118 119 120 121 122 123 124 125
# find filesystem of given mounted dir
find_mount_fs() {
	local mount_point="$1"
	awk "\$2 == \"$mount_point\" {print \$3}" /proc/mounts | tail -n 1
}

# find device for given mounted dir
find_mount_dev() {
126
	local mnt="$1"
127 128 129 130
	awk "\$2 == \"$mnt\" { print \$1 }" /proc/mounts | tail -n 1
}

supported_boot_fs() {
131
	local supported="ext2 ext3 ext4 btrfs"
132 133 134 135 136 137 138 139 140 141
	local fs=
	for fs in $supported; do
		[ "$fs" = "$1" ] && return 0
	done
	echo "$1 is not supported. Only supported are: $supported" >&2
	return 1
}

install_mounted_root() {
	local mnt="$1" mnt_boot="$1" boot_fs= root_fs=
142
	local initfs_features="ata base ide scsi usb virtio"
143

144
	rootdev=$(find_mount_dev "$mnt")
145 146 147 148
	if [ -z "$rootdev" ]; then
		echo "$mnt does not seem to be a mount point" >&2
		return 1
	fi
149
	root_fs=$(find_mount_fs "$mnt")
150 151 152 153 154 155
	initfs_features="$initfs_features $root_fs"

	if is_lvm "$rootdev"; then
		initfs_features="$initfs_features lvm"
	fi

156

157 158 159 160 161 162 163 164 165 166
	bootdev=$(find_mount_dev "$mnt"/boot)
	if [ -z "$bootdev" ]; then
		bootdev=$rootdev
	else
		mnt_boot="$mnt"/boot
		bootdev=$(find_mount_dev "$mnt_boot")
	fi
	boot_fs=$(find_mount_fs "$mnt_boot")
	supported_boot_fs "$boot_fs" || return 1

167
	mbrdisk=$(disk_from_part $bootdev)
168 169
	if [ -e "/sys/block/${rootdev#/dev/}/md" ]; then
		local md=${rootdev#/dev/}
170
		initfs_features="$initfs_features raid"
171 172 173 174
		raidmod=$(cat /sys/block/$md/md/level)
		raidmod=",$raidmod"
		raidopt="-r"
		# get a list of slaves
175
		mbrdisk=
176 177 178
		for i in /sys/block/$md/slaves/*; do
			j=${i##*/}
			i=${j%[0-9]*}
179 180 181
			if [ -b "/dev/$i" ]; then
				mbrdisk="$mbrdisk /dev/${i}"
			fi
182
		done
183
	fi
184 185 186 187 188
	if [ -n "$VERBOSE" ]; then
		echo "Root device:     $rootdev"
		echo "Root filesystem: $root_fs"
		echo "Boot device:     $bootdev"
		echo "Boot filesystem: $boot_fs"
189
		echo "MBR disk(s):    $mbrdisk"
190
	fi
191

192
	if [ -z "$APKOVL" ]; then
193 194
		ovlfiles=/tmp/ovlfiles
		lbu package - | tar -C "$mnt" -zxv > "$ovlfiles"
195 196
	else
		echo "Restoring backup from $APKOVL to $rootdev..."
197
		unpack_apkovl "$APKOVL" "$mnt" || return 1
198
	fi
199

200 201 202 203
	# generate mkinitfs.conf
	mkdir -p "$mnt"/etc/mkinitfs
	echo "features=\"$initfs_features\"" > "$mnt"/etc/mkinitfs/mkinitfs.conf

204 205 206 207 208 209 210 211 212 213 214
	# generate the fstab
	if [ -f "$mnt"/etc/fstab ]; then
		mv "$mnt"/etc/fstab "$mnt"/etc/fstab.old
	fi
	enumerate_fstab "$mnt" >> "$mnt"/etc/fstab
	cat >>"$mnt"/etc/fstab <<EOF
/dev/cdrom	/media/cdrom	iso9660	noauto,ro 0 0
/dev/fd0	/media/floppy	vfat	noauto	0 0
/dev/usbdisk	/media/usb	vfat	noauto	0 0
EOF

215 216
	# remove the installed db in case its there so we force re-install
	rm -f "$mnt"/var/lib/apk/installed
217
	echon "Installing system on $rootdev: "
218 219 220 221
	# apk reads config from target root so we need to copy the config
	mkdir -p "$mnt"/etc/apk/keys/
	cp /etc/apk/keys/* "$mnt"/etc/apk/keys/

222

223 224
	local apkflags="--quiet --progress --update-cache --clean-protected"
	local pkgs=$(cat "$mnt"/var/lib/apk/world)
225
	pkgs="$pkgs acct linux-$KERNEL_FLAVOR alpine-base"
226 227 228 229 230
	local repos=$(sed -e 's/\#.*//' /etc/apk/repositories)
	local repoflags=
	for i in $repos; do
		repoflags="$repoflags --repository $i"
	done
231 232
	
	apk add --root "$mnt" $apkflags --overlay-from-stdin \
233
		$repoflags $pkgs <$ovlfiles>/dev/null || return 1
234
	echo ""
235

236
	# make things bootable
237 238 239 240 241 242
	if is_vmware; then
		pax_nouderef="pax_nouderef "
	else
		pax_nouderef=
	fi

243 244 245 246 247 248 249
	if is_xen; then
		# create a menu.lst
		mkdir -p "$mnt"/boot/grub
		cat >"$mnt"/boot/grub/menu.lst <<EOF
default 0
title $KERNEL_FLAVOR
root (hd0,0)
250
kernel /boot/$KERNEL_FLAVOR root=$(uuid_or_device $rootdev) modules=${root_fs}${raidmod} quiet xen BOOT_IMAGE=/boot/$KERNEL_FLAVOR
251 252 253 254 255
initrd=/boot/initramfs-$KERNEL_FLAVOR
EOF
	else
		# create an extlinux.conf
		cat >"$mnt"/boot/extlinux.conf <<EOF
256 257
timeout 20
prompt 1
258 259 260
default $KERNEL_FLAVOR
label $KERNEL_FLAVOR
	kernel /boot/vmlinuz-$KERNEL_FLAVOR
261
	append initrd=/boot/initramfs-$KERNEL_FLAVOR root=$(uuid_or_device $rootdev) modules=sd-mod,usb-storage,${root_fs}${raidmod} ${pax_nouderef}quiet
262
EOF
263 264
	fi

265 266
	# install extlinux
	apk add -q syslinux
267
	is_xen || extlinux -i $raidopt "$mnt"/boot/
268 269 270 271
}

unmount_partitions() {
	local mnt="$1"
272 273 274

	# unmount the partitions
	umount $(awk '{print $2}' /proc/mounts | grep ^"$mnt" | sort -r)
275
}
276

277
fix_mbr_all_disks() {
278
	# fix mbr for all disk devices
279
	for i in $mbrdisk; do
280 281 282 283
		local errmsg
		echo "Writing MBR to $i"
		errmsg=$(dd if="$MBR" of=$i 2>&1) \
			|| echo "$errmsg"
284
	done
285 286
}

287 288 289 290 291 292 293 294 295
# figure out decent default swap size in mega bytes
find_swap_size() {
	local memtotal_kb=$(awk '$1 == "MemTotal:" {print $2}' /proc/meminfo)
	# use 2 * avaiable ram
	echo $(( $memtotal_kb * 2 / 1024 ))
}

has_mounted_part() {
	local p
Natanael Copa's avatar
Natanael Copa committed
296
	# parse /proc/mounts for mounted devices
297 298
	for p in $(awk '$1 ~ /^\/dev\// {gsub("/dev/", "", $1); print $1}' \
			/proc/mounts); do
299
		[ "$p" = "$1" ] && return 0
Natanael Copa's avatar
Natanael Copa committed
300
		[ -e /sys/block/$1/$p ] && return 0
301 302 303 304
	done
	return 1
}

Natanael Copa's avatar
Natanael Copa committed
305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324
has_holders() {
	local i
	# check if device is used by any md devices
	for i in $1/holders/* $1/*/holders/*; do
		[ -e "$i" ] && return 0
	done
	return 1
}

is_available_disk() {
	local dev=$1
	local b=$(echo $p | sed 's:/:!:g')

	# check if its a "root" block device and not a partition
	[ -e /sys/block/$b ] || return 1
	
	# check so it does not have mounted partitions
	has_mounted_part $dev && return 1

	# check so its not part of an md setup
325 326 327 328
	if has_holders /sys/block/$b; then
		[ -n "$USE_RAID" ] && echo "Warning: $dev is part of a running raid" >&2
		return 1
	fi
Natanael Copa's avatar
Natanael Copa committed
329 330 331 332 333 334 335

	# check so its not an md device
	[ -e /sys/block/$b/md ] && return 1

	return 0
}

336
find_disks() {
Natanael Copa's avatar
Natanael Copa committed
337
	local p=
338
	for p in $(awk '$1 ~ /[0-9]+/ {print $4}' /proc/partitions); do
Natanael Copa's avatar
Natanael Copa committed
339
		is_available_disk $p && echo -n " $p"
340 341 342
	done
}

Natanael Copa's avatar
Natanael Copa committed
343
useall() {
344
	local rootdisk_dev="$1"
Natanael Copa's avatar
Natanael Copa committed
345
	local i size
346 347 348 349 350 351 352 353 354 355 356 357 358
	local boot_size=100 boot_part_type="83" 
	local swap_size=$(find_swap_size) swap_part_type="82"
	local root_part_type="83"
	local raidpkg= partitions=
	local minimum_root_size=$(($boot_size * 2));

	if [ -n "$USE_RAID" ]; then
		boot_part_type="fd"
		swap_part_type="fd"
		root_part_type="fd"
		raidpkg="mdadm"
	fi

359 360
	dmesg -n1
	apk_add -q sfdisk e2fsprogs $raidpkg || return 1
361 362 363 364 365 366 367 368 369 370 371 372
	local root_size=$(( $(sfdisk -s $rootdisk_dev) / 1024 - $swap_size - $boot_size))
	if [ "$root_size" -lt "$minimum_root_size" ]; then
		echo "The $rootdisk_dev is too small. At least $(( $boot_size + $swap_size + $minimum_root_size)) is needed." >&2
		return 1
	fi

	echo ""
	echo "Creating the following partitions on $rootdisk_dev:"
	echo " /boot	${boot_size}MB"
	echo " swap	${swap_size}MB"
	echo " /	${root_size}MB"
	echo ""
373 374 375
	if [ -n "$APKOVL" ]; then
		echo "System from $APKOVL will be restored"
	fi
376 377 378 379 380 381
	echo -n "WARNING: All contents of $rootdisk_dev will be erased. Continue? [y/N]: "
	read i
	case "$i" in
		y*|Y*);;
		*) return 1;;
	esac
382 383
	
	echo "Initializing partitions..."
384 385 386 387 388 389 390
	if [ -n "$USE_RAID" ]; then
		local rd
		for rd in md0 md1 md2; do
			[ -b /dev/$rd ] && mdadm --stop /dev/$rd
		done
	fi

391 392 393 394 395 396
	# new disks does not have an DOS signature in sector 0
	# this makes sfdisk complain. We can workaround this by letting
	# fdisk create that DOS signature, by just do a "w", a write.
	# http://bugs.alpinelinux.org/issues/show/145
	echo "w" | fdisk $rootdisk_dev >/dev/null

397 398 399 400 401 402
	# create new partitions
	(cat <<EOF
0,$boot_size,$boot_part_type,*
,$swap_size,$swap_part_type
,,$root_part_type
EOF
403
	) | sfdisk -q -L -uM $rootdisk_dev >>/tmp/sfdisk.out || return 1
404 405

	# create device nodes if not exist
Natanael Copa's avatar
Natanael Copa committed
406 407
	mdev -s

408 409 410 411
	if [ -n "$USE_RAID" ]; then
		local p= rd=
		for p in $(sfdisk -l $rootdisk_dev 2>/dev/null \
				| awk '/Linux raid/ {print $1}'); do
412
			local opt="--metadata=0.90"
413 414
			case "$p" in
				*1) rd=/dev/md0; boot_dev=/dev/md0;;
415 416
				*2) rd=/dev/md1; swap_dev=/dev/md1
				    opt= ;;
417 418 419
				*3) rd=/dev/md2; root_dev=/dev/md2;;
			esac
			mdadm --create $rd --level=1 --raid-devices=2 \
420
				$opt --quiet --run $p missing
421 422 423 424 425 426 427 428 429 430 431 432 433 434 435 436 437 438 439 440 441 442 443 444 445
		done
	else
		local p=
		for p in $(sfdisk -l $rootdisk_dev 2>/dev/null \
				| awk '$1 ~ /^\/dev/ {print $1}'); do
			case "$p" in
				*1) boot_dev=$p;;
				*2) swap_dev=$p;;
				*3) root_dev=$p;;
			esac
		done
	fi
	mkfs.ext3 -q $boot_dev >/dev/null \
		&& mkswap $swap_dev >/dev/null \
		&& mkfs.ext3 -q >/dev/null $root_dev \
		|| return 1

	mkdir -p /mnt
	mount -t ext3 $root_dev /mnt || return 1
	mkdir -p /mnt/boot
	mount -t ext3 $boot_dev /mnt/boot || return 1
	if [ -n "$USE_RAID" ]; then
		mdadm --detail --scan > /etc/mdadm.conf
		rc-update --quiet add mdadm-raid boot
	fi
446 447

	# manually add swap to local fstab and swapon (in case the install needs swap)
448
	sed -i -e '/swap/d' /etc/fstab
449
	echo -e "$(uuid_or_device $swap_dev)\tswap\t\tswap\tdefaults 0 0" >> /etc/fstab
450 451 452 453 454 455
	swapon -a

	install_mounted_root /mnt || return 1

	# manually add swap to mounted fstab and add the swap service to the boot runlevel
	echo -e "$(uuid_or_device $swap_dev)\tswap\t\tswap\tdefaults 0 0" >> /mnt/etc/fstab
456 457
	chroot /mnt rc-update --quiet add swap boot
	chroot /mnt rc-update --quiet add urandom boot
458 459 460 461 462 463 464 465

	unmount_partitions /mnt
	swapoff -a
	fix_mbr_all_disks

	echo ""
	echo "Installation is done. Please reboot."
	apk del -q syslinux
Natanael Copa's avatar
Natanael Copa committed
466 467
}

Natanael Copa's avatar
Natanael Copa committed
468 469 470 471 472 473 474 475 476 477 478 479 480 481 482 483
usage() {
	cat <<__EOF__
usage: setup-disk [-hr] [-k kernelflavor] [-o apkovl] [MOUNTPOINT]

Install alpine on harddisk.

options:
 -h  Show this help
 -o  Restore system from given apkovl file
 -k  Use kernelflavor instead of $KERNEL_FLAVOR
 -r  Enable software raid1 with single disk

__EOF__
	exit 1
}

484 485 486
KERNEL_FLAVOR=grsec
case "$(uname -r)" in
	*-vs[0-9]*) KERNEL_FLAVOR=vserver;;
487
	*-pae) KERNEL_FLAVOR=pae;;
488 489
esac

490
# Parse args
491
while getopts "hk:o:rv" opt; do
492
	case $opt in
493
		k) KERNEL_FLAVOR="$OPTARG";;
494
		r) USE_RAID=1;;
495
		o) APKOVL="$OPTARG";;
496
		v) VERBOSE=1;;
Natanael Copa's avatar
Natanael Copa committed
497
		*) usage;;
498 499
	esac
done
500
shift $(( $OPTIND - 1))
501

502 503
if [ -d "$1" ]; then
	# install to given mounted root
504
	install_mounted_root "${1%/}"
505 506 507
	exit $?
fi

508
disks=$(find_disks)
Natanael Copa's avatar
Natanael Copa committed
509 510 511 512

# no disks so lets exit quietly.
[ -z "$disks" ] && exit 0

513 514 515 516 517
if [ $# -gt 0 ]; then
	# check that they are 
	for i in "$@"; do
		j=$(readlink -f "$i" | sed 's:^/dev/::; s:/:!:g')
		if ! [ -e "/sys/block/$j/device" ]; then
Natanael Copa's avatar
Natanael Copa committed
518
			echo "$i is not a suitable for partitioning"
519 520 521 522 523
			exit 1
		fi
	done

else
524
	set -- $disks
525 526 527
	rootdisk=
	while ! in_list "$rootdisk" $disks "none" "abort"; do
		echo "Available disks are: $disks"
528
		echon "Which one is the root disk? (or 'none' for tmpfs root) [$1] "
529
		default_read rootdisk $1
530 531
	done
	case "$rootdisk" in
532
		abort) exit 0;;
533 534
	esac
fi
Natanael Copa's avatar
Natanael Copa committed
535

536 537 538 539 540 541 542 543 544 545
#echon "Do you want use *all* of $rootdisk for Alpine? (y/n) [n] "
#default_read useall "n"
#case "$useall" in
#	[Yy]*) useall="yes";;
#esac
#
#if [ "x$useall" != "xyes" ]; then
#	echo "Only 'use all' option is available at the moment. Sorry"
#	exit 1
#fi
Natanael Copa's avatar
Natanael Copa committed
546

547 548 549
if [ "$rootdisk" = "none" ]; then
	exit
fi
Natanael Copa's avatar
Natanael Copa committed
550

551
rootdisk_dev=${rootdisk_dev:-"/dev/$rootdisk"}
552 553
if ! [ -b "$rootdisk_dev" ]; then
	echo "$rootdisk_dev is not a block device" >&2
Natanael Copa's avatar
Natanael Copa committed
554 555 556
	exit 1
fi

557
useall $rootdisk_dev