160 lines
5.8 KiB
Python
160 lines
5.8 KiB
Python
|
#!/usr/bin/python2
|
||
|
# -*- coding: utf8 -*-
|
||
|
#
|
||
|
# The Qubes OS Project, http://www.qubes-os.org
|
||
|
#
|
||
|
# Copyright (C) 2014 Marek Marczykowski-Górecki <marmarek@invisiblethingslab.com>
|
||
|
#
|
||
|
# 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 <b>{}</b>: {}".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 <b>{}</b>: {}".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'],
|
||
|
'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']
|
||
|
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)
|
||
|
|
||
|
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 <b>" + vm.name + "</b> attaches block devices to other VMs: "
|
||
|
msg += ', '.join(["<b>"+v+"</b>("+d+")" for (d,v) in serves_for ])
|
||
|
msg += ".<br><br> Shutting the VM down will dettach the devices from them."
|
||
|
|
||
|
QMessageBox.warning (None, "Warning!", msg)
|