backup_utils.py 6.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178
  1. #!/usr/bin/python2.6
  2. #
  3. # The Qubes OS Project, http://www.qubes-os.org
  4. #
  5. # Copyright (C) 2012 Agnieszka Kostrzewa <agnieszka.kostrzewa@gmail.com>
  6. #
  7. # This program is free software; you can redistribute it and/or
  8. # modify it under the terms of the GNU General Public License
  9. # as published by the Free Software Foundation; either version 2
  10. # of the License, or (at your option) any later version.
  11. #
  12. # This program is distributed in the hope that it will be useful,
  13. # but WITHOUT ANY WARRANTY; without even the implied warranty of
  14. # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  15. # GNU General Public License for more details.
  16. #
  17. # You should have received a copy of the GNU General Public License
  18. # along with this program; if not, write to the Free Software
  19. # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
  20. #
  21. #
  22. import sys
  23. import os
  24. from PyQt4.QtCore import *
  25. from PyQt4.QtGui import *
  26. from pyinotify import WatchManager, Notifier, ThreadedNotifier, EventsCodes, ProcessEvent
  27. import subprocess
  28. import time
  29. from thread_monitor import *
  30. from datetime import datetime
  31. from string import replace
  32. mount_for_backup_path = '/usr/libexec/qubes-manager/mount_for_backup.sh'
  33. def check_if_mounted(dev_path):
  34. mounts_file = open("/proc/mounts")
  35. for m in list(mounts_file):
  36. if m.startswith(dev_path):
  37. return m.split(" ")[1]
  38. return None
  39. def mount_device(dev_path):
  40. try:
  41. mount_dir_name = "backup" + replace(str(datetime.now()),' ', '-').split(".")[0]
  42. pmount_cmd = [mount_for_backup_path, dev_path, mount_dir_name]
  43. res = subprocess.check_call(pmount_cmd)
  44. except Exception as ex:
  45. QMessageBox.warning (None, "Error mounting selected device!", "<b>Could not mount {0}.</b><br><br>ERROR: {1}".format(dev_path, ex))
  46. return None
  47. if res == 0:
  48. dev_mount_path = "/media/"+mount_dir_name
  49. return dev_mount_path
  50. return None
  51. def umount_device(dev_mount_path):
  52. while True:
  53. try:
  54. pumount_cmd = ["pumount", "--luks-force", dev_mount_path]
  55. res = subprocess.check_call(pumount_cmd)
  56. if res == 0:
  57. if dev_mount_path.startswith('/media/backup'):
  58. os.rmdir(dev_mount_path)
  59. dev_mount_path = None
  60. return dev_mount_path
  61. except Exception as ex:
  62. title = "Error unmounting backup device!"
  63. text = "<b>Could not unmount {0}.</b><br>\
  64. <b>Please retry or unmount it manually using</b><br> pumount {0}.<br><br>\
  65. ERROR: {1}".format(dev_mount_path, ex)
  66. button = QMessageBox.warning (None, title, text, QMessageBox.Ok | QMessageBox.Retry, QMessageBox.Retry)
  67. if button == QMessageBox.Ok:
  68. return dev_mount_path
  69. def fill_devs_list(dialog):
  70. dialog.dev_combobox.clear()
  71. dialog.dev_combobox.addItem("None")
  72. dialog.blk_manager.blk_lock.acquire()
  73. for a in dialog.blk_manager.attached_devs:
  74. if dialog.blk_manager.attached_devs[a]['attached_to']['vm'] == dialog.vm.name :
  75. att = a + " " + unicode(dialog.blk_manager.attached_devs[a]['size']) + " " + dialog.blk_manager.attached_devs[a]['desc']
  76. dialog.dev_combobox.addItem(att, QVariant(a))
  77. for a in dialog.blk_manager.free_devs:
  78. att = a + " " + unicode(dialog.blk_manager.free_devs[a]['size']) + " " + dialog.blk_manager.free_devs[a]['desc']
  79. dialog.dev_combobox.addItem(att, QVariant(a))
  80. dialog.blk_manager.blk_lock.release()
  81. dialog.dev_combobox.setCurrentIndex(0) #current selected is null ""
  82. dialog.prev_dev_idx = 0
  83. dialog.dir_line_edit.clear()
  84. enable_dir_line_edit(dialog, True)
  85. def enable_dir_line_edit(dialog, boolean):
  86. dialog.dir_line_edit.setEnabled(boolean)
  87. dialog.select_path_button.setEnabled(boolean)
  88. def dev_combobox_activated(dialog, idx):
  89. if idx == dialog.prev_dev_idx: #nothing has changed
  90. return
  91. #there was a change
  92. dialog.dir_line_edit.setText("")
  93. dialog.backup_dir = None
  94. if dialog.dev_mount_path != None:
  95. dialog.dev_mount_path = umount_device(dialog.dev_mount_path)
  96. if dialog.dev_mount_path != None:
  97. dialog.dev_combobox.setCurrentIndex(dialog.prev_dev_idx)
  98. return
  99. if dialog.dev_combobox.currentText() != "None": #An existing device chosen
  100. dev_name = str(dialog.dev_combobox.itemData(idx).toString())
  101. dialog.blk_manager.blk_lock.acquire()
  102. if dev_name in dialog.blk_manager.free_devs:
  103. if dev_name.startswith(dialog.vm.name): # originally attached to dom0
  104. dev_path = "/dev/"+dev_name.split(":")[1]
  105. else: # originally attached to another domain, eg. usbvm
  106. #attach it to dom0, then treat it as an attached device
  107. dialog.blk_manager.attach_device(dialog.vm, dev_name)
  108. dialog.blk_manager.update()
  109. if dev_name in dialog.blk_manager.attached_devs: #is attached to dom0
  110. assert dialog.blk_manager.attached_devs[dev_name]['attached_to']['vm'] == dialog.vm.name
  111. dev_path = "/dev/" + dialog.blk_manager.attached_devs[dev_name]['attached_to']['frontend']
  112. dialog.blk_manager.blk_lock.release()
  113. #check if device mounted
  114. dialog.dev_mount_path = check_if_mounted(dev_path)
  115. if dialog.dev_mount_path == None:
  116. dialog.dev_mount_path = mount_device(dev_path)
  117. if dialog.dev_mount_path == None:
  118. dialog.dev_combobox.setCurrentIndex(0) #if couldn't mount - set current device to "None"
  119. dialog.prev_dev_idx = 0
  120. return
  121. dialog.prev_dev_idx = idx
  122. # Initialize path with root of mounted device
  123. dialog.dir_line_edit.setText(dialog.dev_mount_path)
  124. dialog.backup_dir = dialog.dev_mount_path
  125. dialog.select_dir_page.emit(SIGNAL("completeChanged()"))
  126. def select_path_button_clicked(dialog):
  127. dialog.backup_dir = dialog.dir_line_edit.text()
  128. file_dialog = QFileDialog()
  129. file_dialog.setReadOnly(True)
  130. if dialog.dev_mount_path != None:
  131. new_path = file_dialog.getExistingDirectory(dialog, "Select backup directory.", dialog.dev_mount_path)
  132. else:
  133. new_path = file_dialog.getExistingDirectory(dialog, "Select backup directory.", "~")
  134. if new_path:
  135. dialog.dir_line_edit.setText(new_path)
  136. dialog.backup_dir = new_path
  137. dialog.select_dir_page.emit(SIGNAL("completeChanged()"))
  138. def simulate_long_lasting_proces(period, progress_callback):
  139. for i in range(period):
  140. progress_callback((i*100)/period)
  141. time.sleep(1)
  142. progress_callback(100)
  143. return 0