block.py 5.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160
  1. #!/usr/bin/python2
  2. # -*- coding: utf8 -*-
  3. #
  4. # The Qubes OS Project, http://www.qubes-os.org
  5. #
  6. # Copyright (C) 2014 Marek Marczykowski-Górecki <marmarek@invisiblethingslab.com>
  7. #
  8. # This program is free software; you can redistribute it and/or
  9. # modify it under the terms of the GNU General Public License
  10. # as published by the Free Software Foundation; either version 2
  11. # of the License, or (at your option) any later version.
  12. #
  13. # This program is distributed in the hope that it will be useful,
  14. # but WITHOUT ANY WARRANTY; without even the implied warranty of
  15. # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  16. # GNU General Public License for more details.
  17. #
  18. # You should have received a copy of the GNU General Public License
  19. # along with this program; if not, write to the Free Software
  20. # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
  21. import threading
  22. import time
  23. from PyQt4.QtGui import QMessageBox
  24. from qubes import qubesutils
  25. class QubesBlockDevicesManager():
  26. def __init__(self, qvm_collection):
  27. self.qvm_collection = qvm_collection
  28. self.attached_devs = {}
  29. self.free_devs = {}
  30. self.current_blk = {}
  31. self.current_attached = {}
  32. self.devs_changed = False
  33. self.last_update_time = time.time()
  34. self.blk_state_changed = True
  35. self.msg = []
  36. self.check_counter = 0
  37. self.blk_lock = threading.Lock()
  38. self.tray_message_func = None
  39. self.update()
  40. def block_devs_event(self, xid):
  41. now = time.time()
  42. #don't update more often than 1/10 s
  43. if now - self.last_update_time >= 0.1:
  44. self.last_update_time = now
  45. self.blk_lock.acquire()
  46. self.blk_state_changed = True
  47. self.blk_lock.release()
  48. def check_for_updates(self):
  49. self.blk_lock.acquire()
  50. ret = (self.blk_state_changed, self.msg)
  51. if self.blk_state_changed == True:
  52. self.check_counter += 1
  53. self.update()
  54. ret = (self.blk_state_changed, self.msg)
  55. #let the update last for 3 manager-update cycles
  56. if self.check_counter == 3:
  57. self.check_counter = 0
  58. self.blk_state_changed = False
  59. self.msg = []
  60. self.blk_lock.release()
  61. return ret
  62. def update(self):
  63. blk = qubesutils.block_list(self.qvm_collection)
  64. for b in blk:
  65. att = qubesutils.block_check_attached(self.qvm_collection, blk[b])
  66. if b in self.current_blk:
  67. if blk[b] == self.current_blk[b]:
  68. if self.current_attached[b] != att: #devices the same, sth with attaching changed
  69. self.current_attached[b] = att
  70. else: #device changed ?!
  71. self.current_blk[b] = blk[b]
  72. self.current_attached[b] = att
  73. else: #new device
  74. self.current_blk[b] = blk[b]
  75. self.current_attached[b] = att
  76. self.msg.append("Attached new device to <b>{}</b>: {}".format(
  77. blk[b]['vm'], blk[b]['device']))
  78. to_delete = []
  79. for b in self.current_blk: #remove devices that are not there anymore
  80. if b not in blk:
  81. to_delete.append(b)
  82. self.msg.append("Detached device from <b>{}</b>: {}".format(
  83. self.current_blk[b]['vm'],
  84. self.current_blk[b]['device']))
  85. for d in to_delete:
  86. del self.current_blk[d]
  87. del self.current_attached[d]
  88. self.__update_blk_entries__()
  89. def __update_blk_entries__(self):
  90. self.free_devs.clear()
  91. self.attached_devs.clear()
  92. for b in self.current_attached:
  93. if self.current_attached[b]:
  94. self.attached_devs[b] = self.__make_entry__(b, self.current_blk[b], self.current_attached[b])
  95. else:
  96. self.free_devs[b] = self.__make_entry__(b, self.current_blk[b], None)
  97. def __make_entry__(self, k, dev, att):
  98. size_str = qubesutils.bytes_to_kmg(dev['size'])
  99. entry = { 'dev': dev['device'],
  100. 'dev_obj': dev,
  101. 'backend_name': dev['vm'],
  102. 'desc': dev['desc'],
  103. 'mode': dev['mode'],
  104. 'size': size_str,
  105. 'attached_to': att, }
  106. return entry
  107. def attach_device(self, vm, dev):
  108. mode = self.free_devs[dev]['mode']
  109. if self.tray_message_func:
  110. self.tray_message_func("{0} - attaching {1}"
  111. .format(vm.name, dev), msecs=3000)
  112. qubesutils.block_attach(self.qvm_collection, vm,
  113. self.free_devs[dev]['dev_obj'], mode=mode)
  114. def detach_device(self, vm, dev_name):
  115. frontend = self.attached_devs[dev_name]['attached_to']['frontend']
  116. vm = self.attached_devs[dev_name]['attached_to']['vm']
  117. if self.tray_message_func:
  118. self.tray_message_func("{0} - detaching {1}".format(vm.name,
  119. dev_name), msecs=3000)
  120. qubesutils.block_detach(vm, frontend)
  121. def check_if_serves_as_backend(self, vm):
  122. serves_for = []
  123. for d in self.attached_devs:
  124. if self.attached_devs[d]['backend_name'] == vm.name:
  125. serves_for.append((self.attached_devs[d]['dev'], self.attached_devs[d]['attached_to']['vm']))
  126. if len(serves_for) > 0:
  127. msg = "VM <b>" + vm.name + "</b> attaches block devices to other VMs: "
  128. msg += ', '.join(["<b>"+v.name+"</b>("+d+")" for (d,v) in serves_for ])
  129. msg += ".<br><br> Shutting the VM down will dettach the devices from them."
  130. QMessageBox.warning (None, "Warning!", msg)