222 lines
6.8 KiB
Bash
Executable file
222 lines
6.8 KiB
Bash
Executable file
#!/bin/sh
|
|
#
|
|
# mentalnet-install - install Mentalnet GNU/Linux from the live CD onto a hard disk
|
|
#
|
|
# Run as root on the live system: mentalnet-install
|
|
#
|
|
# The installed system boots from its own MBR (GRUB) with the root
|
|
# filesystem on a single ext4 partition.
|
|
|
|
set -u
|
|
|
|
MIN_DISK_SECTORS=2097152 # 1 GiB minimum target disk (rootfs is ~150MB,
|
|
# and old disks are cheap)
|
|
BOOT_START_SECTORS=2048 # first sector of root: leaves the MBR gap
|
|
# free for the GRUB core.img embedding
|
|
MNT=/mnt/hd
|
|
|
|
msg() { printf '%s\n' "$*"; }
|
|
warn() { printf 'WARNING: %s\n' "$*"; }
|
|
die() { printf 'ERROR: %s\n' "$*" >&2; exit 1; }
|
|
|
|
# make sure a failed run never leaves the target disk mounted and
|
|
# always restores the terminal state
|
|
cleanup() {
|
|
stty sane 2>/dev/null
|
|
grep -q " $MNT " /proc/mounts 2>/dev/null && umount "$MNT" 2>/dev/null
|
|
[ -n "${TMNT:-}" ] && umount /mnt 2>/dev/null
|
|
return 0
|
|
}
|
|
trap cleanup EXIT
|
|
|
|
[ "$(id -u)" -eq 0 ] || die "please run as root"
|
|
|
|
for t in fdisk mkfs.ext4 grub-install; do
|
|
command -v "$t" >/dev/null 2>&1 || die "required tool '$t' not found"
|
|
done
|
|
|
|
# keep the console usable even if the user hits Ctrl-S (XOFF) by
|
|
# accident while the install is running
|
|
stty -ixon 2>/dev/null
|
|
|
|
banner() {
|
|
clear 2>/dev/null
|
|
msg ""
|
|
msg " ============================================================"
|
|
msg " Mentalnet GNU/Linux - hard disk installer"
|
|
msg " ============================================================"
|
|
msg ""
|
|
msg " This will copy the live system from the CD onto a local"
|
|
msg " disk, install the GRUB bootloader, and make the disk"
|
|
msg " bootable on its own."
|
|
msg ""
|
|
}
|
|
|
|
list_disks() {
|
|
for d in /sys/block/*; do
|
|
dev=${d##*/}
|
|
case "$dev" in
|
|
sr*|loop*|ram*|zram*|md*|dm-*|fd*|nbd*) continue ;;
|
|
esac
|
|
size=$(cat "$d/size" 2>/dev/null) || continue
|
|
mib=$((size / 2048))
|
|
model=$(cat "$d/device/model" 2>/dev/null | tr -d ' ')
|
|
printf ' /dev/%-6s %6s MiB %s\n' "$dev" "$mib" "${model:-}"
|
|
done
|
|
}
|
|
|
|
banner
|
|
msg " Detected disks:"
|
|
list_disks
|
|
msg ""
|
|
|
|
printf ' Disk to install to (e.g. sda): '
|
|
read -r DISK
|
|
[ -n "${DISK:-}" ] || die "no disk given"
|
|
case "$DISK" in
|
|
sr*|loop*|ram*|zram*|md*|dm-*|fd*|nbd*) die "refusing to use $DISK" ;;
|
|
*[!/a-z0-9]*) die "invalid disk name" ;;
|
|
esac
|
|
DEV="/dev/$DISK"
|
|
SYS="/sys/block/$DISK"
|
|
[ -d "$SYS" ] && [ -b "$DEV" ] || die "$DEV is not a block device"
|
|
|
|
SIZE=$(cat "$SYS/size")
|
|
[ "$SIZE" -ge "$MIN_DISK_SECTORS" ] \
|
|
|| die "$DEV is too small (need $((MIN_DISK_SECTORS / 2048)) MiB minimum)"
|
|
|
|
if grep -q "^$DEV" /proc/mounts; then
|
|
die "$DEV (or a partition on it) is currently mounted"
|
|
fi
|
|
|
|
msg ""
|
|
warn "ALL DATA on $DEV will be DESTROYED without further notice."
|
|
printf ' Type YES to continue: '
|
|
read -r ANSWER
|
|
[ "${ANSWER:-}" = "YES" ] || { msg "Aborted."; exit 0; }
|
|
|
|
# --- partition -----------------------------------------------------------
|
|
msg " Partitioning $DEV (MBR, root at sector $BOOT_START_SECTORS) ..."
|
|
|
|
printf 'o\nn\np\n1\n%d\n%d\na\n1\nw\n' \
|
|
"$BOOT_START_SECTORS" "$((SIZE - 1))" \
|
|
| fdisk -u "$DEV" >/dev/null || die "fdisk failed"
|
|
|
|
ROOTPART="${DEV}1"
|
|
i=0
|
|
while [ ! -b "$ROOTPART" ] && [ "$i" -lt 10 ]; do
|
|
i=$((i + 1))
|
|
partprobe "$DEV" 2>/dev/null
|
|
sleep 1
|
|
done
|
|
[ -b "$ROOTPART" ] || die "$ROOTPART did not appear after partitioning"
|
|
|
|
# --- show the result and get final confirmation --------------------------
|
|
msg ""
|
|
msg " New partition table on $DEV:"
|
|
fdisk -l "$DEV"
|
|
msg ""
|
|
printf ' Proceed with installation? [y/N]: '
|
|
read -r GO
|
|
case "${GO:-}" in
|
|
y|Y|yes|Yes|YES) ;;
|
|
*) msg "Aborted."; exit 0 ;;
|
|
esac
|
|
|
|
# --- filesystems ---------------------------------------------------------
|
|
msg " Creating ext4 filesystem on $ROOTPART ..."
|
|
mkfs.ext4 -F -L rootfs "$ROOTPART" >/dev/null || die "mkfs.ext4 failed"
|
|
|
|
# --- copy the live system ------------------------------------------------
|
|
# /mnt lives on the read-only CD, so put a writable tmpfs over it to
|
|
# get a usable mount point
|
|
TMNT=
|
|
if ! grep -q ' /mnt ' /proc/mounts; then
|
|
mount -t tmpfs -o mode=0755 tmpfs /mnt || die "cannot set up /mnt"
|
|
TMNT=1
|
|
fi
|
|
mkdir -p "$MNT"
|
|
mount -t ext4 "$ROOTPART" "$MNT" || die "could not mount $ROOTPART"
|
|
|
|
msg " Copying the system (this takes a while) ..."
|
|
|
|
# NOTE: one complete archive->extraction pair per directory. busybox tar
|
|
# stops reading at the first end-of-archive marker, so a single archive
|
|
# made of several directories would transfer only the first one; a pipe
|
|
# of several archives would leave the reader hanging after the first
|
|
# EOF. Per-directory pairs are safe and give progress feedback on slow
|
|
# machines.
|
|
DIRS=
|
|
for d in bin boot etc home lib lib32 lib64 media opt root sbin srv usr var; do
|
|
[ -e "/$d" ] && DIRS="$DIRS $d"
|
|
done
|
|
|
|
# typing during the long copy used to be echoed and buffered, then
|
|
# replayed as shell commands after the installer exited; mute it
|
|
stty -echo 2>/dev/null
|
|
for d in $DIRS; do
|
|
msg " Copying: /$d"
|
|
(cd / && tar -cf - "$d") | (cd "$MNT" && tar -xpf -) \
|
|
|| { stty echo 2>/dev/null; die "copying /$d failed"; }
|
|
done
|
|
stty echo 2>/dev/null
|
|
|
|
# mount points that live in tmpfs on the CD
|
|
mkdir -p "$MNT/proc" "$MNT/sys" "$MNT/dev" "$MNT/tmp" "$MNT/run" "$MNT/mnt"
|
|
|
|
# the live system's resolv.conf is a symlink into tmpfs (udhcpc writes
|
|
# through it on the CD); on the disk udhcpc needs a real file
|
|
rm -f "$MNT/etc/resolv.conf"
|
|
: > "$MNT/etc/resolv.conf"
|
|
|
|
# CD-only artifacts must not end up on the disk
|
|
rm -f "$MNT/boot/grub/grub-eltorito.img"
|
|
|
|
# --- /etc/fstab ----------------------------------------------------------
|
|
msg " Writing /etc/fstab ..."
|
|
cat > "$MNT/etc/fstab" <<EOF
|
|
# /etc/fstab - generated by mentalnet-install
|
|
$ROOTPART / ext4 rw,noatime 0 1
|
|
proc /proc proc defaults 0 0
|
|
devpts /dev/pts devpts defaults,gid=5,mode=620,ptmxmode=0666 0 0
|
|
tmpfs /dev/shm tmpfs mode=1777 0 0
|
|
tmpfs /tmp tmpfs mode=1777 0 0
|
|
tmpfs /run tmpfs mode=0755,nosuid,nodev 0 0
|
|
sysfs /sys sysfs defaults 0 0
|
|
EOF
|
|
|
|
# --- bootloader ----------------------------------------------------------
|
|
msg " Writing bootloader configuration ..."
|
|
sed "s|@ROOTDEV@|$ROOTPART|" /usr/share/mentalnet/grub-disk.cfg \
|
|
> "$MNT/boot/grub/grub.cfg" \
|
|
|| die "could not write grub.cfg"
|
|
|
|
msg " Installing GRUB to the MBR of $DEV ..."
|
|
grub-install --boot-directory="$MNT/boot" "$DEV" \
|
|
|| die "grub-install failed"
|
|
|
|
sync
|
|
umount "$MNT" || umount -f "$MNT" || die "could not unmount $MNT"
|
|
[ -n "$TMNT" ] && umount /mnt
|
|
|
|
# discard anything the user typed ahead during the (long) copy: it was
|
|
# buffered in the tty and would otherwise be replayed as shell commands
|
|
# after this script exits
|
|
stty -icanon min 0 time 0 2>/dev/null
|
|
i=0
|
|
while [ "$i" -lt 512 ] && read -r junk; do
|
|
i=$((i + 1))
|
|
done
|
|
stty sane 2>/dev/null
|
|
|
|
clear 2>/dev/null
|
|
msg ""
|
|
msg " ============================================================"
|
|
msg " Installation complete!"
|
|
msg " ============================================================"
|
|
msg ""
|
|
msg " Root partition : $ROOTPART (ext4)"
|
|
msg " Login : root / mnlinux"
|
|
msg ""
|
|
msg " Remove the CD and reboot: poweroff"
|
|
msg ""
|