summaryrefslogtreecommitdiff
path: root/bin/vm
blob: e8f7684a41636295f4678d600d0fd2def3e207f3 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
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
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
#!/bin/sh

# vm is a tool to manage and operate virtual machines.

vm_version='vm-0.1'

# TODO:
# - DONE: fetch, build and install vftool in .vm/vftool
# - setup a config file per VM
# - creation of hdd image
# - script install from CDROM iso:
#	- setup system, including static IP address
# 	- setup user account from host
# 	- setup ssh from host
# 	- time synchronization from host
#

unset CDPATH
export LC_ALL=C IFS=' 	
'

add() {
	usage 'add name' 'Add a new VM' && return
	[ -d "$dir/$1" ] && die "vm $1 already exists in $dir/$1"
	init_alpine || die "could not init alpine iso"
	mkdir -p "$dir/$1" && cd "$dir/$1" || die "add $1 failed"
	pwd
}

console() {
	usage 'console name' 'Attach a console to a VM' && return
	[ "$1" ] || die 'console: name is missing'
	cd "$dir/$1" || die 'console $1 failed'
	[ -f vftool.pid ] || die "vm $1 is not active"
	screen -r "$1"
}

del() {
	usage 'del name' 'Delete a VM' && return
	echo 'not implemented yet'
}

die() { [ "$1" ] && echo "$@" >&2; exit 1; }

help() {
	usage 'help' 'Print this help text' && return
        echo "$vm_version\nManage virtual machines\nUsage: vm command [options] [args]"
        Opth=1; for c in $Cmdlist; do $c; done
}

info() {
	usage 'info name' 'Print informations on a VM' && return
	echo 'not implemented yet'
}

# CAUTION: be careful to preserve tabs in the following Makefile template string.

alpine_makefile='# Generated by "vm". DO NOT EDIT.
# Check https://alpinelinux.org/downloads for possible upgrades
iso_url = https://dl-cdn.alpinelinux.org/alpine/v3.13/releases/aarch64/alpine-virt-3.13.2-aarch64.iso
iso = $(notdir $(iso_url))
isoinfo ?= /opt/homebrew/bin/isoinfo

all: initrd vmlinux

initrd: $(iso) $(isoinfo)
	isoinfo -i $(iso) -J -x /boot/initramfs-virt > $@

vmlinux: $(iso) $(isoinfo)
	isoinfo -i $(iso) -J -x /boot/vmlinuz-virt | gunzip > $@

$(iso):
	curl -LO $(iso_url) || { rm -f $@; false; }
	ln -sf $(iso) iso

$(isoinfo):
	brew install cdrtools
'

init_alpine() {
	mkdir -p "$dir/alpine-iso"  && cd "$dir/alpine-iso" || die 'init alpine failed'
	[ -f 'Makefile' ] || echo "$alpine_makefile" > Makefile
	make -s all
}

init_vftool() {
	[ -x "$dir/vftool" ] && return
	cd '/tmp' &&
	git clone --depth=1 'https://github.com/evansm7/vftool' &&
	cd 'vftool' &&
	make &&
	cp 'build/vftool' "$dir/vftool"
	rm -rf '/tmp/vftool'
}

log() {
	usage 'log name' 'print logs of a VM' && return
	[ "$1" ] || die "log failed: name missing"
	cd "$dir/$1" || die "log $1 failed"
	cat vftool.log.old vftool.log 2>/dev/null
}

ls() {
	usage 'ls' 'list VMs' && return
	[ -d "$dir" ] && cd "$dir" || return
	for i in */; do
		i=${i%/}
		[ -f "$i/vftool.pid" ] && state=active || state=stopped
		printf "%-20s %s\n" "$i" "$state"
	done
}

start() {
	usage 'start [-a][-c vm] name' 'Start a VM' && return
	while getopts :ac: opt; do
		case $opt in
		(a) attach=1 ;;
		(c) from=$OPTARG ;;
		(*) Opth=1 start; exit;;
		esac
	done
	shift $((OPTIND - 1))

	[ "$1" ] || die "start failed: name missing"
	init_vftool
	cd "$dir/$1" || die "start $1 failed"
	[ -f vftool.pid ] && die "Error: process $(cat vftool.pid) is active or $PWD/vftool.pid should be removed"
	start_vm &
}

start_vm() (
        trap 'rm -f vftool.pid' EXIT
        [ -f vftool.log ] && cat vftool.log >> vftool.log.old
        "$dir/vftool" -k vmlinux -i initrd -c iso >vftool.log 2>&1 & sleep 1
	exec 1>>vftool.log 2>&1
        echo "$!" >vftool.pid
        screen -S "${PWD##*/}" -d -m "$(grep -om 1 '\/dev\/tty.*' vftool.log)"
        wait
)

stop() {
	usage 'stop name' 'Stop a VM' && return
	[ "$1" ] || die 'stop: name missing'
	cd "$dir/$1" || die 'stop $1 failed'
	[ -f vftool.pid ] || die "stop: vm $1 is not active"
	kill $(cat vftool.pid)
}

usage() { [ "$Opth" ] && printf " %-34s %s\n" "$1" "$2"; }

version() {
	usage 'version' 'Print version' && return
	echo "$vm_version"
}

# Main starts here.
dir="$HOME/.vm"
Cmdlist='add console del info help ls log start 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" ] || { help; exit 1; } && $cmd "$@"