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
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
|
#! /usr/bin/env bash
# Generate systemd .service file for QEMU/KVM virtual machines.
#
# Normally the machines are run from a dedicated user account with its home
# directory containing all the relevant files (management scripts, images,
# configurations, and sockets). However, this can be overriden with the
# following options
#
# --home <dir>
# The virtual machines "home" directory. If unspecified, the user's home
# directory is assumed.
#
# --bin <dir>
# The virtual machines management scripts directory. If unspecified,
# <home>/bin is assumed. If specified as relative, then assumed relative
# to <home>.
#
# --etc <dir>
# The virtual machines configuration files directory. If unspecified,
# <home>/etc is assumed. If specified as relative, then assumed relative
# to <home>.
#
# --var <dir>
# The virtual machines image files directory. If unspecified, <home>/var is
# assumed. If specified as relative, then assumed relative to <home>.
#
# --run <dir>
# The virtual machines sockets directory. If unspecified, <home>/run is
# assumed. If specified as relative, then assumed relative to <home>.
#
# If <user> is unspecified, the current user is assumed. If <group> is
# unspecified, the user's primary group is assumed.
#
usage="usage: $0 [<options>] [<user>] [<group>]"
owd="$(pwd)"
trap "{ cd '$owd'; exit 1; }" ERR
set -o errtrace # Trap in functions.
function info () { echo "$*" 1>&2; }
function error () { info "$*"; exit 1; }
home=
bin=
etc=
var=
run=
while [ "$#" -gt 0 ]; do
case "$1" in
--home)
shift
home="$1"
shift
;;
--bin)
shift
bin="$1"
shift
;;
--etc)
shift
etc="$1"
shift
;;
--var)
shift
var="$1"
shift
;;
--run)
shift
run="$1"
shift
;;
*)
break
;;
esac
done
user="$1"
if [ -z "$user" ]; then
user="$(id -un)"
fi
group="$2"
if [ -z "$group" ]; then
group="$(id -un "$user")"
fi
if [ -z "$home" ]; then
home="$(eval echo ~$user)"
fi
function complete_dir () # <default> <home> <dir>
{
local r
if [ -z "$3" ]; then
r="$2/$1"
elif [ "${3:0:1}" != "/" ]; then
r="$2/$3"
else
r="$3"
fi
echo "$(realpath --no-symlinks --canonicalize-missing "$r")"
}
bin="$(complete_dir bin "$home" "$bin")"
etc="$(complete_dir etc "$home" "$etc")"
var="$(complete_dir var "$home" "$var")"
run="$(complete_dir run "$home" "$run")"
name="vm-$user"
file="$name@.service"
# Thinks that must be \-escaped:
#
# - $ (including in comments)
# - \ (e.g., in line continuations)
#
cat <<EOF >"$file"
# $file -- QEMU/KVM machine service template for systemd
#
# user: $user
# group: $group
# bin: $bin
# etc: $etc
# var: $var
# run: $run
#
# To install:
#
# sudo cp $file /etc/systemd/system/
# sudo chmod 644 /etc/systemd/system/$file
#
# cp ... $var/<machine>.img
# nano $etc/<machine>.conf # Specify RAM, CPU, TAP, MAC, etc.
#
# sudo systemctl start $name@<machine>
# sudo systemctl status $name@<machine>
# login-machine $run/<machine>-con.sock
# sudo systemctl stop $name@<machine>
#
# sudo systemctl enable $name@<machine>
[Unit]
Description=QEMU/KVM virtual machine %I
Wants=network-online.target
#After=network-online.target
After=multi-user.target
[Service]
User=$user
Group=$user
UMask=0007
WorkingDirectory=~
Environment=CPU=1
Environment=RAM=2G
# These MUST be specific in EnvironmentFile!
#
#Environment=TAP=
#Environment=MAC=
# Note that using variable expansion in EnvironmentFile does not work (at
# least not with systemd 229).
#
EnvironmentFile=$etc/%i.conf
# Note that the first word of ExecStart cannot contain variable expansions.
#
ExecStart=$bin/vm-start \\
--cpu \${CPU} \\
--ram \${RAM} \\
--tap \${TAP} \\
--mac \${MAC} \\
--pid $run/%i.pid \\
--monitor $run/%i-mon.sock \\
--console $run/%i-con.sock \\
$var/%i.img
ExecStop=$bin/vm-stop $run/%i.pid $run/%i-mon.sock
# This makes sure systemd waits for the ExecStart command to exit rather
# than killing it as soon as ExecStop exits (this is necessary since our
# vm-stop may exit just before vm-start).
#
KillMode=none
TimeoutStopSec=60
[Install]
WantedBy=multi-user.target
EOF
info "generated $file for"
info " user: $user"
info " group: $group"
info " bin: $bin"
info " etc: $etc"
info " var: $var"
info " run: $run"
|