diff --git a/dom0/qvm-core/qubes.py b/dom0/qvm-core/qubes.py index 41f09d09..67799830 100755 --- a/dom0/qvm-core/qubes.py +++ b/dom0/qvm-core/qubes.py @@ -385,7 +385,7 @@ class QubesVm(object): self._set_netvm(new_netvm) def _set_netvm(self, new_netvm): - if self.is_running() and not new_netvm.is_running(): + if self.is_running() and new_netvm is not None and not new_netvm.is_running(): raise QubesException("Cannot dynamically attach to stopped NetVM") if self.netvm is not None: self.netvm.connected_vms.pop(self.qid) @@ -2266,7 +2266,7 @@ class QubesHVm(QubesVm): format(self.private_img) f_private = open(self.private_img, "w") f_private.truncate(default_hvm_private_img_size) - f_root.close() + f_private.close() return True diff --git a/dom0/qvm-core/qubesutils.py b/dom0/qvm-core/qubesutils.py index 957d01a2..e1c4a808 100644 --- a/dom0/qvm-core/qubesutils.py +++ b/dom0/qvm-core/qubesutils.py @@ -93,6 +93,8 @@ def print_stdout(text): def print_stderr(text): print >> sys.stderr, (text) +###### Block devices ######## + def block_devid_to_name(devid): major = devid / 256 minor = devid % 256 @@ -203,10 +205,11 @@ def block_list(vm = None, system_disks = False): else: vm_list = xs.ls('', '/local/domain') + xs_trans = xs.transaction_start() devices_list = {} for xid in vm_list: - vm_name = xs.read('', '/local/domain/%s/name' % xid) - vm_devices = xs.ls('', '/local/domain/%s/qubes-block-devices' % xid) + vm_name = xs.read(xs_trans, '/local/domain/%s/name' % xid) + vm_devices = xs.ls(xs_trans, '/local/domain/%s/qubes-block-devices' % xid) if vm_devices is None: continue for device in vm_devices: @@ -215,9 +218,9 @@ def block_list(vm = None, system_disks = False): print >> sys.stderr, "Invalid device name in VM '%s'" % vm_name continue - device_size = xs.read('', '/local/domain/%s/qubes-block-devices/%s/size' % (xid, device)) - device_desc = xs.read('', '/local/domain/%s/qubes-block-devices/%s/desc' % (xid, device)) - device_mode = xs.read('', '/local/domain/%s/qubes-block-devices/%s/mode' % (xid, device)) + device_size = xs.read(xs_trans, '/local/domain/%s/qubes-block-devices/%s/size' % (xid, device)) + device_desc = xs.read(xs_trans, '/local/domain/%s/qubes-block-devices/%s/desc' % (xid, device)) + device_mode = xs.read(xs_trans, '/local/domain/%s/qubes-block-devices/%s/mode' % (xid, device)) if device_size is None or device_desc is None or device_mode is None: print >> sys.stderr, "Missing field in %s device parameters" % device @@ -245,13 +248,16 @@ def block_list(vm = None, system_disks = False): "vm": vm_name, "device":device, "size":int(device_size), "desc":device_desc, "mode":device_mode} + xs.transaction_end(xs_trans) return devices_list def block_check_attached(backend_vm, device, backend_xid = None): if backend_xid is None: backend_xid = backend_vm.xid - vm_list = xs.ls('', '/local/domain/%d/backend/vbd' % backend_xid) + xs_trans = xs.transaction_start() + vm_list = xs.ls(xs_trans, '/local/domain/%d/backend/vbd' % backend_xid) if vm_list is None: + xs.transaction_end(xs_trans) return None device_majorminor = None try: @@ -260,10 +266,10 @@ def block_check_attached(backend_vm, device, backend_xid = None): # Unknown devices will be compared directly - perhaps it is a filename? pass for vm_xid in vm_list: - for devid in xs.ls('', '/local/domain/%d/backend/vbd/%s' % (backend_xid, vm_xid)): + for devid in xs.ls(xs_trans, '/local/domain/%d/backend/vbd/%s' % (backend_xid, vm_xid)): (tmp_major, tmp_minor) = (0, 0) - phys_device = xs.read('', '/local/domain/%d/backend/vbd/%s/%s/physical-device' % (backend_xid, vm_xid, devid)) - dev_params = xs.read('', '/local/domain/%d/backend/vbd/%s/%s/params' % (backend_xid, vm_xid, devid)) + phys_device = xs.read(xs_trans, '/local/domain/%d/backend/vbd/%s/%s/physical-device' % (backend_xid, vm_xid, devid)) + dev_params = xs.read(xs_trans, '/local/domain/%d/backend/vbd/%s/%s/params' % (backend_xid, vm_xid, devid)) if phys_device and phys_device.find(':'): (tmp_major, tmp_minor) = phys_device.split(":") tmp_major = int(tmp_major, 16) @@ -283,7 +289,9 @@ def block_check_attached(backend_vm, device, backend_xid = None): (device_majorminor is None and dev_params == device): vm_name = xl_ctx.domid_to_name(int(vm_xid)) frontend = block_devid_to_name(int(devid)) + xs.transaction_end(xs_trans) return {"xid":int(vm_xid), "frontend": frontend, "devid": int(devid), "vm": vm_name} + xs.transaction_end(xs_trans) return None def block_attach(vm, backend_vm, device, frontend=None, mode="w", auto_detach=False): @@ -334,6 +342,93 @@ def block_detach(vm, frontend = "xvdi", vm_xid = None): xl_cmd = [ '/usr/sbin/xl', 'block-detach', str(vm_xid), str(frontend)] subprocess.check_call(xl_cmd) +####### QubesWatch ###### + +def only_in_first_list(l1, l2): + ret=[] + for i in l1: + if not i in l2: + ret.append(i) + return ret + +class QubesWatch(object): + class WatchType(object): + def __init__(self, fn, param): + self.fn = fn + self.param = param + + def __init__(self): + self.xs = xen.lowlevel.xs.xs() + self.watch_tokens_block = {} + self.watch_tokens_vbd = {} + self.block_callback = None + self.domain_callback = None + self.xs.watch('@introduceDomain', QubesWatch.WatchType(self.domain_list_changed, None)) + self.xs.watch('@releaseDomain', QubesWatch.WatchType(self.domain_list_changed, None)) + + def setup_block_watch(self, callback): + old_block_callback = self.block_callback + self.block_callback = callback + if old_block_callback is not None and callback is None: + # remove watches + self.update_watches_vbd([]) + self.update_watches_block([]) + else: + # possibly add watches + self.domain_list_changed(None) + + def setup_domain_watch(self, callback): + self.domain_callback = callback + + def get_block_key(self, xid): + return '/local/domain/%s/qubes-block-devices' % xid + + def get_vbd_key(self, xid): + return '/local/domain/%s/device/vbd' % xid + + def update_watches_block(self, xid_list): + for i in only_in_first_list(xid_list, self.watch_tokens_block.keys()): + #new domain has been created + watch = QubesWatch.WatchType(self.block_callback, i) + self.watch_tokens_block[i] = watch + self.xs.watch(self.get_block_key(i), watch) + for i in only_in_first_list(self.watch_tokens_block.keys(), xid_list): + #domain destroyed + self.xs.unwatch(self.get_block_key(i), self.watch_tokens_block[i]) + self.watch_tokens_block.pop(i) + + def update_watches_vbd(self, xid_list): + for i in only_in_first_list(xid_list, self.watch_tokens_vbd.keys()): + #new domain has been created + watch = QubesWatch.WatchType(self.block_callback, i) + self.watch_tokens_block[i] = watch + self.xs.watch(self.get_vbd_key(i), watch) + for i in only_in_first_list(self.watch_tokens_vbd.keys(), xid_list): + #domain destroyed + self.xs.unwatch(self.get_vbd_key(i), self.watch_tokens_vbd[i]) + self.watch_tokens_vbd.pop(i) + + def domain_list_changed(self, param): + curr = self.xs.ls('', '/local/domain') + if curr == None: + return + if self.domain_callback: + self.domain_callback() + if self.block_callback: + self.update_watches_block(curr) + self.update_watches_vbd(curr) + + def watch_single(self): + result = self.xs.read_watch() + token = result[1] + token.fn(token.param) + + def watch_loop(self): + while True: + self.watch_single() + +######## Backups ######### + def get_disk_usage(file_or_dir): if not os.path.exists(file_or_dir): return 0 diff --git a/misc/qubes_core.modules b/misc/qubes_core.modules index 77267064..9f39e795 100755 --- a/misc/qubes_core.modules +++ b/misc/qubes_core.modules @@ -1 +1,2 @@ modprobe evtchn 2>/dev/null || modprobe xen-evtchn +modprobe xen-blkback 2> /dev/null || modprobe blkbk