#!/bin/bash
# sys/b9 - system bootstrap helper
set -e
source "/home/zwelch/src/mcf/mcsh-release/install/share/mcsh/mcsh.sh"
lib_load 'sys/tool/vbox'
lib_load "mcui"
######
# Native Dependencies
b9_client_packages=( debootstrap build-essential )
######
# Library Configuration
#
# See the reference for b9.conf_ for configuration information.
#
# .. _b9.conf: ../../conf/sys/b9.conf.html
b9_config_init() {
script_setting_vars \
bootstrap_mirror \
bootstrap_fs_size bootstrap_fs_type \
bootstrap_release bootstrap_arch bootstrap_variant \
bootstrap_hostname bootstrap_domain \
bootstrap_admin bootstrap_shell bootstrap_skeldir
script_setting_arrays \
bootstrap_users bootstrap_net_ifaces \
bootstrap_dns_domains bootstrap_dns_servers \
bootstrap_loader_packages bootstrap_core_packages \
bootstrap_net_packages bootstrap_packages
}
b9_config_check() {
[ "$bootstrap_arch" ] || bootstrap_arch=$(dpkg --print-architecture)
}
######
# Bootstrap System Dependencies
# $bootstrap_loader_packages[] - Bootloader packages.
bootstrap_loader_packages=( debconf grub-pc udev )
#;bootstrap_loader_packages=( grub-efi-amd64-signed )
# $bootstrap_core_packages[] - Lists the core system packages.
bootstrap_core_packages=(
dialog
less
linux-image-generic
screen
sudo
vim
)
# $bootstrap_net_packages[] - Packages required for basic NAT networking
bootstrap_net_packages=(
ifupdown
isc-dhcp-client
net-tools
openssh-server
openssh-client
)
######
# Images
b9_image() { cmd_dispatch "$@"; }
b9_image_usage() {
cat <<USAGE
<cmd> <name>
Disk Image Commands:
new <name> Creates a new disk image.
delete <name> Deletes a disk image.
Disk Image Formatting Commands:
fdisk <name> Runs fdisk on the image.
gdisk <name> Runs gdisk on the image.
Low-Level Disk Image Commands:
mount <name> Mount the image via NBD.
umount <name> Unmount the image.
filename <name> Prints the filename of the image.
USAGE
}
b9_image_new() {
has_args 1 "$@"
local name=$1
run vbox_hd_create "$1" "$bootstrap_fs_size"
}
b9_image_info() { has_args 1 "$@"; qemu_img_info "$(vbox_hd_filename "$1")"; }
b9_image_mount() {
has_args 1 "$@"
local name=$1
local dev
dev=$(vbox_hd_partition_mount "$name" "raw")
app_echo "$name: image connected to $dev"
}
b9_image_umount() {
has_args 1 "$@"
local name=$1
vbox_hd_partition_umount "$name" "raw"
app_echo "$name: image disconnected from NBD"
}
b9_image_fdisk() {
has_args 1 "$@"
local name=$1
local -a opts=( --label=/ --filesystem="$bootstrap_fs_type" )
local hdfile
hdfile=$(vbox_hd_filename "$name")
run_sudo virt-format "${opts[@]}" -a "$hdfile"
return
# TODO: do it without virt-format?
local dev
dev=$(vbox_hd_partition_mount "$name" "raw")
local dev=/dev/loop0
qemu_img_mount "$dev" "$(vbox_hd_filename "$name")"
run_sudo fdisk -u -l "$dev"
qemu_img_umount "$dev"
vbox_hd_partition_umount "$name" "raw"
}
b9_image_gdisk() {
error "gdisk: umplemented"
}
b9_image_fish() {
min_args 1 "$@"
local name=$1
shift
local file
file=$(vbox_hd_filename "$name")
run_sudo guestfish -a "$file" --ro "$@"
}
b9_image_filename() {
has_args 1 "$@"
local name=$1
vbox_hd_filename "$1"
}
######
# Partitions
partition_dev() {
has_args 2 "$@"
local name=$1
local part=$2
local dev pdev
dev=$(vbox_hd_partition_mount "$name" "raw")
echo "${dev}p${part}"
}
partition_init() {
has_args 2 "$@"
local fs=$1
local pdev=$2
run_sudo mkfs.$fs "$pdev"
}
partition_delete() {
false
}
partition_mount() {
has_args 2 "$@"
local pdev=$1
local mnt=$2
run_mkdir "$mnt"
run_sudo mount "$pdev" "$mnt"
}
partition_umount() {
min_args 1 "$@"
run_sudo umount "$@"
}
b9_partition() { cmd_dispatch "$@"; }
b9_partition_usage() {
cat <<USAGE
<cmd> ...
Disk Image Partition Commands:
init <name> <part> Format the partition
mount <name> <part> <path> Mount the partition
umount <name> <part> <path> Unmount the partition
USAGE
}
b9_partition_init() {
has_args 2 "$@"
local name=$1
local part=$2
local pdev
pdev=$(partition_dev "$name" "$part")
partition_init "$bootstrap_fs_type" "$pdev"
vbox_hd_partition_umount "$name" "raw"
}
b9_partition_mount() {
min_args 2 "$@"
local name=$1
local part=$2
local path=$3
local pdev mdir
mdir=$(chroot_filename "$name" "$path")
vbox_hd_partition_mount "$name" "$part" "$mdir"
}
b9_partition_umount() {
min_args 2 "$@"
local name=$1
local part=$2
local path=$3
local mdir
mdir=$(chroot_filename "$name" "$path")
vbox_hd_partition_umount "$name" "$part"
vbox_hd_partition_umount "$name" "raw"
}
######
# Chroot CLI
b9_chroot() { cmd_dispatch "$@"; }
b9_chroot_usage() {
cat <<USAGE
<cmd> ...
mount Sets up mounts inside chroot
umount Removes the mounts inside the chroot
bootstrap Runs debootstrap
install Installs initial packages
users Creates initial users
passwd Resets user passwords
USAGE
}
b9_chroot_mount() { chroot_mount "$@"; }
b9_chroot_umount() { chroot_umount "$@"; }
b9_chroot_bootstrap() {
has_args 1 "$@"
local name=$1
local -a opts
[ -z "$bootstrap_arch" ] || \
list_append opts --arch "$bootstrap_arch"
[ -z "$bootstrap_variant" ] || \
list_append opts --variant "$bootstrap_variant"
local rootdir
rootdir="$(chroot_filename "$name" "/")"
run_sudo debootstrap "${opts[@]}" \
"$bootstrap_release" "$rootdir" "$bootstrap_mirror"
}
b9_chroot_locale() {
has_args 1 "$@"
local name=$1
local lang
lang=$(source "$(chroot_filename "$name" "/etc/default/locale")" && echo $LANG)
run_chroot "$name" locale-gen "$lang"
run_chroot "$name" dpkg-reconfigure -f non-interactive tzdata
}
b9_chroot_install() { cmd_dispatch "$@"; }
b9_chroot_install_usage() {
cat <<USAGE
<cmd> <name>
Chroot Installation Commands:
all <name> Install/update everything into chroot
base <name> Install base packages into chroot
loader <name> Install bootloader into chroot
core <name> Install core packages into chroot
USAGE
}
b9_chroot_install_all() {
has_args 1 "$@"
local name=$1
b9_chroot_install_base "$name"
b9_chroot_install_loader "$name"
b9_chroot_install_core "$name"
}
b9_chroot_install_base() {
has_args 1 "$@"
local name=$1
app_echo "$name: updating package cache"
apt_chroot_update "$name"
app_echo "$name: installing language pack"
apt_chroot_install "$name" language-pack-en-base
app_echo "$name: upgrading base packages"
apt_chroot_upgrade "$name"
}
b9_chroot_install_core() {
has_args 1 "$@"
local name=$1
app_echo "$name: installing core packages"
local -a packages
list_append packages "${bootstrap_core_packages[@]}"
list_append packages "${bootstrap_net_packages[@]}"
list_append packages "${bootstrap_packages[@]}"
apt_chroot_install "$name" "${packages[@]}"
}
b9_gen_grub_device_map() {
cat <<DEVICEMAP
(hd0) ${dev}
DEVICEMAP
}
b9_chroot_install_loader() {
has_args 1 "$@"
local name=$1
#---
# gen_chroot_install "$name" fstab "/etc/fstab"
# fstab_hd_type=nbd gen_chroot_install "$name" fstab "/etc/fstab"
# local dev
# dev=$(vbox_hd_partition_mount "$name" "raw")
# vbox_hd_partition_umount "$name" "raw"
# gen_chroot_install "$name" grub_device_map "/boot/grub/device.map"
#+++
#; install grub-pc package
echo "grub-pc grub-pc/install_devices_empty bool true" \
| run_chroot "$name" debconf-set-selections
apt_chroot_install "$name" "${bootstrap_loader_packages[@]}"
}
b9_chroot_update_loader() {
has_args 1 "$@"
local name=$1
dev=$(vbox_hd_partition_mount "$name" "raw")
vbox_hd_partition_umount "$name" "raw"
#; install grub onto image
run_chroot "$name" grub-install "$dev"
run_chroot "$name" update-grub2
local grubcfg="/boot/grub/grub.cfg"
run_chroot "$name" sed -i -e "s,${dev}p1,/dev/sda1,g" "$grubcfg"
#; run_sudo rm -f "$(chroot_filename "$name" "boot/grub/device.map")"
#; gen_chroot_install "$name" fstab "/etc/fstab"
}
b9_chroot_users() {
has_args 1 "$@"
local name=$1
local u
for u in "${bootstrap_users[@]}"; do
local -a opts
list_append opts -s "$bootstrap_shell"
list_append opts -k "$bootstrap_skeldir"
run_chroot "$name" useradd "${opts[@]}" -m "$u"
run_chroot "$name" gpasswd --add "$u" sudo
done
b9_chroot_passwd "$1" root "${bootstrap_users[@]}"
}
b9_chroot_passwd() {
min_args 2 "$@"
local name=$1
shift
local -a plist
local file="$chroot_mntdir/$name.passwd"
run rm -f "$file"
local u
for u in "$@"; do
info "$u: password reset"
local pass
pass=$(apg -n 1 -m 12)
echo "$u:$pass"
list_append plist "$pass"
done | tee "$file" | run_chroot "$name" chpasswd
run chmod 400 "$file"
}
b9_chroot_shell() {
has_args 1 "$@"
local name="$1"
run_chroot "$name" bash -i
}
######
# Chroot File Generation
b9_gen() { cmd_dispatch "$@"; }
b9_gen_usage() {
cat <<USAGE
<cmd> ...
Chroot File Generation Commands:
chroot
Configuration File Inspection Commands:
fstab [<dev>] Prints generated fstab file
hostname Prints generated hostname file
hosts Prints generated hosts file
resolvconf Prints generated resolv.conf file
interfaces Prints generated interfaces file
localtime Prints generated localtime file (bin)
timezone Prints generated timezone file
locale Prints generated locale file
sources Prints generated sources.list file
USAGE
}
b9_gen_fstab() {
local flavor=${fstab_hd_type:-sd}
local dev
case "$flavor" in
(nbd) dev=$(vbox_hd_partition_mount "$name" raw)p1 ;;
(sd) dev=/dev/sda1
esac
#---
cat <<FSTAB
# /etc/fstab: static file system information.
$dev / $bootstrap_fs_type errors=remount-ro 0 0
FSTAB
#+++
case "$1" in
(nbd) vbox_hd_partition_umount "$name" raw ;;
(*) ;;
esac
}
b9_gen_hostname() {
echo "$bootstrap_hostname"
}
b9_gen_hosts() {
cat <<HOSTS
127.0.0.1 localhost.localdomain localhost
127.0.1.1 $bootstrap_hostname.$bootstrap_domain $bootstrap_hostname"
HOSTS
}
b9_gen_resolvconf() {
local search="${bootstrap_dns_domains[*]}"
echo "search $search"
local ns
for ns in "${bootstrap_dns_servers[@]}"; do
echo "nameserver $ns"
done
}
b9_gen_interfaces() {
#---
cat <<INTERFACES
# interfaces(5) file used by ifup(8) and ifdown(8)
auto lo
iface lo inet loopback
auto enp0s3
iface enp0s3 inet dhcp
INTERFACES
#+++
}
b9_gen_localtime() {
local lt
lt=$(realpath "/etc/localtime")
cat "$lt"
}
b9_gen_locale() {
cat "/etc/default/locale"
}
b9_gen_timezone() {
cat "/etc/timezone"
}
b9_gen_sources() {
cat "/etc/apt/sources.list"
}
gen_chroot_install() {
has_args 3 "$@"
local name=$1
local part=$2
local file=$3
run "b9_gen_$part" | chroot_write_file "$name" "$file"
}
b9_gen_chroot() {
has_args 1 "$@"
local name=$1
local bootstrap_hostname=$1
gen_chroot_install "$name" fstab "/etc/fstab"
gen_chroot_install "$name" hostname "/etc/hostname"
gen_chroot_install "$name" hosts "/etc/hosts"
gen_chroot_install "$name" resolvconf "/etc/resolv.conf"
gen_chroot_install "$name" interfaces "/etc/network/interfaces"
gen_chroot_install "$name" localtime "/etc/localtime"
gen_chroot_install "$name" locale "/etc/default/locale"
gen_chroot_install "$name" timezone "/etc/timezone"
gen_chroot_install "$name" sources "/etc/apt/sources.list"
}
######
# Wizard
b9_wizard() { cmd_dispatch "$@"; }
b9_wizard_usage() {
cat <<USAGE
<cmd> <name>
Wizard Commands:
magic <name> Magically create a new bootable image
report <name> Reports information about an image
Wizardly Image Commands:
init <name> Repartitions an existing image
start <name> Mounts the image before chroot
finish <name> Unmounts the image after chroot
Wizardly Chroot Commands:
bootstrap <name> Bootstraps the chroot
generate <name> Generates chroot configuration files
install <name> Installs packages in the chroot
upgrade <name> Upgrades packages in the chroot
USAGE
}
b9_wizard_magic() {
has_args 1 "$@"
local name=$1
b9_wizard_init "$name"
b9_wizard_start "$name"
b9_wizard_bootstrap "$name"
b9_wizard_generate "$name"
b9_wizard_install "$name"
b9_wizard_finish "$name"
b9_wizard_report "$name"
}
b9_wizard_init() {
has_args 1 "$@"
local name="$1"
if vbox_hd_exists "$name"; then
local result
mcui_yesno_warn result "$name: erase the disk image?"
[ "$result" = yes ] || error "user canceled, aborting..."
else
b9_image_new "$name"
fi
b9_image_fdisk "$name"
return
#; virt-format does this for us; only needed if we can use fdisk
b9_image_mount "$name"
b9_partition_init "$name" 1
b9_image_umount "$name"
}
b9_wizard_start() {
has_args 1 "$@"
local name="$1"
b9_image_mount "$name"
b9_partition_mount "$name" 1
}
b9_wizard_bootstrap() {
has_args 1 "$@"
local name="$1"
b9_chroot_bootstrap "$name"
}
b9_wizard_generate() {
has_args 1 "$@"
local name="$1"
b9_gen_chroot "$name"
b9_chroot_mount "$name"
b9_chroot_locale "$name"
b9_chroot_users "$name"
b9_chroot_umount "$name"
}
b9_wizard_install() {
has_args 1 "$@"
local name="$1"
b9_chroot_mount "$name"
b9_chroot_install_all "$name"
b9_chroot_update_loader "$name"
b9_chroot_umount "$name"
}
b9_wizard_shell() {
has_args 1 "$@"
local name="$1"
b9_wizard_start "$name"
b9_chroot_mount "$name"
b9_chroot_shell "$name"
b9_chroot_umount "$name"
b9_wizard_finish "$name"
}
b9_wizard_finish() {
has_args 1 "$@"
local name="$1"
b9_partition_umount "$name" 1
b9_image_umount "$name"
app_echo "$name: wizard finished!"
}
b9_wizard_report() {
has_args 1 "$@"
local name="$1"
local file="$chroot_mntdir/$name.passwd"
app_echo "$name: default VM system passwords:"
cat "$file"
}
######
# Check
b9_check() {
true
}
######
# Main
b9_desc() { echo "Virtual disk bootstrap helper"; }
b9_usage() {
cat <<USAGE
<cmd> [...]
Command Groups:
wizard ... System image creation commands
image ... Disk image commands
partition ... Disk partition commands
chroot ... Chroot creation commands
gen ... Chroot configuration commands
USAGE
}
app_run "$@"
Generated on Fri Jul 28 14:34:41 PDT 2017 by mcsh d14 v0.23.0.