1 #! /bin/bash |
1 #! /bin/bash |
2 # status: in development |
|
3 # $Id$ |
2 # $Id$ |
4 # $URL$ |
3 # $URL$ |
5 # Heiko Schlittermann |
4 # status: in development |
|
5 # 2008 Heiko Schlittermann |
6 |
6 |
7 # Structure of virtual machine directories |
7 # Structure of virtual machine directories |
8 # $DIR/ (see below, hard coded into this script) |
8 # $BASE (see below, hard coded into this script) |
9 # |
9 # | |
10 # $DIR/<vm1>/conv/cmdline # KVM options (ro) |
10 # +--<vm1> |
11 # # example: |
11 # | +--conf |
12 # # -hda rootfs.img |
12 # | | +--options |
13 # # Note: paths are relative to $DIR/<vm1> |
13 # | | +--id |
14 # id # KVM id (ro) |
14 # | +--var |
15 # # Note: has to be unique among all VMs |
15 # | | +--pid |
16 # $DIR/<vm1>/var/pid |
16 # | | +--running_options |
17 # frozen |
17 # | +--<image1> |
18 # .cmdline |
18 # | +-- .... |
19 # $DIR/<vm1>/<image> |
19 # | +--<imageN> |
20 # |
20 # | |
21 # $DIR/<vm2>/... |
21 # +--<vm2> |
22 # ... ... |
22 # | .... |
23 # |
23 # +--<vmN> |
24 # <vmN>/... |
|
25 |
24 |
26 DIR=/kvm |
|
27 |
25 |
28 action="${1?}"; shift |
26 action="${1?}"; shift |
29 vm="$1"; shift |
27 vm="$1" ; shift |
30 |
28 |
31 # no user service able parts below |
29 # no user service able parts below |
32 |
30 |
33 unset ${!LC_*} LANG |
|
34 unset CDPATH |
|
35 PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin |
31 PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin |
36 |
32 unset ${!LC_*} LANG CDPATH |
37 #set +m # switch off job control |
|
38 |
33 |
39 ME=$(basename $0) |
34 ME=$(basename $0) |
|
35 BASE=/kvm |
40 KVM=$(command -v kvm) |
36 KVM=$(command -v kvm) |
41 NC=$(command -v nc) |
37 NC=$(command -v nc) |
42 FUSER=$(command -v fuser) |
38 FUSER=$(command -v fuser) |
43 VNCVIEWER=$(command -v vncviewer) |
39 VNCVIEWER=$(command -v vncviewer) |
|
40 TIMEOUT=30 |
44 |
41 |
45 die() { echo $ME: "$@" >&2; exit 1; } |
42 |
46 warn() { echo $ME: "$@" >&2; } |
43 for f in "/etc/$ME.conf" ~/.$ME; do test -r "$f" && source "$f"; done |
|
44 |
|
45 |
|
46 die() { echo $ME: "$@" >&2; exit 1; } |
|
47 warn() { echo $ME: "$@" >&2; } |
|
48 silent() { "$@" &>/dev/null; } |
|
49 |
|
50 |
|
51 unique_ids() { |
|
52 local dups=$(cat $BASE/*/conf/id 2>/dev/null | sort -n | uniq -c \ |
|
53 | egrep -v '^[[:space:]]+1 ' | tr -s ' ' | cut -f2 -d:) |
|
54 test "$dups" || return 0 |
|
55 ERR="$dups" && return 1 |
|
56 } |
|
57 convert_dirs() { |
|
58 for dir in $BASE/*; do |
|
59 test -d $dir/kvm || test -d $dir/conf || continue |
|
60 local old=$dir/kvm |
|
61 local new=$dir/conf |
|
62 test -d $old && mv $old $new |
|
63 test -f $new/cmdline && mv $new/cmdline $new/options |
|
64 done |
|
65 } |
47 |
66 |
48 ### MAIN |
67 ### MAIN |
49 |
68 |
50 # sanity |
69 # sanity |
51 test "$NC" || die "need nc (netcat)" |
70 test -x "$NC" || die "need nc (netcat)" |
52 test "$KVM" || die "need kvm" |
71 test -x "$KVM" || die "need kvm/qemu/kqemu" |
53 test "$FUSER" || die "need fuser" |
72 test -x "$FUSER" || die "need fuser" |
|
73 test -d "$BASE" || die "base dir $BASE does not exist" |
|
74 test -d "$BASE/$vm" || die "vm $vm does not exist" |
54 |
75 |
55 dups=$(cat $DIR/*/conf/id | sort -n | uniq -c \ |
76 unique_ids || die "duplicate ids (id: $ERR)" |
56 | egrep -v '^[[:space:]]+1 ' | tr -s ' ' | cut -f2 -d:) |
77 convert_dirs |
57 test "$dups" && die "duplicate ids (id: $dups)" |
|
58 |
78 |
59 for vm in ${vm:-$(echo $DIR/*)}; do |
79 trap 'test "$TMP" && rm -fr $TMP' EXIT |
|
80 |
|
81 for vm in ${vm:-$(echo $BASE/*)}; do |
60 vm=$(basename $vm) |
82 vm=$(basename $vm) |
61 conf=$DIR/$vm/conf |
83 conf=$BASE/$vm/conf |
62 var=$DIR/$vm/var |
84 var=$BASE/$vm/var |
63 |
85 |
64 # silent conversion |
86 tmp=$(mktemp -d) |
65 test -d $DIR/$vm/kvm && ! test -d $DIR/$vm/conf \ |
87 TMP="$TMP $tmp" |
66 && mv $DIR/$vm/kvm $DIR/$vm/conf |
|
67 |
88 |
68 test -d $vm || { warn "no such file or directory: $vm"; continue; } |
89 test -d $conf || continue |
69 test -d $conf || continue |
90 test -f $conf/id || { warn "need id file (vm: $vm)" && continue; } |
70 test -f $conf/id || { warn "need id file (vm: $vm)" && continue; } |
91 test -f $conf/options || { warn "need options file (vm: $vm)" && continue; } |
71 test -f $conf/cmdline || { warn "need cmdline file (vm: $vm)" && continue; } |
|
72 test -d $var || mkdir $var || exit 2 |
92 test -d $var || mkdir $var || exit 2 |
73 |
93 |
74 id=$(cat $conf/id) |
94 id=$(cat $conf/id) |
75 monitor=$((4000 + id)) |
95 port=$((4000 + id)) |
76 pid=$(cat $var/pid 2>/dev/null) |
96 pid=$(cat $var/pid 2>/dev/null) |
77 |
97 |
78 case "$action" in |
98 case "$action" in |
79 |
99 |
80 start) |
100 start) |
81 echo "starting $vm" |
101 echo "starting $vm" |
82 cmdline="$(grep -v '^#' $conf/cmdline) \ |
102 options="$(grep -v '^#' $conf/options) \ |
83 -pidfile var/pid \ |
103 -pidfile var/pid \ |
84 -name "$vm" \ |
104 -name "$vm" \ |
85 -vnc :$id \ |
105 -vnc :$id \ |
86 -usb -usbdevice tablet \ |
106 -monitor tcp:localhost:$port,server,nowait \ |
87 -monitor tcp:localhost:$monitor,server,nowait \ |
107 $@" |
88 -daemonize \ |
108 options=$(echo "" $options) # shorten it |
89 $@" |
109 echo "$options" > $var/running_options |
90 echo "$cmdline" > $var/.cmdline |
110 test -f $var/frozen && options="$options $(cat $var/frozen)" |
91 test -f $var/frozen && cmdline="$cmdline $(cat $var/frozen)" |
|
92 |
111 |
93 ( cd $DIR/$vm && $KVM $cmdline ) |
112 if silent fuser $var/pid; then |
94 pid=$(cat $var/pid) |
113 echo "running, pid: $pid, display :$id, monitor tcp:127.0.0.1:$port" |
95 echo "started, pid: $pid, display :$id, monitor tcp:127.0.0.1:$monitor" |
114 else |
|
115 ( cd $BASE/$vm && $KVM $options -daemonize ) |
|
116 pid=$(cat $var/pid 2>/dev/null) |
|
117 test "$pid" || { warn "serious problem ($vm), no pid" && continue; } |
|
118 echo "started, pid: $pid, display :$id, monitor tcp:127.0.0.1:$port" |
|
119 fi |
96 |
120 |
97 test "$DISPLAY" && test "$VNCVIEWER" && test -t \ |
121 test "$DISPLAY" && test "$VNCVIEWER" && test -t \ |
98 && ( $VNCVIEWER :$id &>/dev/null </dev/null & ) |
122 && ( silent exec $VNCVIEWER :$id </dev/null & ) |
99 |
123 |
100 ;; |
124 ;; |
101 stop) echo "stopping (powerdown) $vm" |
125 stop) |
102 #{ |
126 { |
103 { echo system_powerdown | $NC localhost $monitor; } & pid0=$! |
127 echo "stopping (powerdown) $vm" |
104 #( sleep 30; kill $pid0 &>/dev/null ) & pid1=$! |
128 a=$tmp/a; b=$tmp/b; mkfifo $a $b |
105 echo $pid0 |
129 ( $NC localhost $port 0<>$a 1<>$b ) & pid0=$! |
106 wait $pid0 |
130 read <$b # we should not send anything before we got the first line |
107 echo $? |
131 echo -e 'system_powerdown\n' >$a |
108 #kill $pid1 2>/dev/null |
132 ( cat <$b & sleep $TIMEOUT; silent kill $! $pid0 ) & pid1=$! |
109 #kill -0 $pid 2>/dev/null && $0 kill $vm |
133 wait $pid0 |
110 #} & |
134 kill $pid1 2>/dev/null |
111 ;; |
135 silent fuser $var/pid && $0 quit $vm |
112 quit) echo "quit $vm" |
136 } & |
113 $FUSER -k $monitor/tcp |
137 ;; |
114 echo quit | $NC 127.0.0.1 $monitor & |
138 quit) |
115 ;; |
139 { |
116 kill) echo "killing $vm" |
140 echo "quit $vm" |
117 warn "killing vm $vm (id: $id, pid: $pid)" |
141 a=$tmp/a; b=$tmp/b; mkfifo $a $b |
118 kill $pid & |
142 ( $NC localhost $port 0<>$a 1<>$b ) & pid0=$! |
119 ;; |
143 read <$b |
|
144 echo -e 'quit\n' >$a |
|
145 ( cat <$b & sleep $TIMEOUT; silent kill $! $0 ) & pid1=$! |
|
146 wait $pid0 |
|
147 kill $pid1 2>/dev/null |
|
148 silent fuser $var/pid && $0 kill $vm |
|
149 } & |
|
150 ;; |
|
151 kill) |
|
152 echo "killing $vm" |
|
153 kill $pid |
|
154 ;; |
120 freeze) echo "freezing $vm" |
155 freeze) echo "freezing $vm" |
121 fuser -k $monitor/tcp |
156 fuser -k $port/tcp |
122 echo -e "savevm HIBERNATE\nquit" | $NC localhost $monitor & |
157 echo -e "savevm HIBERNATE\nquit" | $NC localhost $port & |
123 echo "-loadvm HIBERNATE" >$var/frozen |
158 echo "-loadvm HIBERNATE" >$var/frozen |
124 ;; |
159 ;; |
125 #status) echo "status $vm: " |
160 #status) echo "status $vm: " |
126 qemu) |
161 qemu) |
127 echo "DO *NOT* USE \"quit\", use ^C for exiting!" |
162 echo "DO *NOT* USE \"quit\", use ^C for exiting!" |
128 $NC localhost $monitor |
163 $NC localhost $port |
129 ;; |
164 ;; |
|
165 |
|
166 list) |
|
167 echo "$vm:" $(silent fuser $var/pid \ |
|
168 && echo "running (pid: $pid)" \ |
|
169 || echo "not running") |
|
170 ;; |
|
171 |
|
172 *) die "unknown command \"$action\"" |
|
173 ;; |
|
174 |
130 esac |
175 esac |
131 |
176 |
132 done |
177 done |
133 |
178 |
134 wait |
179 wait |