diff options
Diffstat (limited to 'bin')
| -rwxr-xr-x | bin/hdmi | 8 | ||||
| -rwxr-xr-x | bin/vm | 258 |
2 files changed, 262 insertions, 4 deletions
@@ -2,8 +2,8 @@ # Toggle auxiliary HDMI screen on/off when connected/disconnected. opt="--auto --left-of eDP1" -#opt="--auto --right-of eDP-1" -#opt="--auto --above eDP-1" +#opt="--auto --right-of eDP1" +#opt="--auto --above eDP1" # Also add the following rule to /etc/udev/rules.d/hdmi.rules # KERNEL=="card0", SUBSYSTEM=="drm", ENV{DISPLAY}=":0", ENV{XAUTHORITY}="/home/marc/.Xauthority", RUN+="/home/marc/bin/hdmi" @@ -11,5 +11,5 @@ opt="--auto --left-of eDP1" # wait for the screen to settle sleep 3 -xrandr | grep -q 'HDMI1 connected' || opt="--off" -xrandr --output HDMI1 $opt +xrandr | grep -q '^DP1 connected' || opt="--off" +xrandr --output DP1 $opt @@ -0,0 +1,258 @@ +#!/bin/sh + +# Manage virtual machines +# +# Prereq: +# - curl +# - cdrtools (isoinfo) +# - expect +# - qemu +# - screen +# +# TODO: +# - extract bzImage and initrd.gz with isoinfo +# - ssh scripts to finish install (once ssh is ready) +# +# DONE: +# - setup vde and networking +# - setup ssh keys so it is possible to ssh in vm from host +# +unset CDPATH +export LC_ALL=C IFS=' +' + +version='vm-0.1' + +arch=$(uname -m) +sys=$(uname -s) +alpine_version='3.15.0' +pubkey=$HOME/.ssh/id_rsa.pub +dir="${VM_DIR:-$HOME/.vm}" + +console() { + usage 'console name' 'Attach a console to a virtual machine' && return + [ "$1" ] || die "missing argument" + is_running "$1" && screen -r "vm!$1!" +} + +create() { + usage 'create [-s size] name' 'Create an alpinelinux disk image' && return + size=8g + while getopts :s: opt; do + case $opt in + s) size=$OPTARG ;; + *) Opth=2 create_alpine_image; return ;; + esac + done + shift $((OPTIND - 1)) + [ -d "$dir/$1" ] && die "create failed: $dir/$1 already exists" + mkdir "$dir/$1" + cd "$dir/$1" || die "create failed: invalid directory $dir/$1" + qemu-img create "$1.raw" "$size" || die "create failed" + mac=$(new_macaddr) + ip=$(new_ip) + hdd="$1.raw" + echo "hdd=$hdd +mac=$mac +ip=$ip" >> config + + # Before install do not use virtio, as devices may not recognized as bootable + # TODO: alternate way: extract kernel and initrd files and pass them directly to qemu + screen -S "vm!$1!" -d -m qemu-system-$arch -nographic \ + -cdrom ../alpine-iso/alpine-virt-$alpine_version-$arch.iso \ + -hdd "$hdd" -net nic,macaddr=$mac -net vde + setup_alpine "$1" + post_setup_alpine "$1" +} + +die() { [ "$1" ] && echo "$0: $*" >&2; exit 1; } + +help() { + usage 'help' 'Print this help text' && return + printf "$version\n Manage virtual machines\n\nUsage: vm command [options] [args]\n" + Opth=1; for c in $Cmdlist; do $c; done +} + +init() { + mkdir -p "$dir" +} + +init_alpine_iso() { + usage init_alpine_iso '' && return + mkdir -p "$dir/alpine-iso" + iso_url="https://dl-cdn.alpinelinux.org/alpine/v${alpine_version%.*}/releases/$arch" + iso="alpine-virt-$alpine_version-$arch.iso" + cd "$dir/alpine-iso" + [ -f "$iso" ] || curl -LO "$iso_url/$iso" || rm -f "$iso" + echo "iso=$iso +" > config + echo 10 > ../index +} + +is_running() { screen -ls "vm!$1!" >/dev/null 2>&1; } + +ls() { + usage 'ls' 'list virtual machines' && return + init && cd "$dir" || die "could not change dir to $dir" + for i in */; do + i=${i%/} + [ "$i" = '*' ] && continue + is_running "$i" && state=active || state=stopped + printf "%-20s %s\n" "$i" "$state" + done +} + +new_ip() { + read index < $dir/index + index=$((index + 1)) + echo "$index" > $dir/index + echo "10.0.2.$index/24" +} + +new_macaddr() { printf 'de:ad:be:ef:%02x:%02x\n' $((RANDOM % 256)) $((RANDOM % 256)); } + +pidof() { + usage 'pidof name' 'print the PID of a virtual machine' && return + p=$(screen -ls "vm!$1!" | awk 'NR==2 {print substr($1, 1, index($1, ".")-1)}') + [ "$p" ] && pgrep -P $p +} + +post_setup_alpine() { + start "$1" + read proto key id < "$pubkey" + # echo ' + expect -c ' + set timeout -1 + spawn screen -x "vm!'$1'!" + expect { + " login: " { send "root\r"; exp_continue } + "Password: " { send "root\r"; exp_continue } + ":~# " + } + send "mkdir -pm 0700 .ssh\r" + expect ":~# " + send "echo '$proto' '$key' '$id' >.ssh/authorized_keys\r" + expect ":~# " + send "exit\r" + ' +} + +# setup_alpine automates alpine installation from iso to image. +# When done, base image system is ready, with storage and network up. +# No user nor ssh access configured yet. +# TODO: custom network (in case of no dhcp). +setup_alpine() { + usage 'setup_alpine' && return + expect -c ' + set timeout -1 + spawn screen -x "vm!'$1'!" + expect { + "localhost login: " { send "root\r"; exp_continue } + "localhost:~# " { send "SWAP_SIZE=0 setup-alpine\r"; exp_continue } + "Select keyboard layout: " { send "\r"; exp_continue } + "Enter system hostname" { send "'$1'\r"; exp_continue } + "Which one do you want to initialize?" { send "\r"; exp_continue } + "Ip address for eth0?" { send "'$ip'\r"; exp_continue } + "Gateway?" { send "10.0.2.2\r"; exp_continue } + "manual network configuration?" { send "\r"; exp_continue } + "DNS domain name?" { send "lan\r"; exp_continue } + "DNS nameserver(s)?" { send "10.0.2.2 1.1.1.1\r"; exp_continue } + "New password:" { send "root\r"; exp_continue } + "Retype password:" { send "root\r"; exp_continue } + "Which timezone are you in?" { send "\r"; exp_continue } + "HTTP/FTP proxy URL?" { send "\r"; exp_continue } + "Enter mirror number " { send "\r"; exp_continue } + "Which SSH server?" { send "\r"; exp_continue } + "Which disk(s) would you like to use?" { send "sda\r"; exp_continue } + "How would you like to use it?" { send "sys\r"; exp_continue } + "Erase the above disk(s) and continue?" { send "y\r"; exp_continue } + "Installation is complete" { send "poweroff\r" } + } + interact + ' +} + +start_vde() { + usage start_vde && return + sudo sh <<- EOT + vde_switch -tap tap0 -sock /tmp/vde.ctl -daemon -mod 666 + sleep 1 + ip address add 10.0.2.2/24 dev tap0 + ip link set tap0 up + echo 1 > /proc/sys/net/ipv4/ip_forward + iptables -t nat -A POSTROUTING -s 10.0.2.0/24 -o eth0 -j MASQUERADE + iptables -t nat -A POSTROUTING -s 10.0.2.0/24 -o wlan0 -j MASQUERADE + EOT + #slirpvde --dhcp --daemon +} + +stop_vde() { + # killall slirpvde + sudo sh <<- EOT + iptables -t nat -D POSTROUTING -s 10.0.2.0/24 -o eth0 -j MASQUERADE + iptables -t nat -D POSTROUTING -s 10.0.2.0/24 -o wlan0 -j MASQUERADE + ip link set tap0 down + killall vde_switch + EOT +} + +start() { + usage 'start [-acd] name' 'start a virtual machine' && return + while getopts :acd opt; do + case $opt in + a) opta=1 ;; + c) boot=c ;; + d) boot=d ;; + esac + done + shift $((OPTIND -1)) + [ "$1" ] || die 'start failed: name missing' + cd "$dir/$1" || die "start failed: invalid directory $dir/$1" + is_running "$1" || start_qemu "$1" + [ "$opta" ] && console "$1" +} + +start_qemu() ( + opt='-nographic -cpu max' + [ "$sys" = Linux ] && opt="$opt -enable-kvm" + . ./config || die "could not source $PWD/config" + exec 1>>qemu.log 2>&1 + date +%F_%T + set -x + screen -S "vm!$1!" -d -m qemu-system-$arch $opt \ + ${smp+-smp $smp} \ + ${ram+-m $ram} \ + ${mac+-net nic,macaddr=$mac,model=virtio-net-pci} -net vde \ + ${hdd+-drive file="$hdd",if=virtio,media=disk,format=raw} \ + ${iso+-drive file="$iso",if=virtio,media=cdrom,format=raw} \ + ${boot+-boot $boot} +) + +stop() { + usage 'stop name' 'stop a virtual machine' && return + is_running "$1" && kill "$(pidof "$1")" +} + +usage() { + case $Opth in + 1) printf " %-34s %s\n" "$1" "$2" ;; + 2) printf "$0 $1\n\t$2\n" ;; + *) return 1 ;; + esac +} + +version() { + usage 'version' 'Print version' && return + echo "$version" +} + +Cmdlist='console create help init_alpine_iso ls pidof setup_alpine start start_vde stop version' +[ "$1" ] && C=$1 && shift 1 || { help; exit 1; } +#for c in $Cmdlist; do +# case $c in +# ("$C") cmd=$c; break ;; +# ("$C"*) [ "$cmd" ] && die "ambiguous command $C" || cmd=$c ;; +# esac +#done +cmd=$C +[ "$cmd" ] || { help; exit 1; } && $cmd "$@" |
