ia64/xen-unstable

view tools/examples/block @ 10949:4c2fab8f8c34

[qemu] Use xenstore to configure ioemu block devices.
- read ioemu block device config from xenstore
- don't require the ioemu: prefix on block devices any longer
- allow change of media associated with cdrom drives
- replace cdrom= option by :cdrom suffix on regular block device config:
'file:/root/mytest.iso,hdc:cdrom,r'
- don't create default cdrom drive anymore - to create default empty
cdrom drive use: ',hdc:cdrom,r'

Signed-off-by: Christian Limpach <Christian.Limpach@xensource.com>
author chris@kneesaa.uk.xensource.com
date Thu Aug 03 18:28:29 2006 +0100 (2006-08-03)
parents d76ef15c9c95
children 4d1af7ebf676
line source
1 #!/bin/sh
3 dir=$(dirname "$0")
4 . "$dir/block-common.sh"
6 expand_dev() {
7 local dev
8 case $1 in
9 /*)
10 dev=$1
11 ;;
12 *)
13 dev=/dev/$1
14 ;;
15 esac
16 echo -n $dev
17 }
20 ##
21 # canonicalise_mode mode
22 #
23 # Takes the given mode, which may be r, w, ro, rw, w!, or rw!, or variations
24 # thereof, and canonicalises them to one of
25 #
26 # 'r': perform checks for a new read-only mount;
27 # 'w': perform checks for a read-write mount; or
28 # '!': perform no checks at all.
29 #
30 canonicalise_mode()
31 {
32 local mode="$1"
34 if ! expr index "$mode" 'w' >/dev/null
35 then
36 echo 'r'
37 elif ! expr index "$mode" '!' >/dev/null
38 then
39 echo 'w'
40 else
41 echo '!'
42 fi
43 }
46 ##
47 # check_sharing device mode
48 #
49 # Check whether the device requested is already in use. To use the device in
50 # read-only mode, it may be in use in read-only mode, but may not be in use in
51 # read-write anywhere at all. To use the device in read-write mode, it must
52 # not be in use anywhere at all.
53 #
54 # Prints one of
55 #
56 # 'local': the device may not be used because it is mounted in the current
57 # (i.e. the privileged domain) in a way incompatible with the
58 # requested mode;
59 # 'guest': the device may not be used because it already mounted by a guest
60 # in a way incompatible with the requested mode; or
61 # 'ok': the device may be used.
62 #
63 check_sharing()
64 {
65 local dev="$1"
66 local mode="$2"
68 local devmm=$(device_major_minor "$dev")
69 local file
71 if [ "$mode" == 'w' ]
72 then
73 toskip="^$"
74 else
75 toskip="^[^ ]* [^ ]* [^ ]* ro[, ]"
76 fi
78 for file in $(cat /proc/mounts | grep -v "$toskip" | cut -f 1 -d ' ')
79 do
80 if [ -e "$file" ]
81 then
82 local d=$(device_major_minor "$file")
84 if [ "$d" == "$devmm" ]
85 then
86 echo 'local'
87 return
88 fi
89 fi
90 done
92 local base_path="$XENBUS_BASE_PATH/$XENBUS_TYPE"
93 for dom in $(xenstore-list "$base_path")
94 do
95 for dev in $(xenstore-list "$base_path/$dom")
96 do
97 d=$(xenstore_read_default "$base_path/$dom/$dev/physical-device" "")
99 if [ "$d" == "$devmm" ]
100 then
101 if [ "$mode" == 'w' ]
102 then
103 if ! same_vm $dom
104 then
105 echo 'guest'
106 return
107 fi
108 else
109 local m=$(xenstore_read "$base_path/$dom/$dev/mode")
110 m=$(canonicalise_mode "$m")
112 if [ "$m" == 'w' ]
113 then
114 if ! same_vm $dom
115 then
116 echo 'guest'
117 return
118 fi
119 fi
120 fi
121 fi
122 done
123 done
125 echo 'ok'
126 }
129 same_vm()
130 {
131 local otherdom="$1"
132 # Note that othervm can be MISSING here, because Xend will be racing with
133 # the hotplug scripts -- the entries in /local/domain can be removed by
134 # Xend before the hotplug scripts have removed the entry in
135 # /local/domain/0/backend/. In this case, we want to pretend that the
136 # VM is the same as FRONTEND_UUID, because that way the 'sharing' will be
137 # allowed.
138 local othervm=$(xenstore_read_default "/local/domain/$otherdom/vm" \
139 "$FRONTEND_UUID")
141 [ "$FRONTEND_UUID" == "$othervm" ]
142 }
145 ##
146 # check_device_sharing dev mode
147 #
148 # Perform the sharing check for the given physical device and mode.
149 #
150 check_device_sharing()
151 {
152 local dev="$1"
153 local mode=$(canonicalise_mode "$2")
154 local result
156 if [ "$mode" == '!' ]
157 then
158 return 0
159 fi
161 result=$(check_sharing "$dev" "$mode")
163 if [ "$result" != 'ok' ]
164 then
165 do_ebusy "Device $dev is mounted " "$mode" "$result"
166 fi
167 }
170 ##
171 # check_device_sharing file dev mode
172 #
173 # Perform the sharing check for the given file mounted through the given
174 # loopback interface, in the given mode.
175 #
176 check_file_sharing()
177 {
178 local file="$1"
179 local dev="$2"
180 local mode="$3"
182 result=$(check_sharing "$dev" "$mode")
184 if [ "$result" != 'ok' ]
185 then
186 do_ebusy "File $file is loopback-mounted through $dev,
187 which is mounted " "$mode" "$result"
188 fi
189 }
192 ##
193 # do_ebusy prefix mode result
194 #
195 # Helper function for check_device_sharing check_file_sharing, calling ebusy
196 # with an error message constructed from the given prefix, mode, and result
197 # from a call to check_sharing.
198 #
199 do_ebusy()
200 {
201 local prefix="$1"
202 local mode="$2"
203 local result="$3"
205 if [ "$result" == 'guest' ]
206 then
207 dom='a guest '
208 when='now'
209 else
210 dom='the privileged '
211 when='by a guest'
212 fi
214 if [ "$mode" == 'w' ]
215 then
216 m1=''
217 m2=''
218 else
219 m1='read-write '
220 m2='read-only '
221 fi
223 release_lock "block"
224 ebusy \
225 "${prefix}${m1}in ${dom}domain,
226 and so cannot be mounted ${m2}${when}."
227 }
230 t=$(xenstore_read_default "$XENBUS_PATH/type" 'MISSING')
232 case "$command" in
233 add)
234 phys=$(xenstore_read_default "$XENBUS_PATH/physical-device" 'MISSING')
235 if [ "$phys" != 'MISSING' ]
236 then
237 # Depending upon the hotplug configuration, it is possible for this
238 # script to be called twice, so just bail.
239 exit 0
240 fi
242 if [ -n "$t" ]
243 then
244 p=$(xenstore_read "$XENBUS_PATH/params")
245 mode=$(xenstore_read "$XENBUS_PATH/mode")
246 fi
248 case $t in
249 phy)
250 dev=$(expand_dev $p)
251 FRONTEND_ID=$(xenstore_read "$XENBUS_PATH/frontend-id")
252 FRONTEND_UUID=$(xenstore_read_default \
253 "/local/domain/$FRONTEND_ID/vm" 'unknown')
254 claim_lock "block"
255 check_device_sharing "$dev" "$mode"
256 write_dev "$dev"
257 release_lock "block"
258 exit 0
259 ;;
261 file)
262 # Canonicalise the file, for sharing check comparison, and the mode
263 # for ease of use here.
264 file=$(readlink -f "$p") || fatal "$p does not exist."
265 mode=$(canonicalise_mode "$mode")
267 claim_lock "block"
269 if [ "$mode" == 'w' ] && ! stat "$file" -c %A | grep -q w
270 then
271 release_lock "block"
272 ebusy \
273 "File $file is read-only, and so I will not
274 mount it read-write in a guest domain."
275 fi
277 loopdev=''
278 for dev in /dev/loop*
279 do
280 if [ ! -b "$dev" ]
281 then
282 continue
283 fi
285 f=$(losetup "$dev" 2>/dev/null) || f=''
287 if [ "$f" ]
288 then
289 # $dev is in use. Check sharing.
290 if [ "$mode" == '!' ]
291 then
292 continue
293 fi
295 f=$(echo "$f" | sed -e 's/.*(\(.*\)).*/\1/g')
297 # $f is the filename, as read from losetup, but the loopback
298 # driver truncates filenames at 64 characters, so we need to go
299 # trawling through the store if it's longer than that. Truncation
300 # is indicated by an asterisk at the end of the filename.
301 if expr index "$f" '*' >/dev/null
302 then
303 found=""
304 for dom in $(xenstore-list "$XENBUS_BASE_PATH")
305 do
306 for domdev in $(xenstore-list "$XENBUS_BASE_PATH/$dom")
307 do
308 d=$(xenstore_read_default \
309 "$XENBUS_BASE_PATH/$dom/$domdev/node" "")
310 if [ "$d" == "$dev" ]
311 then
312 f=$(xenstore_read "$XENBUS_BASE_PATH/$dom/$domdev/params")
313 found=1
314 break 2
315 fi
316 done
317 done
319 if [ ! "$found" ]
320 then
321 # This loopback device is in use by someone else, so skip it.
322 log debug "Loopback sharing check skips device $dev."
323 continue
324 fi
325 fi
327 # Canonicalise the filename for the comparison.
329 # I have seen this readlink fails because the filename given by
330 # losetup is only the basename. This cannot happen when the loop
331 # device is set up through this script, because file is
332 # canonicalised above, but it may happen when loop devices are set
333 # up some other way. This readlink may also conceivably fail if
334 # the file backing this loop device has been removed.
336 # For maximum safety, in the case that $f does not resolve, we
337 # assume that $file and $f are in the same directory.
339 # If you create a loopback filesystem, remove it and continue to
340 # run on it, and then create another file with the same name, then
341 # this check will block that -- don't do that.
343 # If you create loop devices through some other mechanism, use
344 # relative filenames, and then use the same filename through this
345 # script, then this check will block that -- don't do that either.
347 f=$(readlink -f "$f" || echo $(dirname "$file")/$(basename "$f"))
350 if [ "$f" == "$file" ]
351 then
352 check_file_sharing "$file" "$dev" "$mode"
353 fi
354 else
355 # $dev is not in use, so we'll remember it for use later; we want
356 # to finish the sharing check first.
358 if [ "$loopdev" == '' ]
359 then
360 loopdev="$dev"
361 fi
362 fi
363 done
365 if [ "$loopdev" == '' ]
366 then
367 fatal 'Failed to find an unused loop device'
368 fi
370 do_or_die losetup "$loopdev" "$file"
371 xenstore_write "$XENBUS_PATH/node" "$loopdev"
372 write_dev "$loopdev"
373 release_lock "block"
374 exit 0
375 ;;
377 "")
378 claim_lock "block"
379 success
380 echo happy gun \"$t\" >>/tmp/block.$$
381 release_lock "block"
382 ;;
383 esac
384 ;;
386 remove)
387 case $t in
388 phy)
389 exit 0
390 ;;
392 file)
393 node=$(xenstore_read "$XENBUS_PATH/node")
394 losetup -d "$node"
395 exit 0
396 ;;
398 "")
399 exit 0
400 ;;
401 esac
402 ;;
404 esac
406 # If we've reached here, $t is neither phy nor file, so fire a helper script.
407 [ -x /etc/xen/scripts/block-"$t" ] && \
408 /etc/xen/scripts/block-"$t" "$command" $node