#!/usr/bin/python2 # -*- coding: utf8 -*- # # The Qubes OS Project, http://www.qubes-os.org # # Copyright (C) 2014 Marek Marczykowski-Górecki # # This program is free software; you can redistribute it and/or # modify it under the terms of the GNU General Public License # as published by the Free Software Foundation; either version 2 # of the License, or (at your option) any later version. # # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License # along with this program; if not, write to the Free Software # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. import threading import time from PyQt4.QtGui import QMessageBox from qubes import qubesutils class QubesBlockDevicesManager(): def __init__(self, qvm_collection): self.qvm_collection = qvm_collection self.attached_devs = {} self.free_devs = {} self.current_blk = {} self.current_attached = {} self.devs_changed = False self.last_update_time = time.time() self.blk_state_changed = True self.msg = [] self.check_counter = 0 self.blk_lock = threading.Lock() self.tray_message_func = None self.update() def block_devs_event(self, xid): now = time.time() #don't update more often than 1/10 s if now - self.last_update_time >= 0.1: self.last_update_time = now self.blk_lock.acquire() self.blk_state_changed = True self.blk_lock.release() def check_for_updates(self): self.blk_lock.acquire() ret = (self.blk_state_changed, self.msg) if self.blk_state_changed == True: self.check_counter += 1 self.update() ret = (self.blk_state_changed, self.msg) #let the update last for 3 manager-update cycles if self.check_counter == 3: self.check_counter = 0 self.blk_state_changed = False self.msg = [] self.blk_lock.release() return ret def update(self): blk = qubesutils.block_list() for b in blk: att = qubesutils.block_check_attached(None, blk[b]['device'], backend_xid = blk[b]['xid']) if b in self.current_blk: if blk[b] == self.current_blk[b]: if self.current_attached[b] != att: #devices the same, sth with attaching changed self.current_attached[b] = att else: #device changed ?! self.current_blk[b] = blk[b] self.current_attached[b] = att else: #new device self.current_blk[b] = blk[b] self.current_attached[b] = att self.msg.append("Attached new device to {}: {}".format( blk[b]['vm'], blk[b]['device'])) to_delete = [] for b in self.current_blk: #remove devices that are not there anymore if b not in blk: to_delete.append(b) self.msg.append("Detached device from {}: {}".format( self.current_blk[b]['vm'], self.current_blk[b]['device'])) for d in to_delete: del self.current_blk[d] del self.current_attached[d] self.__update_blk_entries__() def __update_blk_entries__(self): self.free_devs.clear() self.attached_devs.clear() for b in self.current_attached: if self.current_attached[b]: self.attached_devs[b] = self.__make_entry__(b, self.current_blk[b], self.current_attached[b]) else: self.free_devs[b] = self.__make_entry__(b, self.current_blk[b], None) def __make_entry__(self, k, dev, att): size_str = qubesutils.bytes_to_kmg(dev['size']) entry = { 'dev': dev['device'], 'backend_name': dev['vm'], 'desc': dev['desc'], 'mode': dev['mode'], 'size': size_str, 'attached_to': att, } return entry def attach_device(self, vm, dev): backend_vm_name = self.free_devs[dev]['backend_name'] dev_id = self.free_devs[dev]['dev'] mode = self.free_devs[dev]['mode'] backend_vm = self.qvm_collection.get_vm_by_name(backend_vm_name) if self.tray_message_func: self.tray_message_func("{0} - attaching {1}" .format(vm.name, dev), msecs=3000) qubesutils.block_attach(vm, backend_vm, dev_id, mode=mode) def detach_device(self, vm, dev_name): dev_id = self.attached_devs[dev_name]['attached_to']['devid'] vm_xid = self.attached_devs[dev_name]['attached_to']['xid'] if self.tray_message_func: self.tray_message_func("{0} - detaching {1}".format(vm.name, dev_name), msecs=3000) qubesutils.block_detach(None, dev_id, vm_xid) def check_if_serves_as_backend(self, vm): serves_for = [] for d in self.attached_devs: if self.attached_devs[d]['backend_name'] == vm.name: serves_for.append((self.attached_devs[d]['dev'], self.attached_devs[d]['attached_to']['vm'])) if len(serves_for) > 0: msg = "VM " + vm.name + " attaches block devices to other VMs: " msg += ', '.join([""+v+"("+d+")" for (d,v) in serves_for ]) msg += ".

Shutting the VM down will dettach the devices from them." QMessageBox.warning (None, "Warning!", msg)