#!/bin/sh PREFIX= . "$PREFIX/lib/libalpine.sh" MBR=${MBR:-"/usr/share/syslinux/mbr.bin"} in_list() { local i="$1" shift while [ $# -gt 0 ]; do [ "$i" = "$1" ] && return 0 shift done return 1 } # 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= case "$1" in /dev/md*) echo "$1" && return 0;; esac 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 enumerate_fstab() { local mnt="$1" local fs_spec= fs_file= fs_vfstype= fs_mntops= fs_freq= fs_passno= [ -z "$mnt" ] && return local escaped_mnt=$(echo $mnt | sed 's:/:\\/:g') awk "\$2 ~ /^$escaped_mnt/ {print \$0}" /proc/mounts | \ 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 } is_vmware() { grep -q VMware /proc/scsi/scsi 2>/dev/null \ || grep -q VMware /proc/ide/hd*/model 2>/dev/null } is_xen() { [ -d /proc/xen ] } # return true (0) if given device is lvm is_lvm() { lvs "$1" >/dev/null 2>&1 } # Find the disk device from given partition disk_from_part() { # we need convert cciss/c0d0* cciss!c0d0*... local i= part=$(echo ${1#/dev/} | sed 's:/:!:g') for i in /sys/block/*/$part; do i=${i%/*} # ...and back from cciss!c0d0 to cciss/c0d0 if [ -b "/dev/${i##*/}" ]; then echo "/dev/${i##*/}" | sed 's:!:/:g' return 0 fi done return 1 } 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 } # 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() { local mnt="$1" awk "\$2 == \"$mnt\" { print \$1 }" /proc/mounts | tail -n 1 } supported_boot_fs() { local supported="ext2 ext3 ext4 btrfs" 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= local initfs_features="ata base ide scsi usb virtio" rootdev=$(find_mount_dev "$mnt") if [ -z "$rootdev" ]; then echo "$mnt does not seem to be a mount point" >&2 return 1 fi root_fs=$(find_mount_fs "$mnt") initfs_features="$initfs_features $root_fs" if is_lvm "$rootdev"; then initfs_features="$initfs_features lvm" fi 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 mbrdisk=$(disk_from_part $bootdev) if [ -e "/sys/block/${rootdev#/dev/}/md" ]; then local md=${rootdev#/dev/} initfs_features="$initfs_features raid" raidmod=$(cat /sys/block/$md/md/level) raidmod=",$raidmod" raidopt="-r" # get a list of slaves mbrdisk= for i in /sys/block/$md/slaves/*; do j=${i##*/} i=${j%[0-9]*} if [ -b "/dev/$i" ]; then mbrdisk="$mbrdisk /dev/${i}" fi done fi if [ -n "$VERBOSE" ]; then echo "Root device: $rootdev" echo "Root filesystem: $root_fs" echo "Boot device: $bootdev" echo "Boot filesystem: $boot_fs" echo "MBR disk(s): $mbrdisk" fi if [ -z "$APKOVL" ]; then ovlfiles=/tmp/ovlfiles lbu package - | tar -C "$mnt" -zxv > "$ovlfiles" else echo "Restoring backup from $APKOVL to $rootdev..." unpack_apkovl "$APKOVL" "$mnt" || return 1 fi # generate mkinitfs.conf mkdir -p "$mnt"/etc/mkinitfs echo "features=\"$initfs_features\"" > "$mnt"/etc/mkinitfs/mkinitfs.conf # 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 </dev/null || return 1 echo "" # make things bootable if is_vmware; then pax_nouderef="pax_nouderef " else pax_nouderef= fi if is_xen; then # create a menu.lst mkdir -p "$mnt"/boot/grub cat >"$mnt"/boot/grub/menu.lst <"$mnt"/boot/extlinux.conf <&1) \ || echo "$errmsg" done } # figure out decent default swap size in mega bytes find_swap_size() { if [ -n "$SWAP_SIZE" ]; then return fi local memtotal_kb=$(awk '$1 == "MemTotal:" {print $2}' /proc/meminfo) # use 2 * avaiable ram echo $(( $memtotal_kb * 2 / 1024 )) } has_mounted_part() { local p # parse /proc/mounts for mounted devices for p in $(awk '$1 ~ /^\/dev\// {gsub("/dev/", "", $1); print $1}' \ /proc/mounts); do [ "$p" = "$1" ] && return 0 [ -e /sys/block/$1/$p ] && return 0 done return 1 } 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 if has_holders /sys/block/$b; then [ -n "$USE_RAID" ] && echo "Warning: $dev is part of a running raid" >&2 return 1 fi # check so its not an md device [ -e /sys/block/$b/md ] && return 1 return 0 } find_disks() { local p= for p in $(awk '$1 ~ /[0-9]+/ {print $4}' /proc/partitions); do is_available_disk $p && echo -n " $p" done } stop_all_raid() { local rd for rd in /dev/md*; do [ -b $rd ] && mdadm --stop $rd done } native_disk_install() { local rootdisk_dev="$1" local i size local boot_size=100 boot_part_type="83" local swap_size="$SWAP_SIZE" swap_part_type="82" local root_part_type="83" local raidpkg= partitions= local minimum_root_size=$(($boot_size * 2)) local rootfs=ext4 bootfs=ext4 if [ -n "$USE_RAID" ]; then boot_part_type="fd" swap_part_type="fd" root_part_type="fd" raidpkg="mdadm" fi dmesg -n1 apk_add -q sfdisk e2fsprogs $raidpkg || return 1 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 "" if [ -n "$APKOVL" ]; then echo "System from $APKOVL will be restored" fi echo -n "WARNING: All contents of $rootdisk_dev will be erased. Continue? [y/N]: " read i case "$i" in y*|Y*);; *) return 1;; esac echo "Initializing partitions..." [ -n "$USE_RAID" ] && stop_all_raid # 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 # create new partitions (cat <>/tmp/sfdisk.out || return 1 # create device nodes if not exist mdev -s if [ -n "$USE_RAID" ]; then local p= rd= for p in $(sfdisk -l $rootdisk_dev 2>/dev/null \ | awk '/Linux raid/ {print $1}'); do local opt="--metadata=0.90" case "$p" in *1) rd=/dev/md0; boot_dev=/dev/md0;; *2) rd=/dev/md1; swap_dev=/dev/md1 opt= ;; *3) rd=/dev/md2; root_dev=/dev/md2;; esac mdadm --create $rd --level=1 --raid-devices=2 \ $opt --quiet --run $p missing 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.$bootfs -q $boot_dev >/dev/null \ && mkswap $swap_dev >/dev/null \ && mkfs.$rootfs -q >/dev/null $root_dev \ || return 1 mkdir -p /mnt mount -t $rootfs $root_dev /mnt || return 1 mkdir -p /mnt/boot mount -t $bootfs $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 # manually add swap to local fstab and swapon (in case the install needs swap) sed -i -e '/swap/d' /etc/fstab echo -e "$(uuid_or_device $swap_dev)\tswap\t\tswap\tdefaults 0 0" >> /etc/fstab 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 chroot /mnt rc-update --quiet add swap boot chroot /mnt rc-update --quiet add urandom boot unmount_partitions /mnt swapoff -a fix_mbr_all_disks echo "" echo "Installation is done. Please reboot." apk del -q syslinux } # install needed programs init_progs() { local raidpkg= [ -n "$USE_RAID" ] && raidpkg="mdadm" apk_add -q sfdisk e2fsprogs lvm2 $raidpkg } # setup disk dev in $1 for LVM usage. # usage: setup_partitions # if is set, then will a partition for boot be created. setup_partitions() { local diskdev="$1" local answer= local boot_size="$2" boot_part_type="83" local lvm_part_type="8e" local minimum_lvm_size=$(($boot_size * 2)) if [ -n "$USE_RAID" ]; then boot_part_type="fd" lvm_part_type="fd" fi local lvm_size=$(( $(sfdisk -s $diskdev) / 1024 - $boot_size)) if [ "$lvm_size" -lt "$minimum_lvm_size" ]; then echo "The $diskdev is too small. At least $(( $boot_size + $minimum_lvm_size)) is needed." >&2 return 1 fi echo -n "WARNING: All contents of $diskdev will be erased. Continue? [y/N]: " read answer case "$answer" in y*|Y*);; *) return 1;; esac echo "Initializing partitions..." [ -n "$USE_RAID" ] && stop_all_raid # 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 $diskdev >/dev/null # create new partitions ( if [ $boot_size -gt 0 ]; then echo "0,$boot_size,$boot_part_type,*" fi echo ",,$lvm_part_type" ) | sfdisk -q -L -uM $diskdev >>/tmp/sfdisk.out || return 1 # create device nodes if not exist mdev -s } # find the bootable partition on given disk find_boot_partition() { sfdisk -d $1 | awk '/bootable/ {print $1}' } # find the partition(s) for LVM # this is not marked as bootable and is either type 8e of fd depending on # if raid is used or not find_lvm_partition() { local type=8e [ -n "$USE_RAID" ] && type=fd sfdisk -d $1 | grep -v bootable | awk "/Id=$type/ {print \$1}" } # set up boot device setup_boot_dev() { local bootfs=ext4 local diskdev="$1" local part=$(find_boot_partition $diskdev) local bootdev=$part [ -z "$bootdev" ] && return 1 if [ -n "$USE_RAID" ]; then mdadm --create /dev/md0 --level=1 --raid-devices=2 \ --metadata=0.90 --quiet --run $part missing || return 1 bootdev=/dev/md0 fi mkfs.$bootfs -q $bootdev BOOT_DEV="$boot_dev" } # setup device for lvm, create raid array if needed setup_lvm_volume_group() { local diskdev="$1" local vgname="$2" local part=$(find_lvm_partition $diskdev) local lvmdev=$part if [ -n "$USE_RAID" ]; then if [ -n "$BOOT_DEV" ]; then lvmdev=/dev/md1 else lvmdev=/dev/md0 fi mdadm --create $lvmdev --level=1 --raid-devices=2 \ --quiet --run $part missing || return 1 fi # be quiet on success local errmsg=$(dd if=/dev/zero of=$lvmdev bs=1k count=1 2>&1) \ || echo "$errmsg" pvcreate --quiet $lvmdev && vgcreate --quiet $vgname $lvmdev } # setup and enable swap on given volumegroup if needed setup_swap() { local vgname="$1" local swap_dev=/dev/$vgname/lv_swap if [ -z "$SWAP_SIZE" ] || [ "$SWAP_SIZE" -eq 0 ]; then return fi lvcreate --quiet -n lv_swap -L ${SWAP_SIZE}MB $vgname mkswap $swap_dev >/dev/null sed -i -e '/swap/d' /etc/fstab echo -e "$swap_dev\tswap\t\tswap\tdefaults 0 0" >> /etc/fstab swapon -a rc-update --quiet add swap boot } # if /var is mounted, move out data and umount it reset_var() { [ -z "$(find_mount_dev /var)" ] && return 0 mkdir /.var mv /var/* /.var/ 2>/dev/null umount /var && rm -rf /var && mv /.var /var && rm -rf /var/lost+found } data_only_disk_install() { local diskdev="$1" local varfs=ext4 local vgname=vg0 local var_dev=/dev/$vgname/lv_var init_progs || return 1 setup_partitions $diskdev 0 || return 1 setup_lvm_volume_group $diskdev $vgname || return 1 setup_swap $vgname lvcreate --quiet -n ${var_dev##*/} -l 100%FREE $vgname mkfs.$varfs -q $var_dev >/dev/null || return 1 sed -i -e '/[[:space:]]\/var[[:space:]]/d' /etc/fstab echo -e "${var_dev}\t/var\t\t${varfs}\tdefaults 1 2" >> /etc/fstab mv /var /.var mkdir /var mount /var mv /.var/* /var/ rmdir /.var if [ -n "$USE_RAID" ]; then mdadm --detail --scan > /etc/mdadm.conf rc-update --quiet add mdadm-raid boot fi /etc/init.d/syslog --quiet restart } 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 } diskselect_help() { cat <<__EOF__ TODO __EOF__ } diskmode_help() { cat <<__EOF__ TODO __EOF__ } # ask for a root or data disk # returns answer in global variable $answer ask_disk() { local prompt="$1" local help_func="$2" shift 2 answer= while ! in_list "$answer" $@ "none" "abort"; do echo "Available disks are: $@" echon "$prompt [$1] " default_read answer $1 case "$answer" in 'abort') exit 0;; 'none') return 0;; '?') $help_func;; *) if ! [ -b "/dev/$answer" ]; then echo "/dev/$answer is not a block device" >&2 answer= fi;; esac done } KERNEL_FLAVOR=grsec case "$(uname -r)" in *-vs[0-9]*) KERNEL_FLAVOR=vserver;; *-pae) KERNEL_FLAVOR=pae;; esac SWAP_SIZE=$(find_swap_size) # Parse args while getopts "hk:o:qrs:v" opt; do case $opt in k) KERNEL_FLAVOR="$OPTARG";; o) APKOVL="$OPTARG";; q) QUIET=1;; r) USE_RAID=1;; s) SWAP_SIZE="$OPTARG";; v) VERBOSE=1;; *) usage;; esac done shift $(( $OPTIND - 1)) if [ -d "$1" ]; then # install to given mounted root install_mounted_root "${1%/}" exit $? fi reset_var swapoff -a # stop all volume groups in use vgchange --ignorelockingfailure -a n >/dev/null 2>&1 if [ -n "$USE_RAID" ]; then stop_all_raid fi disks=$(find_disks) disk=none # no disks so lets exit quietly. if [ -z "$disks" ]; then [ -z "$QUIET" ] && echo "No disks found." >&2 exit 0 fi 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 echo "$i is not a suitable for partitioning" exit 1 fi done disk=${1##/dev/} else ask_disk "Which disk would you like to use? (or '?' for help or 'none')" \ diskselect_help $disks disk=$answer fi diskmode= if [ "$disk" != none ]; then answer= while true; do echon "How would you like to use $disk? ('root', 'data' or '?' for help) [?] " default_read answer '?' case "$answer" in '?') diskmode_help;; root|data) break;; esac done diskmode="$answer" fi dmesg -n1 # native disk install case "$diskmode" in root) native_disk_install /dev/$disk;; data) data_only_disk_install /dev/$disk;; esac